Menu ▾ ▴

#4107 _Generic distinguishes function-pointer types depending on their spelling

closed-fixed
None
Front-end
5
2026-10-18
2026-10-03
No

1: Sample code that reproduces the problem

int f(int);
typedef int function_type(int);
typedef function_type *pointer_type;
pointer_type pf = f;

_Static_assert(_Generic(pf, function_type *: 1, default: 0), "pf, function_type *"); // fails
_Static_assert(_Generic(pf, pointer_type: 1, default: 0), "pf, pointer_type"); // passes
_Static_assert(_Generic(&f, function_type *: 1, default: 0), "&f, function_type *"); // passes
_Static_assert(_Generic(&f, pointer_type: 1, default: 0), "&f, pointer_type"); // fails

2: Exact command used to run SDCC on this sample code

sdcc -c --std=c11 bug.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/huc6280/f8/f8l TD- 4.6.2 #0 (macOS ARM64)
published under GNU General Public License (GPL)

4: Copy of the error message or incorrect output, or a clear description of the observed versus expected behavior.

SDCC reports two out of three assertion failures:

bug.c:6: warning 215: static assertion failed: "pf, function_type *"
bug.c:9: warning 215: static assertion failed: "&f, pointer_type"

GCC 16.1.0 and Clang 23.1.0 do not report any assertion failures, which is the expected behaviour:
https://godbolt.org/z/rnG4av3Ts

Text edited by @spth to fix code section highlighting.

Related

Bugs: #3916

Discussion

  • Christopher Bazley

    I can't fix the formatting myself

     
  • Christopher Bazley

    addSymChain in SDCCsymt.c calls changePointer, which changes pointers to functions that do not use banked calls to type CPOINTER. This happens for the declarations of pointer_type and pf. checkDecl also calls changePointer, but typedefs bypass that call.

    For the generic associations with function_type *, the type_name rule in SDCC.y calls pointerTypes and processFuncPtr, without calling changePointer. pointerTypes uses port->unqualified_pointer, which leaves the type as GPOINTER on the tested targets.

    For the generic associations with pointer_type, the type_name rule uses the specifier_qualifier_list-only alternative. It uses the complete pointer type already processed by changePointer when the typedef was declared, so these association types remain CPOINTER.

    The GENERIC case in SDCCast.c mutates a copy of pointers marked DCL_TYPE_IMPLICITINTRINSIC, which becomes type GPOINTER. This affects the controlling expression &f in the example, whereas the declared pointer object's type remains CPOINTER.

    Presumably function pointer types should be canonicalised before comparison, but I don't know enough about address spaces and other SDCC-specific features to be sure how to do that correctly.

     
    • Philipp Klaus Krause

      AFAIR, for a similar issue with objects, we introduced the sclass_implicitintrinsic flag as a hack to mark where a named address space had been assigned implicitly, and should be ignored for_Generic. Maybe the easiest solution would be to check if that would work for functions, too (and probably was just forgotten to implement then)?

       
      • Christopher Bazley

        Hi Philipp,

        Please find a candidate fix for this bug attached. It's a standalone patch for r16960 with no dependencies.

        AFAIR, for a similar issue with objects, we introduced the sclass_implicitintrinsic flag as a hack to mark where a named address space had been assigned implicitly, and should be ignored for_Generic.

        The existing implicit-address-space handling already affects &f: its pointer type has DCL_TYPE_IMPLICITINTRINSIC set, so the GENERIC case of decorateType changes its pointer kind from CPOINTER to GPOINTER.

        The declaration of pf, however, goes through changePointer, which sets its pointer kind to CPOINTER without setting that flag. The pointer type recorded for pf does not have DCL_TYPE_IMPLICITINTRINSIC, so _Generic does not reset its pointer kind to GPOINTER. The typedef declaration of pointer_type goes through the same processing as pf, so using that typedef name in an association likewise supplies a CPOINTER type.

        The abstract association type function_type * takes a different path: pointerTypes assigns GPOINTER on the affected targets, and processFuncPtr does not call changePointer to convert it to CPOINTER.

        The patch adds changePointer after the existing flag handling in the GENERIC case of decorateType in SDCCast.c, and inside processFuncPtr in SDCCsymt.c. Thus the types of pf and &f, and the association types function_type * and pointer_type, all use CPOINTER internally and match as expected.

         

        Last edit: Christopher Bazley 2026-10-09
  • Philipp Klaus Krause

    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -1,4 +1,5 @@
     1: Sample code that reproduces the problem
    +
     ```
     int f(int);
     typedef int function_type(int);
    @@ -9,9 +10,10 @@
     _Static_assert(_Generic(pf, pointer_type: 1, default: 0), "pf, pointer_type"); // passes
     _Static_assert(_Generic(&f, function_type *: 1, default: 0), "&f, function_type *"); // passes
     _Static_assert(_Generic(&f, pointer_type: 1, default: 0), "&f, pointer_type"); // fails
    -````
    +```
    
     2: Exact command used to run SDCC on this sample code
    +
     ```
     sdcc -c --std=c11 bug.c
     ```
    @@ -25,9 +27,13 @@
     4:  Copy of the error message or incorrect output, or a clear description of the observed versus expected behavior.
    
     SDCC reports two out of three assertion failures:
    +
     ```
     bug.c:6: warning 215: static assertion failed: "pf, function_type *"
     bug.c:9: warning 215: static assertion failed: "&f, pointer_type"
     ```
    +
     GCC 16.1.0 and Clang 23.1.0 do not report any assertion failures, which is the expected behaviour:
     https://godbolt.org/z/rnG4av3Ts
    +
    +Text edited by @spth to fix code section highlighting.
    
     
  • Christopher Bazley

    Please consider the revised patch that I am now attaching instead of the version previously attached.

    This revision moves the _Generic and sizeof checks to compile-time assertions in valdiag. They check types and never call a function. The duplicate-association checks remain, and the compiler fix is unchanged.

     

    Last edit: Christopher Bazley 2026-10-14
    • Philipp Klaus Krause

      The patch looks good to me.

       
  • Christopher Bazley

    • status: open --> closed-fixed
    • assigned_to: Christopher Bazley
     
  • Christopher Bazley

    Fixed in [r17018].

     

    Related

    Commit: [r17018]

  • Gabriele Gorla

    Gabriele Gorla - 2026-10-18

    Test fails for mcs51-huge

    cases/./../tests/bug-4107.c:60: error 228: multiple matching expressions in generic association
    cases/./../tests/bug-4107.c:61: error 228: multiple matching expressions in generic association
    make[3]: *** [Makefile:434: gen/mcs51-huge/tst_bug-4107.rel] Error 1
    results/mcs51-huge/tst_bug-4107.out:2:--- FAIL: cannot compile/link cases/tst_bug-4107.c
    Failure: cases/tst_bug-4107.c
    tst_bug-4107                        (f:  1, t:   1, c:    1, b:       0, T:         0)
    
     
  • Gabriele Gorla

    Gabriele Gorla - 2026-10-18
    • status: closed-fixed --> open
     
  • Christopher Bazley

    • status: open --> closed-fixed
     
  • Christopher Bazley

    Thanks for highlighting the regression. It should be fixed by [r17029].

     

    Related

    Commit: [r17029]


Log in to post a comment.