From f4d81ced3e3f5eab50e8f70f8830da4aa9be68d6 Mon Sep 17 00:00:00 2001 From: Christopher Bazley Date: Sat, 26 Sep 2026 13:27:47 +0100 Subject: [PATCH] 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. --- tcc-doc.texi | 4 ++- tcc.h | 19 +++++++------ tccgen.c | 32 ++++++++++++++++++++-- tests/tests2/100_c99array-decls.c | 20 ++++++++++++++ tests/tests2/60_errors_and_warnings.c | 20 ++++++++++++++ tests/tests2/60_errors_and_warnings.expect | 9 ++++++ 6 files changed, 92 insertions(+), 12 deletions(-) diff --git a/tcc-doc.texi b/tcc-doc.texi index 1ffc0b24..07326291 100644 --- a/tcc-doc.texi +++ b/tcc-doc.texi @@ -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 diff --git a/tcc.h b/tcc.h index 96e03198..864a4e32 100644 --- a/tcc.h +++ b/tcc.h @@ -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) diff --git a/tccgen.c b/tccgen.c index 23116d00..d8f32875 100644 --- a/tccgen.c +++ b/tccgen.c @@ -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; diff --git a/tests/tests2/100_c99array-decls.c b/tests/tests2/100_c99array-decls.c index 46950aa2..af0143b0 100644 --- a/tests/tests2/100_c99array-decls.c +++ b/tests/tests2/100_c99array-decls.c @@ -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 *]); diff --git a/tests/tests2/60_errors_and_warnings.c b/tests/tests2/60_errors_and_warnings.c index c13aed83..2990bc43 100644 --- a/tests/tests2/60_errors_and_warnings.c +++ b/tests/tests2/60_errors_and_warnings.c @@ -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 diff --git a/tests/tests2/60_errors_and_warnings.expect b/tests/tests2/60_errors_and_warnings.expect index 63aa9912..375d6b4a 100644 --- a/tests/tests2/60_errors_and_warnings.expect +++ b/tests/tests2/60_errors_and_warnings.expect @@ -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