1: Sample code that reproduces the problem.
int f(void)
{
auto int x = 1;
return x;
}
2: Exact command used to run SDCC on this sample code.
sdcc -mz80 --std=c23 -S repro.c
The same source compiles without diagnostics with --std=c11.
The failure also occurs with --std=c2y.
3: SDCC version tested.
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/huc6280/f8/f8l TD- 4.6.4 #16998 (Linux)
published under GNU General Public License (GPL)
4: Observed and expected behaviour.
SDCC exits with status 1 and reports:
repro.c:3: error 141: two or more data types in declaration for 'x'
There is only one type specifier: int. Here auto is an ordinary
storage-class specifier in a block-scope declaration. The declaration is
valid and its type is already specified, so no type inference is required.
It should compile and f() should return 1.
C23 draft N3096, 6.7.2 paragraph 6, introduces type inference for declarations whose declaration specifiers contain no type specifier. Its storage-class rules are in 6.7.1. The corresponding provisions in N3886 are 6.7.3 paragraph 6 and 6.7.2. The example contains an explicit type specifier and does not violate those rules.
This breaks existing valid declarations when selecting C23 or C2Y mode. It is distinct from [bugs:#4139], which concerns conversions for declarations whose types really must be inferred.
5: Additional checks and compiler comparison.
Compiler Explorer: SDCC trunk beside GCC 16.2.
Both panes contain the sample above. SDCC uses -mz80 --std=c23; GCC uses -std=c23 -pedantic-errors -fsyntax-only. SDCC r16998 rejects it; GCC 16.2.0 accepts it without diagnostics.
| Case | SDCC C11 | SDCC C23/C2Y | GCC 16.2 C23 |
|---|---|---|---|
auto int value = 1; |
Accepted | Error 141 | Accepted |
auto float value = 2; |
Accepted | Error 141 | Accepted |
auto unsigned char value = 200; |
Accepted | Error 141 | Accepted |
auto I value = 1;, with typedef int I; |
Accepted | Error 141 | Accepted |
int value = 1; |
Accepted | Accepted | Accepted |
auto int value;, followed by assignment |
Accepted | Accepted | Accepted |
auto int value = {1}; |
Accepted | Accepted | Accepted |
auto int *value = source; |
Accepted | Accepted | Accepted |
These results are consistent on Z80, STM8 default/large, MCS51 default/large/stack-auto, PDK14, S08 and Rabbit R2K, with both local builds. Eight sources, nine configurations, three language modes and two builds produced 432 compiler invocations. The four affected cases fail in every C23/C2Y configuration; the other cases and all C11 translations are clean. GCC 16.2 accepts all eight sources without diagnostics. No runtime test was performed.
Implementation observation: prepareDeclarationSymbol enables inference when the declaration uses auto in C23 mode and has a scalar expression initialiser, without checking that the declaration specifiers omit a type specifier. The inferred type is then merged with the explicit type, producing the spurious duplicate-type diagnostic. A braced initialiser or pointer declarator bypasses that path, explaining the results above.
Please consider the attached candidate bug fix patch.
C23 type inference applies only when no type specifier is present. Explicitly typed
autodeclarations instead use ordinary initialisation. The effect of the patch is to not infer a second type from the initialisers of such declarations, including when length or signedness specifiers implyintwithout an explicit type noun.Added diagnostic and runtime tests for explicit types, conversions and valid inference cases.