1: Sample code that reproduces the problem.
#include <stdlib.h>
// & yields a pointer to non-optional-qualified type
#define optional_cast(p) ((typeof(&*(p)))(p))
// const-qualified referenced type of s is accidental
void free_str_2(_Optional const char *s)
{
free(optional_cast(s)); // constraint violation
}
2: Exact command used to run SDCC on this sample code
sdcc --stack-auto --std=c23 -c free_str_2.c
3: SDCC version tested (type "sdcc -v" to find it)
SDCC : mcs51/z80/z180/r2k/r2ka/r3ka/r4k/r5k/r6k/sm83/tlcs90/ez80/z80n/r800/ds390/pic16/pic14/TININative/ds400/hc08/s08/stm8/pdk13/pdk14/pdk15/mos6502/mos65c02/f8/f8l TD- 4.5.24 #16456 (Mac OS X ppc)
4: Copy of the error message or incorrect output, or a clear description of the observed versus expected behavior.
The above example is taken from the Semantics part of the subsection "Typeof specifiers" in the _Optional TS. Specifically:
Because s does not have a variably modified type in the macro-replaced expression (typeof(&(s)))(s)) that results from invocations of the optional_cast macro, the operand of typeof is not evaluated, therefore no diagnostic message is recommended for the lvalue (s) .
The address-of operator is defined as removing the _Optional qualifier from the type of its operand but it must not remove any other qualifiers. The expected type of the argument expression in the free function call is therefore const char *, which violates a constraint on assignment (assuming the free function has the ISO standard parameter type).
SDCC does not report that the const qualifier was lost from the pointer target during the assignment implicit in the function call. That seems like a bug, because the ISO C standard requires a diagnostic message to be produced for every constraint violation.
After further investigation, I don't think this bug is specific to
_Optional:I am attaching a proposed bugfix for this issue, created with assistance from Codex in ChatGPT Work. I hope that it will prove acceptable when reviewed by SDCC's maintainers. This patch is relatively simple compared to some others.
I have done the following testing:
i.e.
make -j2
make -C support/regression clean-results
make -C support/regression -j2 test-ucz80
make -C support/valdiag clean
make -C support/valdiag -j2
Summary for 'ucz80': 0 failures, 36740 tests, 6407 test cases, 7347620 bytes, 1613638336 ticks
Summary for 'mcs51': 0 failures, 651 tests, 367 test cases, 0 bytes, 0 ticks
Summary for 'mcs51-large': 0 failures, 649 tests, 367 test cases, 0 bytes, 0 ticks
Summary for 'mcs51-stack-auto': 0 failures, 650 tests, 367 test cases, 0 bytes, 0 ticks
Summary for 'ds390': 0 failures, 637 tests, 367 test cases, 0 bytes, 0 ticks
Summary for 'z80': 0 failures, 640 tests, 367 test cases, 0 bytes, 0 ticks
Summary for 'z180': 0 failures, 639 tests, 367 test cases, 0 bytes, 0 ticks
Summary for 'r2k': 0 failures, 639 tests, 367 test cases, 0 bytes, 0 ticks
Summary for 'r4k': 0 failures, 639 tests, 367 test cases, 0 bytes, 0 ticks
etc.
The patch looks good to me (except for the naming of the parameters - I'd prefer "target" instead of "new", "source" instead of "orig" to keep the terminology consistent, but I can change that when applying).
For now, IMO we should wait a few days for the discussion on sdcc-devel on LLM use to settle.
Retesting this patch revealed that it triggers a flood of 'volatile pointer lost' warnings from tests in the regression suite. There is also an open question about whether treating pointers specially is sufficient, or whether the conditions should be updated to include array types too.
Last edit: Christopher Bazley 2026-09-19
The attached candidate patch has now been significantly reworked and retested. It no longer triggers a flood of warnings from existing tests, and it now produces the correct behaviour in cases of array-to-pointer decay. Please consider it for incorporation into SDCC, but ensure that it is applied after the fix for bug #4072, on which it depends.
Commit message follows:
regression test results:
Summary for 'ucz80': 0 failures, 36743 tests, 6408 test cases, 7347903 bytes, 1609599005 ticks
valdiag test results:
Summary for 'mcs51': 0 failures, 659 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'mcs51-large': 0 failures, 657 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'mcs51-stack-auto': 0 failures, 658 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'ds390': 0 failures, 645 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'z80': 0 failures, 648 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'z180': 0 failures, 647 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'r2k': 0 failures, 647 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'r4k': 0 failures, 647 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'sm83': 0 failures, 647 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'tlcs90': 0 failures, 647 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'hc08': 0 failures, 646 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 's08': 0 failures, 646 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'mos6502': 0 failures, 646 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'stm8': 0 failures, 649 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'f8': 0 failures, 645 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'pdk13': 0 failures, 648 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'pdk14': 0 failures, 646 tests, 369 test cases, 0 bytes, 0 ticks
Summary for 'pdk15': 0 failures, 644 tests, 369 test cases, 0 bytes, 0 ticks
I've started reviewing this, but most likely won't finish today.
So basically, this patch does the checking already on the AST instead of later on the iCode. Which, I think makes sense, since the AST is closer to what the user wrote. And it looks like it does the right thing for access qualifiers on the immediate pointer target. I've done some testing, and there are still issues with qualifiers further down the type chain, and for qualifiers other than access qualifiers:
The most complicated are the intrinsic named address space qualifiers. Users probably don't want warnings where qualifiers change in a way that the immediate target is in an address space that contains the address space of the source. But we'd still need those warnings if such a change happens further down the type chain.
Still the patch is already an improvement over the current situation. But it doesn't fix all cases from the title of this bug report.
Hi Philipp, thanks very much for the review.
Sorry that the fix is incomplete. I am mainly focusing on improving ISO C conformance. I am not familiar with the semantics of the non-standard qualifiers (other than
_Optional), therefore I don't feel qualified to comment on what their behaviour should be.If you are happy with the direction of the current patch, maybe the best thing to do would be to merge it as-is and leave treatment of non-standard qualifiers to another patch?
By the way, did you see that this patch depends on the candidate fix for bug #4072 ?
Hi Philipp,
Please find a revised candidate fix attached, which still depends on the fix for bug #4072. The previous implementation has been extended to diagnose incompatible qualifiers further down the type chain, including in function parameter and return types. Top-level parameter qualifiers are ignored when comparing function types, as are qualifiers on return types.
The tests now cover deeper indirection, pointer-to-array versus pointer-to-pointer incompatibility, and qualification differences in function types. Cases whose expected diagnostic messages would change under N3449 are grouped separately and highlighted by comments. Workarounds for bugs #4104 and #4105 are also highlighted by comments.
Named address-space compatibility is not yet addressed, but I think that handling the other qualifiers correctly would be a worthwhile step forward. Personally, I'd treat that as a separate bug or feature request that comes with its own sample code to reproduce any known issues.
Since the new functions operate on types, they should go in SDCCsymt.c, not SDCCast.c (even if all calls for now are from SDCCast.c), since in general symbol and type handling is done in SDCCsymt.c. I guess we can also drop the "Std" in the name of compatibleStdQualifiers, since we want to extend this to handle all qualifiers later. Preferably put a
// TODO: Handle named qualifiers for named address spaceswhere their handling is missing for now.Regarding named address spaces qualifiers: I agree that we can consider that to be a separate issue, for which I'd open a ticket once this one is closed.
Thanks. Here's another attempt at a viable patch. Type-only helpers are moved to SDCCsymt.c.
New ticket for qualifiers for intrinsic named address spaces: [bugs:#4106].
Related
Bugs: #4106
Thanks. Fixed in [r16956] via your patch.
Related
Commit: [r16956]