Menu

#3886 Compiler don't create constant for compound literal

pending-fixed
None
STM8
5
6 days ago
2025-10-18
No

version: 4.5.11 #15736 (Linux)
run: sdcc -mstm8 --std=c2y main.c

2 Attachments

Discussion

  • Janko Stamenović

    Compiling this with -mz80

    typedef struct {const char* text;} wrapper;
    void use_value(wrapper v);
    void main() {
      wrapper value = (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: wrapper value = (wrapper){.text = "not created constant"};
        ld  hl, #0
        add hl, sp
        ex  de, hl
        ld  hl, #
        ldi
        ld  a, (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:

    typedef struct {const char* text;} wrapper;
    void use_value(wrapper v);
    void main() {
      wrapper value = {.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).

     
  • Vladimir Setyaev

    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})
    
    typedef struct {const char* text;} wrapper;
    void use_value(wrapper v);
    void main() {
      wrapper value = CREATE_WRAPPER("must be created");
      use_value(value);
      use_value( CREATE_WRAPPER("created"));
    }
    

    and more case

    typedef struct {const char* text;} wrapper;
    
    typedef struct {int a; wrapper* b; float c;} foo;
    
    void usage()
    {
      foo f = {.a=1, .b = &((wrapper){.text = "must be created"}),.c = 7.2f};
    }
    
     

    Last edit: Vladimir Setyaev 2025-10-18
    • Janko Stamenović

      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:

      typedef struct {const char* p;} S;
      S f( void ) {
          S r = (S){.p = "must be created" };
          return r;
      }
      

      It results (for -mz80) in:

      Error: <q> missing or improper operators, terminators, or delimiters
      

      Using it in return doesn't fail

      typedef struct {const char* p;} S;
      S f( 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
  • Benedikt Freisen

    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

  • Vladimir Setyaev

    Do you mean like this ?

    int arr[] = (int[]){1,2,3,4,5};
    

    Upd: This expression don't work on stm8.

     

    Last edit: Vladimir Setyaev 2025-10-23
    • Benedikt Freisen

      No. I meant turning the initialization into a simple assignment, i.e. turning this

      typedef struct {const char* p;} S;
      S f( void ) {
          S r = (S){.p = "must be created" };
          return r;
      }
      

      into this

      typedef struct {const char* p;} S;
      S f( void ) {
          S r;
          r = (S){.p = "must be created" };
          return r;
      }
      

      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.

       
  • Vladimir Setyaev

    Only the second expression works.

     
  • Howard M. Harte

    Howard M. Harte - 6 days ago

    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.

     
  • Benedikt Freisen

    • status: open --> pending-fixed
    • assigned_to: Benedikt Freisen
     

Log in to post a comment.