typedefstruct{constchar*text;}wrapper;voiduse_value(wrapperv);voidmain(){wrappervalue=(wrapper){.text="not created constant"};use_value(value);use_value((wrapper){.text="created constant"});}
produces invalid asm (no value in ld hl, # ):
;a.c:4:wrappervalue=(wrapper){.text="not created constant"};ldhl,#0addhl,spexde,hlldhl,#ldilda,(hl)ld(de),a
a.asm:58: Error: <q> missing or improper operators, terminators, or delimiters
And when modified to omit the (wrapper) in the = line:
typedefstruct{constchar*text;}wrapper;voiduse_value(wrapperv);voidmain(){wrappervalue={.text="also created constant"};use_value(value);use_value((wrapper){.text="created constant"});}
both constants are created and used (e.g. if use_value prints them, both are printed).
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This is a known way to fix the bug, but it prevents the feature from being used in macros.
#define CREATE_WRAPPER(literal) ((wrapper){.text = literal})typedefstruct{constchar*text;}wrapper;voiduse_value(wrapperv);voidmain(){wrappervalue=CREATE_WRAPPER("must be created");use_value(value);use_value(CREATE_WRAPPER("created"));}
and more case
typedefstruct{constchar*text;}wrapper;typedefstruct{inta;wrapper*b;floatc;}foo;voidusage(){foof={.a=1,.b=&((wrapper){.text="must be created"}),.c=7.2f};}
Last edit: Vladimir Setyaev 2025-10-18
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I agree that it's a bug that limits which forms could be used without problems, my message was an attempt to narrow it down more, e.g. it's not port-specific. Thanks for the second example, it's more minimal, and this one is even more:
typedefstruct{constchar*p;}S;Sf(void){Sr=(S){.p="must be created"};returnr;}
It results (for -mz80) in:
Error: <q> missing or improper operators, terminators, or delimiters
Using it in return doesn't fail
typedefstruct{constchar*p;}S;Sf(void){return(S){.p="must be created"};}
so the S r = (S){.p = is the needed to trigger, i.e. it's more clear it must be related to the = context.
Last edit: Janko Stamenović 2025-10-19
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This looks like it could be related to [bugs:#3837]. In both cases, the context is a compound literal used as initializer.
Does the same error occur when the compound literal is used in an assignment rather than an initializer?
No. I meant turning the initialization into a simple assignment, i.e. turning this
typedefstruct{constchar*p;}S;Sf(void){Sr=(S){.p="must be created"};returnr;}
into this
typedefstruct{constchar*p;}S;Sf(void){Sr;r=(S){.p="must be created"};returnr;}
The latter does apparently work, which means that the bug only manifests when a compound literal is used when initializing a variable rather than merely assigning to it.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This looks fixed by patches/509, which addresses the same root cause from the other end: a compound literal in a declaration initializer is never given storage, so the temporary has no rname and the operand that reads it is emitted empty.
Compiling the reproducer from this report to assembly, before and after that patch:
port without patches/509 with it
z80 ld hl, # (empty) correct
z180 ld hl, # (empty) correct
mcs51 mov ___memcpy_PARM_2,# correct
stm8 correct correct
That matches the workarounds noted here: gatherImplicitVariables() walks the function body, so it finds a literal in a statement but not one still sitting in sym->ival as an initializer list, and gatherAutoInit() only turns that into a tree later - after allocVariables() has assigned storage. So the assignment and return forms work and S r = (S){...}; does not.
Two things that differ from the report, in case they matter:
z180 and mcs51 are affected too. The report lists STM8 and Z80. I see the empty operand on z80, z180 and mcs51.
I could not reproduce it on stm8. Four shapes - designated and positional initializer, const char * and char * member, and a second member added - all give correct output on stm8 both with and without the patch. If you have an stm8 case that fails, it may be a different path and worth keeping separate from this one.
For completeness: ds390 cannot compile this reproducer at all, with or without the patch, because the function returns a struct - error 54: Function cannot return aggregate. That is a port limitation unrelated to this bug.
Happy to confirm against whatever revision you would like, or to add the reproducer to the regression suite if that is useful.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Compiling this with
-mz80produces invalid asm (no value in
ld hl, #):And when modified to omit the
(wrapper)in the=line:both constants are created and used (e.g. if use_value prints them, both are printed).
This is a known way to fix the bug, but it prevents the feature from being used in macros.
and more case
Last edit: Vladimir Setyaev 2025-10-18
I agree that it's a bug that limits which forms could be used without problems, my message was an attempt to narrow it down more, e.g. it's not port-specific. Thanks for the second example, it's more minimal, and this one is even more:
It results (for
-mz80) in:Using it in return doesn't fail
so the
S r = (S){.p =is the needed to trigger, i.e. it's more clear it must be related to the=context.Last edit: Janko Stamenović 2025-10-19
This looks like it could be related to [bugs:#3837]. In both cases, the context is a compound literal used as initializer.
Does the same error occur when the compound literal is used in an assignment rather than an initializer?
Related
Bugs: #3837
Do you mean like this ?
Upd: This expression don't work on stm8.
Last edit: Vladimir Setyaev 2025-10-23
No. I meant turning the initialization into a simple assignment, i.e. turning this
into this
The latter does apparently work, which means that the bug only manifests when a compound literal is used when initializing a variable rather than merely assigning to it.
Only the second expression works.
This looks fixed by patches/509, which addresses the same root cause from the other end: a compound literal in a declaration initializer is never given storage, so the temporary has no rname and the operand that reads it is emitted empty.
Compiling the reproducer from this report to assembly, before and after that patch:
That matches the workarounds noted here:
gatherImplicitVariables()walks the function body, so it finds a literal in a statement but not one still sitting insym->ivalas an initializer list, andgatherAutoInit()only turns that into a tree later - afterallocVariables()has assigned storage. So the assignment and return forms work andS r = (S){...};does not.Two things that differ from the report, in case they matter:
z180 and mcs51 are affected too. The report lists STM8 and Z80. I see the empty operand on z80, z180 and mcs51.
I could not reproduce it on stm8. Four shapes - designated and positional initializer,
const char *andchar *member, and a second member added - all give correct output on stm8 both with and without the patch. If you have an stm8 case that fails, it may be a different path and worth keeping separate from this one.For completeness: ds390 cannot compile this reproducer at all, with or without the patch, because the function returns a struct -
error 54: Function cannot return aggregate. That is a port limitation unrelated to this bug.Happy to confirm against whatever revision you would like, or to add the reproducer to the regression suite if that is useful.
[patches:#509] mentioned above is in [r16747].
Related
Commit: [r16747]
Patches:
#509