tccgen: combine qualifiers through array types

Conditional expressions combine the qualifiers
of their pointer target types. TCC only examines
the outer array node, although array qualifiers
are stored on the element type. This makes the
result depend on operand order.

Collect and apply qualifiers through array layers.
This commit is contained in:
Christopher Bazley 2026-09-07 08:39:41 +01:00
parent 4666db3288
commit 0cd015b4ed
4 changed files with 45 additions and 4 deletions

View File

@ -136,6 +136,8 @@ static void block(int flags);
static void gen_cast(CType *type);
static void gen_cast_s(int t);
static inline CType *pointed_type(CType *type);
static int type_qualifiers(CType *type);
static void parse_btype_qualify(CType *type, int qualifiers);
static int is_compatible_types(CType *type1, CType *type2);
static int parse_btype(CType *type, AttributeDef *ad, int ignore_label);
static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td);
@ -2972,15 +2974,14 @@ static int combine_types(CType *dest, SValue *op1, SValue *op2, int op)
pointed to types minus qualifs should be compatible */
type = *((pbt1 == VT_VOID) ? type1 : type2);
/* combine qualifs */
newquals = ((pt1->t | pt2->t) & (VT_CONSTANT | VT_VOLATILE));
if ((~pointed_type(&type)->t & (VT_CONSTANT | VT_VOLATILE))
& newquals)
newquals = type_qualifiers(pt1) | type_qualifiers(pt2);
if ((~type_qualifiers(pointed_type(&type))) & newquals)
{
/* copy the pointer target symbol */
type.ref = sym_push(SYM_FIELD, &type.ref->type,
0, type.ref->c);
copied = 1;
pointed_type(&type)->t |= newquals;
parse_btype_qualify(pointed_type(&type), newquals);
}
/* pointers to incomplete arrays get converted to
pointers to completed ones if possible */
@ -3567,6 +3568,14 @@ static inline CType *pointed_type(CType *type)
return &type->ref->type;
}
/* Array qualifiers are represented on the element type. */
static int type_qualifiers(CType *type)
{
while (type->t & VT_ARRAY)
type = pointed_type(type);
return type->t & (VT_CONSTANT | VT_VOLATILE);
}
/* modify type so that its it is a pointer to type. */
ST_FUNC void mk_pointer(CType *type)
{

View File

@ -3956,6 +3956,14 @@ void builtin_test(void)
COMPAT_TYPE(int[2], const int[2]);
COMPAT_TYPE(int[2][3], volatile int[][3]);
COMPAT_TYPE(int (*)[], const int (*)[]);
COMPAT_TYPE(__typeof__(1 ? (const int (*)[2])0 : (int (*)[2])0),
const int (*)[2]);
COMPAT_TYPE(__typeof__(1 ? (const int (*)[2][3])0 :
(volatile int (*)[2][3])0),
const volatile int (*)[2][3]);
COMPAT_TYPE(__typeof__(1 ? (volatile int (*)[2][3])0 :
(const int (*)[2][3])0),
const volatile int (*)[2][3]);
COMPAT_TYPE(int *, int *);
COMPAT_TYPE(int *, void *);
COMPAT_TYPE(int *, const int *);

View File

@ -661,4 +661,22 @@ void good_volatile_write(volatile my_struct *s)
s->arr[0] = 1;
}
#elif defined test_conditional_array_qualifiers
typedef int Array2[2];
typedef int Array2x3[2][3];
Array2 *p;
const Array2 *cp;
const Array2x3 *cap;
volatile Array2x3 *vap;
void test(void)
{
(1 ? p : cp)[0][0] = 1;
(1 ? cp : p)[0][0] = 2;
(1 ? cap : vap)[0][0][0] = 3;
(1 ? vap : cap)[0][0][0] = 4;
}
#endif

View File

@ -290,3 +290,9 @@ bar 15 12 34
60_errors_and_warnings.c:651: warning: assignment discards qualifiers from pointer target type
[test_volatile_array_member_write_ok]
[test_conditional_array_qualifiers]
60_errors_and_warnings.c:676: warning: assignment of read-only location
60_errors_and_warnings.c:677: warning: assignment of read-only location
60_errors_and_warnings.c:678: warning: assignment of read-only location
60_errors_and_warnings.c:679: warning: assignment of read-only location