C23: issue log

Issue Summary Status
1000 Structure type compatibility with flexible array members Open
1001 Qualified rvalues from structure or union members Open
1002 Type qualifiers in [*] in abstract declarators Open
1003 Linkage between library functions Open
1004 Classification of scanf failures Open
1005 Annex D refers to option removed from UAX#31 in revision 39 Open
1006 atomic_fetch operations and "address types" Open
1007 Implicitly unsigned bit-fields ambiguity Open
1008 Bad constraint on attributes with tags Open
1009 extern thread_local should not be an external definition Open
1010 Termination of the execution with threads Open
1011 powr(negative, qNaN) Open
1012 Error returns from specific width length modifiers Open
1013 Ambiguity of "same type" Open

Issue 1000: Structure type compatibility with flexible array members

Authors: Joseph Myers
Date: 2025-03-05
Status: Open

The definition of compatible type for structure types (C23 6.2.7) allows two structures to be compatible where one ends with a flexible array member and the other ends with a member with complete array type. (Before C23, this was an issue for structures in separate translation units; C23 makes it applicable within a single translation unit.)

struct s { int a; char b[]; } x;

void f()
{
  struct s { int a; char b[2]; } y = {};
  x = y; // OK, assignment between compatible types.
}

However, the definition of flexible array members explicitly anticipates that the offset of the flexible array member may be different from that of a member with complete array type ("the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array"), which is not consistent with the types being compatible. Furthermore, the rules for assignment (and likewise initialization and argument passing and function return) and conditional expressions are underspecified in this case; it is not clear what members would be copied when such a mixture of types is involved.

Suggested correction

In C23 6.2.7 (Compatible type and composite type), after the first bullet point for compatibility for structure and union types, insert another bullet point:



Issue 1001: Qualified rvalues from structure or union members

Authors: Joseph Myers
Date: 2025-03-06
Status: Open
Cross-references: 0423, 0481

C23 6.7.3.5 (Atomic type specifiers) says "The properties associated with atomic types are meaningful only for expressions that are lvalues." and, similarly, in 6.7.4.1 (Type qualifiers), "The properties associated with qualified types are meaningful only for expressions that are lvalues.". Not being meaningful, however, leaves open the possibility that an rvalue might nevertheless have such a type (with no semantic difference compared to having an unqualified type).

Before C11, it was never possible to observe whether an rvalue had a qualified type. C11 introduced _Generic with the potential to observe more details of the types of rvalues, and C23 introduced typeof, and these features resulted in a series of changes to avoid rvalues having qualified or atomic types or to avoid such types being observable:

In June 2024, Aaron Ballman pointed out in reflector message 26025 that it was still possible to have a qualified rvalue when accessing a qualified member of an rvalue structure or union (the result of a function call, assignment expression, conditional expression or comma expression with structure or union type), leading to a long discussion continuing through July and into August. In that discussion, Alex Celeste pointed out (message 26098) that qualifiers on array element types (which before C23 were not considered to be qualifiers on the array type) should still be preserved.

Consistency with the direction of the sequence of changes listed above would indicate that qualifiers and _Atomic should not be preserved in other cases. That suggests the following would be valid.

struct s1 { const int i; } f1();
struct s2 { const int a[1]; } f2();
struct s1a { _Atomic int i; } f1a();
struct s2a { _Atomic int a[1]; } f2a();

extern int i;
extern const int a[1];
extern _Atomic int aa[1];

extern typeof(f1().i) i;
extern typeof(f2().a) a;
extern typeof(f1a().i) i;
extern typeof(f2a().a) aa;

static_assert(_Generic(f1().i, int: 1, default: 0));
static_assert(_Generic(f2().a, const int *: 1, default: 0));
static_assert(_Generic(f1a().i, int: 1, default: 0));
static_assert(_Generic(f2a().a, _Atomic int *: 1, default: 0));

Suggested correction

In C23 6.5.3.4 (Structure and union members), insert at the end of the first Semantics paragraph:

If the first expression is not an lvalue and the designated member does not have array type, the result has the unqualified, non-atomic version of the type of the designated member.



Issue 1002: Type qualifiers in [*] in abstract declarators

Authors: Joseph Myers
Date: 2025-03-06
Status: Open
Cross-references: 0289

An array declarator can have the form (syntax in C23 6.7.7.1):

direct-declarator [ type-qualifier-listopt * ]

but the corresponding abstract-declarator syntax in 6.7.8 omits the type-qualifier-listopt. David Keaton stated in reflector message 14798 that this difference was not deliberate. The response to issue 0289 fixed such a difference in the case where an assignment expression is involved, but did nothing about the [*] case.

If abstract declarators have a more restrictive syntax than declarators, that confuses both the rules about replacing an expression at function prototype scope with *, and the rule that "If, in a parameter declaration, an identifier can be treated either as a typedef name or as a parameter name, it shall be taken as a typedef name.". It seems clear that the syntax should match in the two cases.

Suggested correction

In C23 6.7.8 (Type names), change the fourth option in the Syntax for array-abstract-declarator:

direct-abstract-declaratoropt [ type-qualifier-listopt * ]



Issue 1003: Linkage between library functions

Authors: Joseph Myers
Date: 2025-03-07
Status: Open
Cross-references: 0078

C23 footnote 20, in 6.2.2 (Linkages of identifiers), says:

There is no linkage between different identifiers.

Is this footnote (not normative) referring only to linkage between identifiers defined by the user's program? Or is it also intended to say that there cannot be linkage between different library functions?

In practice, it is common that pointers to library functions with compatible semantics may compare equal: that memcpy == memmove, or ldexp == scalbn, for example. The response to C90 issue 0078 said that this is permitted. However, the footnote in question was added in C99, which makes it less clear if that issue response reflects the current intent.

It seems appropriate to amend both normative and non-normative text to make clear that it is OK for pointers to different library functions to compare equal.

Suggested correction

In C23 7.1.2 (Standard headers), insert text as follows.

Any declaration of a library function shall have external linkage. Pointers to different library functions may compare equal where this is compatible with the semantics of those functions.FN)

FN)For example, it is possible that memcpy == memmove or that ldexp == scalbn.

In C23 6.2.2 (Linkages of identifiers), amend footnote 20 as follows.

There is no This document does not define any mechanism to establish linkage between different identifiers. As described in 7.1.2, it is possible that pointers to different library functions compare equal where that is compatible with the semantics of those functions.



Issue 1004: Classification of scanf failures

Authors: Joseph Myers
Date: 2025-03-07
Status: Open
Cross-references: 1012

C23 7.23.6.3 (The fscanf function) describes failures as follows:

Failures are described as input failures (due to the occurrence of an encoding error or the unavailability of input characters), or matching failures (due to inappropriate input).

Unsupported specific width length modifiers (for example, %w123d if there is no int123_t or int_least123_t type and w123 is not implementation-defined to be supported anyway) are defined as error conditions for both printf and scanf functions (rather than undefined behavior). But is such an error an input failure or a matching failure? Or is it some new kind of failure, requiring the quoted dichotomy to be updated?

The same issue also applies for fwscanf.

No specific change is proposed here to address this issue since appropriate wording would depend on direction from the committee on what kind of failure this should be.



Issue 1005: Annex D refers to option removed from UAX#31 in revision 39

Authors: Joseph Myers
Date: 2025-03-07
Status: Open

C23 D.2.2 (Restricted Format Characters) refers to an option UAX#31-R1a that appears to have been removed in revision 39 of UAX#31 (the current revision as of this writing is revision 41).

This subclause should thus be updated or removed. See reflector message 25125 for some relevant references. It would be best for our Unicode experts and C++ liaison to establish what fix is best and most compatible with C++, but in the absence of that I propose the obvious minimal change.

Suggested correction

Remove C23 D.2.2 (Restricted Format Characters).



Issue 1006: atomic_fetch operations and "address types"

Authors: Joseph Myers
Date: 2025-03-07
Status: Open

C23 7.17.7.6 (The atomic_fetch and modify generic functions) says:

All these operations are applicable to an object of any atomic integer type other than _Atomic bool, atomic_bool, or the atomic version of an enumeration with underlying type bool.

and then later says:

For address types, the result may be an undefined address, but the operations otherwise have no undefined behavior.

The effect seems to be confused about whether pointer types should be included or not; the first quoted sentence clearly does not include them, but the second one seems to envisage that they might be included. It also has further problems:

Making the minimal change of removing the second quoted sentence, as suggested in C23 CD1 comment GB-196, did not receive consensus, but the wording is still defective in its present state so some change is needed to address that (either allowing pointer types and adjusting the wording to avoid using terminology that is not defined, or removing the problematic sentence if only integer types are to be allowed). A larger set of changes, mixing both normative and non-normative changes but including changes to allow pointer types here, was in N2389, which was discussed in Ithaca, October 2019.

Given the previous lack of consensus, no specific wording change is proposed here; committee direction would be needed before proposing specific changes to address this issue.



Issue 1007: Implicitly unsigned bit-fields ambiguity

Authors: Joseph Myers
Date: 2025-03-11
Status: Open
Cross-references: 0315

There are several cases in which the rule that bit-fields declared with types that are not explicitly signed or unsigned might be treated as if either signed or unsigned had been specified leaves ambiguity about exactly what is permitted. One such case dates back to C90, and others have arisen over time as features have been added to the standard (but they are all included together in this issue report given how closely related they are).

In C23, the rule is specified in 6.7.3.1 (Type specifiers):

Each of the comma-separated multisets designates the same type, except that for bit-fields, it is implementation-defined whether the specifier int designates the same type as signed int or the same type as unsigned int.

Footnote 132, in 6.7.3.2 (Structure and union specifiers), explicitly notes that such a type might be produced from a typedef name or typeof specifiers:

As specified in 6.7.3, if the actual type specifier used is int or a typedef-name defined as int, then it is implementation-defined whether the bit-field is signed or unsigned. This includes an int type specifier produced using the typeof specifiers (6.7.3.6).

(It's not entirely clear that this inclusion of typedef names is adequately supported by the normative text, but that's not the subject of this issue report.)

The final response to issue 0315 said the implementation-defined signedness also applied for other implementation-defined declared types such as char, short, long and long long.

No specific wording is suggested to address the questions here, as direction from the committee is needed. For C2Y, my preference would be to follow C++14 and remove the option for bit-fields to be implicitly unsigned when the declared type is not unsigned, but that would need a separate paper and not be appropriate to apply to C23 (notwithstanding that C++ core issue 739 was considered a defect for C++). If that change were made for C2Y, it might then be appropriate to declare most of these cases explicitly unspecified for C23; if no such change is made for C2Y, further consideration should be given to specifying some of these cases explicitly.

Question 1

Suppose the declared type of the bit-field is a typedef name defined in a standard header and required to be a signed integer type. Must that type also be signed as a bit-field, or might it be unsigned (for example, plain int)?

#include <stddef.h>
#include <stdint.h>

struct s1 {
  ptrdiff_t b1a : 20;
  int32_t b1b : 20;
};

Although ptrdiff_t illustrates that this issue was present in C90, it may be more likely to be encountered with types such as int32_t.

If the option for bit-fields to be implicitly unsigned is not removed, it might be appropriate to require typedefs in standard headers that are required to be signed integer types to be signed as bit-field types as well.

Question 2

C11 introduced the rule that typedef names can be redefined with the same type in the same scope. But what if the type is only the same outside of bit-fields? Is the type of the bit-field considered explicitly signed when one definition uses signed and another does not?

typedef int T;
typedef signed int T;

struct s2 {
  T b2 : 20;
};

Question 3

In some cases, it seems clear whether a type from the typeof specifiers should be considered explicitly signed. For example, typeof specifiers applied to a type name do not introduce any issues, and if the typeof specifiers are applied to an expression referring to a single declaration, or to a cast, it seems clear whether the resulting type is explicitly signed.

int i;
signed int si;

struct s3a {
  typeof (int) b3a : 20; // not explicitly signed
  typeof (signed int) b3b : 20; // explicitly signed
  typeof (i) b3c : 20; // not explicitly signed
  typeof (si) b3d : 20; // explicitly signed
  typeof ((int) si) b3e : 20; // not explicitly signed
  typeof ((signed int) i) b3f : 20; // explicitly signed
};

In some other cases, it is less clear: where composite types are involved, or usual arithmetic conversions, for example.

int i;
signed int si;
extern int x;
extern signed int x;

struct s3b {
  typeof (x) b3g : 20;
  typeof (i + si) b3h : 20;
};

In some further cases, the type appears to be specified as not explicitly signed (and so potentially unsigned as a bit-field) because the standard says int rather than signed int, but may not have been considering this issue.

signed short s;

struct s3c {
  typeof (+s) b3i : 20; // integer promotions convert signed short to int
  typeof (s == s) b3j : 20; // comparisons return int
  typeof (0) b3k : 20; // this integer literal has type int
  typeof (-1) b3l : 20; // negating an int literal leaves type int
};

Which of the above cases should be considered explicitly signed, and which should not?

Question 4

Does the rule about implementation-defined signedness include _BitInt?

struct s4 {
  _BitInt (32) b4 : 20; // may this be unsigned?
};

I think there is a reasonable case to answer "no" to this question based only on the current wording, on the basis that the response to issue 0315 was only concerned with implementation-defined bit-field types, and support for _BitInt types for bit-fields is not implementation-defined.

Question 5

If the answer to Question 4 is "yes", does the constraint disallowing a type _BitInt(1) apply before or after the reinterpretation as unsigned for a bit-field?

struct s5 {
  _BitInt (1) b5 : 1; // is this valid if _BitInt bit-fields are unsigned?
};


Issue 1008: Bad constraint on attributes with tags

Authors: Joseph Myers
Date: 2025-03-11
Status: Open

C23 6.7.3.2 (Structure and union specifiers) contains a constraint:

An attribute specifier sequence shall not appear in a struct-or-union specifier without a member declaration list, except in a declaration of the form:

struct-or-union attribute-specifier-sequence identifier ;

C23 6.7.3.4 (Tags) contains a stricter constraint:

A type specifier of the form

struct-or-union attribute-specifier-sequenceopt identifier

shall not contain an attribute specifier sequence.

The latter constraint does not make sense; if such an attribute specifier sequence is not allowed, why is this case in the syntax for struct-or-union-specifier at all?

The stated intent given in drafting notes in N2335 was:

struct [[something]] x; /* valid */
void f(struct [[something]] s); /* invalid */

However, both those cases go through the syntax for type-specifier, so both would be invalid with the constraints as written.

I think the latter constraint is in fact not needed at all because everything relevant is covered in the first quoted constraint.

Suggested correction

In C23 6.7.3.4 (Tags), remove the paragraph (including attached footnote):

A type specifier of the form

struct-or-union attribute-specifier-sequenceopt identifier

shall not contain an attribute specifier sequence.141)

141) As specified in 6.7.3.2, the type specifier can be followed by a ; or a member declaration list.



Issue 1009: extern thread_local should not be an external definition

Authors: Joseph Myers
Date: 2025-03-12
Status: Open

The following issue was pointed out by Ori Bernstein in reflector message 25279.

C23 6.9.3 says:

If the declaration of an identifier for an object has file scope and an initializer, or has file scope and storage-class specifier thread_local, the declaration is an external definition for the identifier.

This fails to allow for the combination extern thread_local, which should not be an external definition.

Suggested correction

In C23 6.9.3, amend the quoted paragraph as follows:

If the declaration of an identifier for an object has file scope and an initializer, or has file scope and storage-class specifier thread_local without extern, the declaration is an external definition for the identifier.



Issue 1010: Termination of the execution with threads

Authors: Jens Gustedt
Date: 2025-03-13
Status: Open

In C23 there is still ambiguity concerning the termination of the execution in the presence of threads, in particular if some threads are detached and not joined during the execution. Here, the behavior of thrd_exit for the last thread is not sufficiently clarified. C23 7.28.5.5 (The thrd_exit function) p5 states

The program terminates normally after the last thread has been terminated. The behavior is as if the program called the exit function with the status EXIT_SUCCESS at thread termination time.

This text seems to indicate that the notion of happens-before extends to the overall termination of all threads (thus a "last" thread), but it leaves it unspecified whether or not the execution of the cleanup code that is run on exit (atexit handlers, closure of streams and similar) synchronizes with threads that have been detached.

Our observation of implementations seems to indicate that such a synchronization does indeed take place, it would be better to note this explicitly in the standard. We propose to change the above paragraph to the following:

The program terminates normally after the last thread has been terminated. The behavior is as if the program end of the call to thrd_exit of this last thread synchronized with all terminations of other threads of the execution and thereafter is called the exit function with the status EXIT_SUCCESS at thread termination time.



Issue 1011: powr(negative, qNaN)

Authors: Joseph Myers
Date: 2025-03-18
Status: Open

Annex F is unclear about whether powr(x, qNaN) raises "invalid" for negative x, though the intent seems to be that it should (as indicated in reflector message 29708 and CFP message 3415).

C23 F.10.1 (Mathematics <math.h> and <tgmath.h>) says:

Functions with a NaN argument return a NaN result and raise no floating-point exception, except where explicitly stated otherwise.

C23 F.10.5.7 (The powr functions) says:

powr(x, y) returns a NaN and raises the "invalid" floating-point exception for x < 0.

This doesn't explicitly include the case where y is a NaN. By contrast, F.10.5.8 (The rootn functions) says:

rootn(x, 0) returns a NaN and raises the "invalid" floating-point exception for all x (including NaN).

IEEE 754-2019 says in clause 9:

powr(x, y) signals the invalid operation exception for x < 0

and:

powr(x, qNaN) is qNaN for x ≥ 0

together with a more general statement:

Outside its domain an operation shall return a quiet NaN and signal the invalid operation exception.

This seems to indicate that "invalid" should be raised in this case.

Suggested correction

In F.10.5.7 (The powr functions), amend the seventh bullet point as indicated.

powr(x, y) returns a NaN and raises the "invalid" floating-point exception for x < 0 and all y (including NaN).



Issue 1012: Error returns from specific width length modifiers

Authors: Joseph Myers
Date: 2025-03-18
Status: Open
Cross-references: 1004

Each function in the printf and scanf family has its own Returns specification that describes error cases and corresponding return values as well as the return value on success (the error cases sometimes depend on the specific function from the family; for example, some kinds of errors can only occur for the functions using files, not those using strings).

When error cases were added to fprintf, fscanf, fwprintf and fwscanf for unsupported specific width length modifiers, no corresponding changes were made to the Returns specification for the other functions in those families, so failing to give those other functions a corresponding error case (and leaving them with implicit undefined behavior in this case). It seems clear that all functions in these families should have corresponding error handling.

This is related to but separate from issue 1004, which concerns the classification of such errors from scanf functions.

Also, the descriptions for printf functions refer to "specified width length modifier" but those for scanf functions refer to "specific width length modifier", with no apparent reason for the inconsistent terminology.

Suggested correction

All these changes are to the Returns specification of their respective functions, and all subclause numbers are in C23.

First, for consistency of terminology, make the following changes.

In 7.23.6.2 (The fprintf function), change:

The fprintf function returns the number of characters transmitted, or a negative value if an output or encoding error occurred or if the implementation does not support a specified specific width length modifier.

In 7.31.2.2 (The fwprintf function), change:

The fwprintf function returns the number of wide characters transmitted, or a negative value if an output or encoding error occurred or if the implementation does not support a specified specific width length modifier.

Then amend the other functions in these families as follows.

In each of 7.23.6.4 (The printf function), 7.23.6.6 (The snprintf function), 7.23.6.7 (The sprintf function), 7.23.6.9 (The vfprintf function), 7.23.6.11 (The vprintf function), 7.23.6.13 (The vsnprintf function), 7.23.6.14 (The vsprintf) function), 7.31.2.4 (The swprintf function), 7.31.2.6 (The vfwprintf function), 7.31.2.8 (The vswprintf function), 7.31.2.10 (The vwprintf function), 7.31.2.12 (The wprintf function), K.3.5.4.7 (The sprintf_s function), K.3.5.4.14 (The vsprintf_s function), K.3.9.2.4 (The swprintf_s function), and K.3.9.2.9 (The vswprintf_s function), change:

... encoding error occurred or if the implementation does not support a specific width length modifier ...

(Some of these functions also refer to output errors earlier in the amended text; others, where the output is to a string rather than a file, do not. In the case of swprintf,vswprintf, swprintf_s, and vswprintf_s, the amended text is followed by another error case in the same sentence; in the case of sprintf_s, vsprintf_s, swprintf_s, and vswprintf_s, a description of the return value appears after the amended text.)

In each of 7.23.6.5 (The scanf function), 7.23.6.8 (The sscanf function), 7.23.6.10 (The vfscanf function), 7.23.6.12 (The vscanf function), 7.23.6.15 (The vsscanf function), 7.31.2.5 (The swscanf function), 7.31.2.7 (The vfwscanf function), 7.31.2.9 (The vswscanf function), 7.31.2.11 (The vwscanf function), 7.31.2.13 (The wscanf function), K.3.5.4.3 (The fscanf_s function), K.3.5.4.5 (The scanf_s function), K.3.5.4.8 (The sscanf_s function), K.3.5.4.10 (The vfscanf_s function), K.3.5.4.12 (The vscanf_s function), K.3.5.4.15 (The vsscanf_s function), K.3.9.2.2 (The fwscanf_s function), K.3.9.2.5 (The swscanf_s function), K.3.9.2.7 (The vfwscanf_s function), K.3.9.2.10 (The vswscanf_s function), K.3.9.2.12 (The vwscanf_s function), and K.3.9.2.14 (The wscanf_s function), change:

... in the event of an early matching failure or if the implementation does not support a specific width length modifier.

In each of K.3.5.4.2 (The fprintf_s function), K.3.5.4.4 (The printf_s function), K.3.5.4.6 (The snprintf_s function), K.3.5.4.9 (The vfprintf_s function), K.3.5.4.11 (The vprintf_s function), K.3.5.4.13 (The vsnprintf_s function), K.3.9.2.1 (The fwprintf_s function), K.3.9.2.3 (The snwprintf_s function), K.3.9.2.6 (The vfwprintf_s function), K.3.9.2.8 (The vsnwprintf_s function), K.3.9.2.11 (The vwprintf_s function), and K.3.9.2.13 (The wprintf_s function), change:

... runtime-constraint violation occurred or if the implementation does not support a specific width length modifier.



Issue 1013: Ambiguity of "same type"

Authors: Joseph Myers
Date: 2025-03-18
Status: Open

Most places in the standard requiring consistency of types in two declarations or expressions use the notion of "compatible type". Two places, however, require the types to be the "same type", not just compatible, so avoiding needing to merge information from separate declarations but encountering places where it is not clear what counts as the same type (a concept with no definition in the standard).

C23 6.7.1 (Declarations) says:

a typedef name can be redefined to denote the same type as it currently does, provided that type is not a variably modified type;

C23 6.7.3.4 (Tags) says:

Where two declarations that use the same tag declare the same type, they shall both use the same choice of struct, union, or enum. If two declarations of the same type have a member-declaration or enumerator-list, one shall not be nested within the other and both declarations shall fulfill all requirements of compatible types (6.2.7) with the additional requirement that corresponding members of structure or union types shall have the same (and not merely compatible) types.

No specific wording is suggested to address the ambiguities identified here; direction from the committee is needed on what the answers should be, and maybe on the overall approach to take to defining "same type" better.

Question 1

Do function type attributes form part of the type for the purposes of "same type"?

typedef int T1(int);
typedef int T1(int) [[reproducible]];

Suggested answer: they form part of the type (so where implementations accept this example, that is a bug).

Question 2

Are function types the same if one specified a parameter as a pointer, and another specified it as a function or array adjusted to a pointer, or if one specified a qualified type for a parameter and another specified an unqualified type?

typedef int T2A(int *);
typedef int T2A(int []);

typedef int T2B(int (*)());
typedef int T2B(int ());

typedef int T2C(int);
typedef int T2C(const int);

C23 6.7.7.4 (Function declarators) says:

In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.

That wording does not refer to "same type", but it would seem rather confusing for the types before adjustment to affect "same type" when not affecting the function's type in other ways.

Suggested answer: those pairs of types are the same (but wording may need amending to make that clear).

Question 3

Does the use of static inside a parameter array declarator affect the type for the purposes of "same type"?

typedef int T3(int [2]);
typedef int T3(int [static 2]);

Suggested answer: it should not affect the type, if the suggested answer to the previous question is followed.

Question 4

Both the uses of the "same type" concept are in contexts where variably modified types are disallowed, to avoid problems comparing such types. However, in the presence of typeof that does not in fact suffice to prevent such comparison issues, because of variably modified parameters in function definitions, which are not treated the same as [*] but also do not make the function type itself variably modified. Which of the following are considered the same type?

void f4a(int m, char (*p)[m]) {}
void f4b(int n, char (*p)[n]) {}
void f4c(int n, char (*p)[(n)]) {}
void f4d(int t, char (*p)[t*1]) {}
void f4e(int t, char (*p)[t+1]) {}

typedef void T4(int, char (*)[*]);
typedef typeof(f4a) T4;
typedef typeof(f4b) T4;
typedef typeof(f4c) T4;
typedef typeof(f4d) T4;
typedef typeof(f4e) T4;

Suggested approach for resolving this: apply the adjustment to [*] for the purposes of the function type even in the case of function definitions (but without affecting the type of the parameters when referenced inside the definition).

Question 5

A similar issue applies when parameters are declared using a block-scope typedef name for a variably modified type; the adjustment to [*] appears not to occur in that case either. Which of the following are considered the same type?

void f(int a)
{
  typedef int T[a];
  typedef int U[a];
  typedef void T5(T*);
  typedef void T5(T*);
  typedef void T5(U*);
}

Suggested approach for resolving this: apply the adjustment to [*] to any VLA type referenced in a parameter type, including when it comes from a typedef name, not just when it uses an array declarator.