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