tccgen: Minimal implementation of restrict

TCC currently recognises the restrict keyword but
discards it. Give the qualifier its own type bit and
diagnose misuse that violates constraints in ISO C.

There is (obviously) no new aliasing analysis.
This commit is contained in:
Christopher Bazley 2026-09-26 13:27:47 +01:00
parent c9829912d7
commit f4d81ced3e
6 changed files with 92 additions and 12 deletions

View File

@ -669,7 +669,9 @@ is the same as writing
emitted at the end of the compilation unit only if used. The behavior
of @code{extern inline} depends on @option{-fgnu89-inline} (@pxref{Invoke}).
@item @code{restrict} keyword is ignored.
@item @code{restrict} type qualifier. TCC retains the qualifier and checks
that it is applied only to pointers to object or incomplete types. It is not
currently used for alias analysis or optimization.
@end itemize
@section ISO C11 extensions

19
tcc.h
View File

@ -1063,17 +1063,18 @@ struct filespec {
#define VT_BITFIELD 0x0080 /* bitfield modifier */
#define VT_CONSTANT 0x0100 /* const modifier */
#define VT_VOLATILE 0x0200 /* volatile modifier */
#define VT_QUAL (VT_CONSTANT | VT_VOLATILE)
#define VT_VLA 0x0400 /* VLA type (also has VT_PTR and VT_ARRAY) */
#define VT_LONG 0x0800 /* long type (also has VT_INT rsp. VT_LLONG) */
#define VT_RESTRICT 0x0400 /* restrict modifier */
#define VT_QUAL (VT_CONSTANT | VT_VOLATILE | VT_RESTRICT)
#define VT_VLA 0x0800 /* VLA type (also has VT_PTR and VT_ARRAY) */
#define VT_LONG 0x1000 /* long type (also has VT_INT rsp. VT_LLONG) */
/* storage */
#define VT_EXTERN 0x00001000 /* extern definition */
#define VT_STATIC 0x00002000 /* static variable */
#define VT_TYPEDEF 0x00004000 /* typedef definition */
#define VT_INLINE 0x00008000 /* inline definition */
#define VT_TLS 0x00010000 /* thread-local storage */
/* currently unused: 0x000[248]0000 */
#define VT_EXTERN 0x00002000 /* extern definition */
#define VT_STATIC 0x00004000 /* static variable */
#define VT_TYPEDEF 0x00008000 /* typedef definition */
#define VT_INLINE 0x00010000 /* inline definition */
#define VT_TLS 0x00020000 /* thread-local storage */
/* currently unused: 0x000[48]0000 */
#define VT_STRUCT_SHIFT 20 /* shift for bitfield shift values (32 - 2*6) */
#define VT_STRUCT_MASK (((1U << (6+6)) - 1) << VT_STRUCT_SHIFT | VT_BITFIELD)

View File

@ -136,6 +136,7 @@ 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 void check_restrict_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);
@ -2677,6 +2678,8 @@ static void type_to_str(char *buf, int buf_size,
if (t & VT_INLINE)
pstrcat(buf, buf_size, "inline ");
if (bt != VT_PTR) {
if (t & VT_RESTRICT)
pstrcat(buf, buf_size, "restrict ");
if (t & VT_VOLATILE)
pstrcat(buf, buf_size, "volatile ");
if (t & VT_CONSTANT)
@ -2775,6 +2778,8 @@ static void type_to_str(char *buf, int buf_size,
goto no_var;
}
pstrcpy(buf1, sizeof(buf1), "*");
if (t & VT_RESTRICT)
pstrcat(buf1, sizeof(buf1), "restrict ");
if (t & VT_CONSTANT)
pstrcat(buf1, buf_size, "const ");
if (t & VT_VOLATILE)
@ -3568,6 +3573,18 @@ static inline CType *pointed_type(CType *type)
return &type->ref->type;
}
static void check_restrict_type(CType *type)
{
while ((type->t & VT_BTYPE) == VT_PTR) {
if ((type->t & VT_RESTRICT)
&& (pointed_type(type)->t & VT_BTYPE) == VT_FUNC)
tcc_error("restrict-qualified type must be a pointer to object or incomplete type");
type = pointed_type(type);
}
if (type->t & VT_RESTRICT)
tcc_error("restrict-qualified type must be a pointer to object or incomplete type");
}
/* Array qualifiers are represented on the element type. */
static int type_qualifiers(CType *type)
{
@ -4724,6 +4741,8 @@ static void parse_btype_qualify(CType *type, int qualifiers)
type->ref = sym_push(SYM_FIELD, &type->ref->type, 0, type->ref->c);
type = &type->ref->type;
}
if (qualifiers & VT_RESTRICT)
check_restrict_type(type);
type->t |= qualifiers;
}
@ -4882,11 +4901,14 @@ static int parse_btype(CType *type, AttributeDef *ad, int ignore_label)
next();
typespec_found = 1;
break;
case TOK_REGISTER:
case TOK_AUTO:
case TOK_RESTRICT1:
case TOK_RESTRICT2:
case TOK_RESTRICT3:
t |= VT_RESTRICT;
next();
break;
case TOK_REGISTER:
case TOK_AUTO:
next();
break;
case TOK_UNSIGNED:
@ -4998,6 +5020,7 @@ the_end:
t = (t & ~(VT_BTYPE|VT_LONG)) | (VT_DOUBLE|VT_LONG);
#endif
type->t = t;
check_restrict_type(type);
return type_found;
}
@ -5162,6 +5185,9 @@ static int post_type(CType *type, AttributeDef *ad, int storage, int td)
next();
continue;
case TOK_RESTRICT1: case TOK_RESTRICT2: case TOK_RESTRICT3:
array_qualifiers |= VT_RESTRICT;
next();
continue;
case TOK_STATIC:
case '*':
next();
@ -5297,6 +5323,7 @@ static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td)
case TOK_RESTRICT1:
case TOK_RESTRICT2:
case TOK_RESTRICT3:
qualifiers |= VT_RESTRICT;
goto redo;
/* XXX: clarify attribute handling */
case TOK_ATTRIBUTE1:
@ -5336,6 +5363,7 @@ static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td)
}
post_type(post, ad, post != ret ? 0 : storage,
td & ~(TYPE_DIRECT|TYPE_ABSTRACT));
check_restrict_type(type);
parse_attribute(ad);
type->t |= storage;
return ret;

View File

@ -20,6 +20,26 @@ void fooc(int x[volatile 5])
}
void foovm(int x[const *]);
void foovm(int * const x);
typedef int *pointer_array[2];
typedef int *restrict restricted_pointer;
typedef restrict pointer_array restricted_pointer_array;
_Static_assert(!__builtin_types_compatible_p(int **,
restricted_pointer *),
"nested restrict qualifier is retained");
_Static_assert(__builtin_types_compatible_p(restricted_pointer_array,
restricted_pointer[2]),
"restrict can qualify an array of pointer types");
restricted_pointer *restricted_pp;
int **unrestricted_pp;
_Static_assert(__builtin_types_compatible_p(
__typeof__(1 ? restricted_pp : unrestricted_pp),
restricted_pointer *),
"conditional expression combines restrict qualifiers");
#ifdef INVALID
void wrongc(int x[3][const 4]);
void wrongvm(int x[static *]);

View File

@ -679,4 +679,24 @@ void test(void)
(1 ? vap : cap)[0][0][0] = 4;
}
#elif defined test_restrict_scalar
restrict int object;
#elif defined test_restrict_function_pointer
int (*restrict function_pointer)(void);
#elif defined test_discarded_restrict
typedef int *restrict restricted_pointer;
restricted_pointer *source;
int **destination;
void test(void)
{
destination = source;
}
#endif

View File

@ -296,3 +296,12 @@ bar 15 12 34
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
[test_restrict_scalar]
60_errors_and_warnings.c:684: error: restrict-qualified type must be a pointer to object or incomplete type
[test_restrict_function_pointer]
60_errors_and_warnings.c:688: error: restrict-qualified type must be a pointer to object or incomplete type
[test_discarded_restrict]
60_errors_and_warnings.c:699: warning: assignment discards qualifiers from pointer target type