diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index f3181e7b57c..050eb6a7010 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -5117,8 +5117,9 @@ handle_nonstring_attribute (tree *node, tree name, tree ARG_UNUSED (args), if (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE) { /* Accept the attribute on arrays and pointers to all three - narrow character types. */ - tree eltype = TREE_TYPE (type); + narrow character types, including multi-dimensional arrays + or pointers to them. */ + tree eltype = strip_array_types (TREE_TYPE (type)); eltype = TYPE_MAIN_VARIANT (eltype); if (eltype == char_type_node || eltype == signed_char_type_node diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-10.c b/gcc/testsuite/c-c++-common/attr-nonstring-10.c new file mode 100644 index 00000000000..bf1ce2aa2f7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-nonstring-10.c @@ -0,0 +1,75 @@ +/* Test to exercise attribute "nonstring". + { dg-do compile } + { dg-options "-O2 -Wattributes -Wstringop-truncation -ftrack-macro-expansion=0" } */ + +#define ATTR(list) __attribute__ (list) +#define NONSTR ATTR ((nonstring)) +#define strncpy(d, s, n) (__builtin_strncpy ((d), (s), (n)), sink (d)) + +void sink (void*); + +/* Global string with an known bound. */ +extern char gns3[][3] NONSTR; + +/* Global non-string pointers. */ +extern NONSTR char (*pns_1)[1]; +extern char (* NONSTR pns_2)[2]; +extern char (*pns_3)[3] NONSTR; + +struct MemArrays +{ + NONSTR char ma3[2][3]; + char NONSTR ma4[3][4]; + char ma5[4][5] NONSTR; +}; + + +void test_array (const char *s, unsigned n) +{ + const char s7[] = "1234567"; + + strncpy (gns3[2], "", 0); + strncpy (gns3[2], "a", 1); + strncpy (gns3[2], "a", 2); + strncpy (gns3[2], "a", 3); + strncpy (gns3[2], "ab", 3); + strncpy (gns3[2], "abc", 3); +} + + +void test_pointer (const char *s, unsigned n) +{ + const char s7[] = "1234567"; + + strncpy (*pns_1, "a", 1); + strncpy (*pns_2, "ab", 2); + strncpy (*pns_3, "abc", 3); + strncpy (*pns_3, s7, 3); + + strncpy (*pns_1, s, 1); + strncpy (*pns_2, s, 1); + strncpy (*pns_3, s, 1); + + strncpy (*pns_1, s, n); + strncpy (*pns_2, s, n); + strncpy (*pns_3, s, n); +} + + +void test_member_array (struct MemArrays *p, const char *s, unsigned n) +{ + const char s7[] = "1234567"; + + strncpy (p->ma3[1], "", 0); + strncpy (p->ma3[1], "a", 1); + strncpy (p->ma4[2], "ab", 2); + strncpy (p->ma5[3], "abc", 3); + + strncpy (p->ma3[1], s, 1); + strncpy (p->ma4[2], s, 1); + strncpy (p->ma5[3], s, 1); + + strncpy (p->ma3[1], s7, n); + strncpy (p->ma4[2], s7, n); + strncpy (p->ma5[3], s7, n); +} diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-11.c b/gcc/testsuite/c-c++-common/attr-nonstring-11.c new file mode 100644 index 00000000000..12fbc9005e3 --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-nonstring-11.c @@ -0,0 +1,466 @@ +/* Test to exercise warnings when an array declared with attribute "nonstring" + is passed to a function that expects a nul-terminated string as an argument. + { dg-do compile } + { dg-options "-O2 -Wattributes -Wstringop-overflow -ftrack-macro-expansion=0" } */ + +typedef __SIZE_TYPE__ size_t; +typedef __builtin_va_list va_list; + +#if __cplusplus +extern "C" { +#endif + +void* memchr (const void*, int, size_t); +int memcmp (const void*, const void*, size_t); +void* memcpy (void*, const void*, size_t); +void* memmove (void*, const void*, size_t); + +int printf (const char*, ...); +int puts (const char*); +int puts_unlocked (const char*); +int sprintf (char*, const char*, ...); +int snprintf (char*, size_t, const char*, ...); +int vsprintf (char*, const char*, va_list); +int vsnprintf (char*, size_t, const char*, va_list); + +int strcmp (const char*, const char*); +int strncmp (const char*, const char*, size_t); + +char* stpcpy (char*, const char*); +char* stpncpy (char*, const char*, size_t); + +char* strcat (char*, const char*); +char* strncat (char*, const char*, size_t); + +char* strcpy (char*, const char*); +char* strncpy (char*, const char*, size_t); + +char* strchr (const char*, int); +char* strdup (const char*); +size_t strlen (const char*); +size_t strnlen (const char*, size_t); +char* strndup (const char*, size_t); + +#if __cplusplus +} /* extern "C" */ +#endif + +#define NONSTRING __attribute__ ((nonstring)) + +/* STR needs to be bigger than ARR to trigger warnings, otherwise + since STR must be a string, using both in a string function + can be assumed to be safe even if ARR isn't nul-terminated. */ +char str[3][5]; +char arr[3][4] NONSTRING; + +char (*ptr)[6]; +char (*parr)[6] NONSTRING; + +struct MemArrays +{ + char str[5][5]; + char arr[4][4] NONSTRING; + char (*parr)[4] NONSTRING; +}; + +void sink (int, ...); + + +#define T(call) sink (0, call) + +void test_printf (struct MemArrays *p) +{ + T (printf (str[2])); + T (printf (arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (printf (*ptr)); + T (printf (*parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (printf (p->str[2])); + T (printf (p->arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +void test_puts (struct MemArrays *p) +{ + T (puts (str[2])); + T (puts (arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (puts (*ptr)); + T (puts (*parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (puts (p->str[2])); + T (puts (p->arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +void test_snprintf (char *d, size_t n, struct MemArrays *p) +{ + T (snprintf (d, n, str[2])); + T (snprintf (d, n, arr[2])); /* { dg-warning "argument 3 declared attribute .nonstring." } */ + + T (snprintf (d, n, *ptr)); + T (snprintf (d, n, *parr)); /* { dg-warning "argument 3 declared attribute .nonstring." } */ + + T (snprintf (d, n, p->str[2])); + T (snprintf (d, n, p->arr[2])); /* { dg-warning "argument 3 declared attribute .nonstring." } */ +} + + +void test_sprintf (char *d, struct MemArrays *p) +{ + T (sprintf (d, str[2])); + T (sprintf (d, arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + + T (sprintf (d, *ptr)); + T (sprintf (d, *parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + + T (sprintf (d, p->str[2])); + T (sprintf (d, p->arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */ +} + + +void test_vsnprintf (char *d, size_t n, struct MemArrays *p, va_list va) +{ + T (vsnprintf (d, n, str[2], va)); + T (vsnprintf (d, n, arr[2], va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */ + + T (vsnprintf (d, n, *ptr, va)); + T (vsnprintf (d, n, *parr, va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */ + + T (vsnprintf (d, n, p->str[2], va)); + T (vsnprintf (d, n, p->arr[2], va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */ +} + + +void test_vsprintf (char *d, struct MemArrays *p, va_list va) +{ + T (vsprintf (d, str[2], va)); + T (vsprintf (d, arr[2], va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + + T (vsprintf (d, *ptr, va)); + T (vsprintf (d, *parr, va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + + T (vsprintf (d, p->str[2], va)); + T (vsprintf (d, p->arr[2], va)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ +} + + +void test_strcmp (struct MemArrays *p) +{ + T (strcmp (str[2], str[2])); + T (strcmp (str[2], arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcmp (arr[2], str[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strcmp (str[2], *ptr)); + T (strcmp (str[2], *parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcmp (*parr, str[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strcmp (p->str[2], p->arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcmp (p->arr[2], p->str[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + T (strcmp (*p->parr, p->str[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +void test_strncmp_warn (struct MemArrays *p) +{ + enum { N = sizeof arr[2] }; + T (strncmp (str[2], arr[2], N)); /* { dg-bogus "argument 2 declared attribute 'nonstring' is smaller than the specified bound 4" "" { xfail *-*-* } } */ + T (strncmp (arr[2], str[2], N)); /* { dg-bogus "argument 1 declared attribute 'nonstring' is smaller than the specified bound 4" "" { xfail *-*-* } } */ + + T (strncmp (str[2], arr[2], N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */ + T (strncmp (arr[2], str[2], N + 1)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */ + + T (strncmp (str[2], *parr, N + 1)); + T (strncmp (*parr, str[2], N + 1)); + + T (strncmp (p->str[2], p->arr[2], N)); + T (strncmp (p->arr[2], p->str[2], N)); + T (strncmp (*p->parr, p->str[2], N)); + + T (strncmp (p->str[2], p->arr[2], N)); + T (strncmp (p->arr[2], p->str[2], N)); + T (strncmp (*p->parr, p->str[2], N)); +} + + +void test_strncmp_nowarn (struct MemArrays *p, size_t n) +{ + T (strncmp (str[2], str[2], n)); + T (strncmp (str[2], arr[2], n)); + T (strncmp (arr[2], str[2], n)); + + T (strncmp (str[2], *ptr, n)); + T (strncmp (str[2], *parr, n)); + T (strncmp (*parr, str[2], n)); + + T (strncmp (p->str[2], p->arr[2], n)); + T (strncmp (p->arr[2], p->str[2], n)); + T (strncmp (*p->parr, p->str[2], n)); +} + + +void test_stpcpy (struct MemArrays *p) +{ + T (stpcpy (str[2], str[2])); + T (stpcpy (str[2], arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (stpcpy (arr[2], str[2])); + + T (stpcpy (str[2], *ptr)); + T (stpcpy (str[2], *parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (stpcpy (*parr, str[2])); + + T (stpcpy (p->str[2], p->arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (stpcpy (p->arr[2], p->str[2])); + T (stpcpy (*p->parr, p->str[2])); +} + + +void test_stpncpy_nowarn (struct MemArrays *p, unsigned n) +{ + T (stpncpy (str[2], str[2], n)); + T (stpncpy (str[2], arr[2], n)); + T (stpncpy (arr[2], str[2], n)); + + T (stpncpy (str[2], *ptr, n)); + T (stpncpy (str[2], *parr, n)); + T (stpncpy (*parr, str[2], n)); + + T (stpncpy (p->str[2], p->arr[2], n)); + T (stpncpy (p->arr[2], p->str[2], n)); + T (stpncpy (*p->parr, p->str[2], n)); +} + + +void test_stpncpy_warn (struct MemArrays *p, unsigned n) +{ + enum { N = sizeof arr[2] }; + + T (stpncpy (str[2], str[2], N)); + T (stpncpy (str[2], arr[2], N)); /* { dg-bogus "argument 2 declared attribute 'nonstring' is smaller than the specified bound 4" "" { xfail *-*-* } } */ + T (stpncpy (arr[2], str[2], N)); + + T (stpncpy (str[2], *ptr, N)); + T (stpncpy (str[2], *parr, N)); + T (stpncpy (*parr, str[2], N)); + + T (stpncpy (p->str[2], p->arr[2], N)); + T (stpncpy (p->arr[2], p->str[2], N)); + T (stpncpy (*p->parr, p->str[2], N)); + + T (stpncpy (*ptr, str[2], N + 1)); + T (stpncpy (*ptr, arr[2], N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */ + T (stpncpy (arr[2], str[2], N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */ + + T (stpncpy (*ptr, *ptr, N + 1)); + T (stpncpy (*ptr, *parr, N + 1)); + T (stpncpy (*parr, str[2], N + 1)); + + T (stpncpy (*ptr, p->arr[2], N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller" } */ + T (stpncpy (p->arr[2], p->str[2], N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */ + T (stpncpy (*p->parr, p->str[2], N + 1)); +} + + +void test_strcat (struct MemArrays *p) +{ + T (strcat (str[2], str[2])); + T (strcat (str[2], arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcat (arr[2], str[2])); + + T (strcat (str[2], *ptr)); + T (strcat (str[2], *parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcat (*parr, str[2])); + + T (strcat (p->str[2], p->arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcat (p->arr[2], p->str[2])); + T (strcat (*p->parr, p->str[2])); +} + + +void test_strncat (struct MemArrays *p, unsigned n) +{ + T (strncat (str[2], str[2], n)); + T (strncat (str[2], arr[2], n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strncat (arr[2], str[2], n)); + + T (strncat (str[2], *ptr, n)); + T (strncat (str[2], *parr, n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strncat (*parr, str[2], n)); + + T (strncat (p->str[2], p->arr[2], n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strncat (p->arr[2], p->str[2], n)); + T (strncat (*p->parr, p->str[2], n)); +} + + +void test_strcpy (struct MemArrays *p) +{ + T (strcpy (str[2], str[2])); + T (strcpy (str[2], arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcpy (arr[2], str[2])); + + T (strcpy (str[2], *ptr)); + T (strcpy (str[2], *parr)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcpy (*parr, str[2])); + + T (strcpy (p->str[2], p->arr[2])); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + T (strcpy (p->arr[2], p->str[2])); + T (strcpy (*p->parr, p->str[2])); +} + + +void test_strncpy (struct MemArrays *p, unsigned n) +{ + T (strncpy (str[2], str[2], n)); + T (strncpy (str[2], arr[2], n)); + T (strncpy (arr[2], str[2], n)); + + T (strncpy (str[2], *ptr, n)); + T (strncpy (str[2], *parr, n)); + T (strncpy (*parr, str[2], n)); + + T (strncpy (p->str[2], p->arr[2], n)); + T (strncpy (p->arr[2], p->str[2], n)); + T (strncpy (*p->parr, p->str[2], n)); +} + + +void test_strchr (struct MemArrays *p, int c) +{ + T (strchr (str[2], c)); + T (strchr (arr[2], c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strchr (*ptr, c)); + T (strchr (*parr, c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strchr (p->str[2], c)); + T (strchr (p->arr[2], c)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +void test_strdup (struct MemArrays *p) +{ + T (strdup (str[2])); + T (strdup (arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strdup (*ptr)); + T (strdup (*parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strdup (p->str[2])); + T (strdup (p->arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +void test_stnrdup_nowarn (struct MemArrays *p, size_t n) +{ + T (strndup (str[2], n)); + T (strndup (arr[2], n)); + + T (strndup (*ptr, n)); + T (strndup (*parr, n)); + + T (strndup (p->str[2], n)); + T (strndup (p->arr[2], n)); +} + + +void test_stnrdup_warn (struct MemArrays *p) +{ + enum { N = sizeof arr[2] }; + + T (strndup (str[2], N)); + T (strndup (arr[2], N)); /* { dg-bogus "argument 1 declared attribute 'nonstring' is smaller than the specified bound 4" "" { xfail *-*-* } } */ + + T (strndup (*ptr, N)); + T (strndup (*parr, N)); + + T (strndup (p->str[2], N)); + T (strndup (p->arr[2], N)); + + + T (strndup (arr[2], N + 1)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 5|specified bound 5 exceeds source size 4" } */ + T (strndup (*parr, N + 1)); + T (strndup (p->arr[2], N + 1)); /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 5|specified bound 5 exceeds source size 4" } */ + T (strndup (*p->parr, N + 1)); +} + + +void test_strlen (struct MemArrays *p, char *s NONSTRING, size_t n) +{ + T (strlen (str[2])); + T (strlen (arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strlen (*ptr)); + T (strlen (*parr)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strlen (p->str[2])); + T (strlen (p->arr[2])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + + T (strlen (s)); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + { + strcpy (s, "123"); + T (strlen (s)); + } + + { + char a[][3] __attribute__ ((nonstring)) = { { 1, 2, 3 } }; + + T (strlen (a[0])); /* { dg-warning "argument 1 declared attribute .nonstring." } */ + } + + { + char a[][4] __attribute__ ((nonstring)) = { { 1, 2, 3, 4 } }; + + strcpy (a[0], "12"); + T (strlen (a[0])); + } +} + + +void test_strnlen (struct MemArrays *p, size_t n) +{ + T (strnlen (str[2], n)); + T (strnlen (arr[2], n)); + + T (strnlen (*ptr, n)); + T (strnlen (*parr, n)); + + T (strnlen (p->str[2], n)); + T (strnlen (p->arr[2], n)); +} + + +/* Verify no warnings are issued for raw mempory functions. */ + +void test_mem_functions (struct MemArrays *p, int c, size_t n) +{ + T (memchr (arr[2], c, n)); + T (memchr (*parr, c, n)); + T (memchr (p->arr[2], c, n)); + T (memchr (*p->parr, c, n)); + + T (memcmp (str[2], arr[2], n)); + T (memcmp (arr[2], str[2], n)); + T (memcmp (str[2], *parr, n)); + T (memcmp (*parr, str[2], n)); + T (memcmp (p->str[2], p->arr[2], n)); + T (memcmp (p->arr[2], p->str[2], n)); + T (memcmp (*p->parr, p->str[2], n)); + + T (memcpy (str[2], arr[2], n)); + T (memcpy (arr[2], str[2], n)); + T (memcpy (str[2], *parr, n)); + T (memcpy (*parr, str[2], n)); + T (memcpy (p->str[2], p->arr[2], n)); + T (memcpy (p->arr[2], p->str[2], n)); + T (memcpy (*p->parr, p->str[2], n)); + + T (memmove (str[2], arr[2], n)); + T (memmove (arr[2], str[2], n)); + T (memmove (str[2], *parr, n)); + T (memmove (*parr, str[2], n)); + T (memmove (p->str[2], p->arr[2], n)); + T (memmove (p->arr[2], p->str[2], n)); + T (memmove (*p->parr, p->str[2], n)); +} diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-12.c b/gcc/testsuite/c-c++-common/attr-nonstring-12.c new file mode 100644 index 00000000000..75787e898be --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-nonstring-12.c @@ -0,0 +1,79 @@ +/* PR middle-end/83131 - c-c++/common/attr-nonstring-3 failure for strcmp + tests on PowerPC + { dg-do compile } + { dg-options "-O2 -Wstringop-overflow -ftrack-macro-expansion=0" } */ + +#if __cplusplus +extern "C" { +#endif + +typedef __SIZE_TYPE__ size_t; + +extern int strcmp (const char*, const char*); +extern int strncmp (const char*, const char*, size_t); + +#if __cplusplus +} /* extern "C" */ +#endif + +extern char arx[][18] __attribute__ ((nonstring)); +extern char ar5[5][5] __attribute__ ((nonstring)); +extern char str[][18]; + +enum { N = sizeof ar5[2] }; +enum { X = sizeof ar5[2] + 1 }; + + +int warn_strcmp_cst_1 (void) +{ + return strcmp ("bar", arx[3]); /* { dg-warning "argument 2 declared attribute .nonstring." } */ +} + +int warn_strcmp_cst_2 (void) +{ + return strcmp (arx[3], "foo"); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +int warn_strncmp_cst_1 (void) +{ + return strncmp ("12345", ar5[2], X); /* { dg-warning "argument 2 declared attribute .nonstring." } */ +} + +int warn_strncmp_cst_2 (void) +{ + return strncmp (ar5[2], "12345", X); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +int nowarn_strncmp_cst_1 (void) +{ + return strncmp ("12345", ar5[2], N); +} + +int nowarn_strncmp_cst_2 (void) +{ + return strncmp (ar5[2], "12345", N); +} + + +int warn_strncmp_var_1 (void) +{ + return strncmp (str[5], ar5[2], X); /* { dg-warning "argument 2 declared attribute .nonstring." } */ +} + +int warn_strncmp_var_2 (void) +{ + return strncmp (ar5[2], str[5], X); /* { dg-warning "argument 1 declared attribute .nonstring." } */ +} + + +int nowarn_strncmp_var_1 (void) +{ + return strncmp (str[5], ar5[2], N); +} + +int nowarn_strncmp_var_2 (void) +{ + return strncmp (ar5[2], str[5], N); +} diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-13.c b/gcc/testsuite/c-c++-common/attr-nonstring-13.c new file mode 100644 index 00000000000..87beb799925 --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-nonstring-13.c @@ -0,0 +1,131 @@ +/* PR middle-end/84725 - enable attribute nonstring for all narrow character + types + Verify that using attribute nonstring with all three narrow character + types is accepted and using arrays and pointers to characters of all + three types (including their qualified forms) declared with the + attributes doesn't trigger -Wstringop-truncation warnings. + { dg-do compile } + { dg-options "-O -Wall -Wstringop-truncation" } */ + +#if __cplusplus +extern "C" +#endif +char* strncpy (char*, const char*, __SIZE_TYPE__); + +#define NONSTR __attribute__ ((nonstring)) + +#define S "1234" + +struct Arrays +{ + char NONSTR a[4][4]; + signed char NONSTR b[4][4]; + unsigned char NONSTR c[4][4]; +}; + +void test_arrays (struct Arrays *p, const char *s) +{ + strncpy (p->a[2], s, sizeof p->a[2]); + strncpy ((char*)p->b[2], s, sizeof p->b[2]); + strncpy ((char*)p->c[2], s, sizeof p->c[2]); +} + +struct Pointers +{ + char NONSTR (*p)[4]; + signed char NONSTR (*q)[4]; + unsigned char NONSTR (*r)[4]; +}; + +void test_pointers (struct Pointers *p) +{ + strncpy (*p->p, S, sizeof S - 1); + strncpy ((char*)*p->q, S, sizeof S - 1); + strncpy ((char*)*p->r, S, sizeof S - 1); +} + +struct ConstArrays +{ + const char NONSTR a[4][4]; + const signed char NONSTR b[4][4]; + const unsigned char NONSTR c[4][4]; +}; + +void test_const_arrays (struct ConstArrays *p, const char *s) +{ + strncpy ((char*)p->a[2], s, sizeof p->a[2]); + strncpy ((char*)p->b[2], s, sizeof p->b[2]); + strncpy ((char*)p->c[2], s, sizeof p->c[2]); +} + +struct ConstPointers +{ + const char NONSTR (*p)[4]; + const signed char NONSTR (*q)[4]; + const unsigned char NONSTR (*r)[4]; +}; + +void test_const_pointers (struct ConstPointers *p) +{ + strncpy ((char*)*p->p, S, sizeof S - 1); + strncpy ((char*)*p->q, S, sizeof S - 1); + strncpy ((char*)*p->r, S, sizeof S - 1); +} + +struct VolatileArrays +{ + volatile char NONSTR a[4][4]; + volatile signed char NONSTR b[4][4]; + volatile unsigned char NONSTR c[4][4]; +}; + +void test_volatile_arrays (struct VolatileArrays *p, const char *s) +{ + strncpy ((char*)p->a[2], s, sizeof p->a[2]); + strncpy ((char*)p->b[2], s, sizeof p->b[2]); + strncpy ((char*)p->c[2], s, sizeof p->c[2]); +} + +struct VolatilePointers +{ + volatile char NONSTR (*p)[4]; + volatile signed char NONSTR (*q)[4]; + volatile unsigned char NONSTR (*r)[4]; +}; + +void test_volatile_pointers (struct VolatilePointers *p) +{ + strncpy ((char*)*p->p, S, sizeof S - 1); + strncpy ((char*)*p->q, S, sizeof S - 1); + strncpy ((char*)*p->r, S, sizeof S - 1); +} + +struct ConstVolatileArrays +{ + const volatile char NONSTR a[4][4]; + const volatile signed char NONSTR b[4][4]; + const volatile unsigned char NONSTR c[4][4]; +}; + +void test_const_volatile_arrays (struct ConstVolatileArrays *p, const char *s) +{ + strncpy ((char*)p->a[2], s, sizeof p->a[2]); + strncpy ((char*)p->b[2], s, sizeof p->b[2]); + strncpy ((char*)p->c[2], s, sizeof p->c[2]); +} + +struct ConstVolatilePointers +{ + const volatile char NONSTR (*p)[4]; + const volatile signed char NONSTR (*q)[4]; + const volatile unsigned char NONSTR (*r)[4]; +}; + +void test_const_volatile_pointers (struct ConstVolatilePointers *p) +{ + strncpy ((char*)*p->p, S, sizeof S - 1); + strncpy ((char*)*p->q, S, sizeof S - 1); + strncpy ((char*)*p->r, S, sizeof S - 1); +} + +/* { dg-prune-output "-Wdiscarded-qualifiers" } */ diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-14.c b/gcc/testsuite/c-c++-common/attr-nonstring-14.c new file mode 100644 index 00000000000..9b020a699f2 --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-nonstring-14.c @@ -0,0 +1,184 @@ +/* PR 85623 - strncmp() warns about attribute 'nonstring' incorrectly + in -Wstringop-overflow + { dg-do compile } + { dg-options "-O2 -Wstringop-overread -ftrack-macro-expansion=0" } */ + +#include "../gcc.dg/range.h" + +#if __cplusplus +extern "C" { +#endif + +extern int strcmp (const char*, const char*); +extern int strncmp (const char*, const char*, size_t); +extern int strncasecmp (const char*, const char*, size_t); + +extern size_t strspn (const char*, const char*); +extern size_t strcspn (const char*, const char*); + +#if __cplusplus +} +#endif + +#define S26 "0123456789abcdefghijklmnopqrstuvwxyz" +#define S(n) (S26 + sizeof S26 - 1 - (n)) + +char __attribute__ ((nonstring)) a3[3][3]; + +void sink (int); + +#define T(call) sink (call) + +void test_strcmp_cst (void) +{ + /* Verify that no warning is issued for strcmp() calls with a non-string + array argument when the other argument is a string whose length is + less than the size of the array. Because the function stops reading + at the first nul character there is no chance that it will read past + the end of the array. */ + T (strcmp (S (0), a3[2])); + T (strcmp (S (1), a3[2])); + T (strcmp (S (2), a3[2])); + /* The following reads a3[2][3]. */ + T (strcmp (S (3), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */ + /* The following also reads past the end of a3[2]. */ + T (strcmp (S (9), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */ + + T (strcmp (a3[2], S (0))); + T (strcmp (a3[2], S (1))); + T (strcmp (a3[2], S (2))); + T (strcmp (a3[2], S (3))); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strcmp (a3[2], S (9))); /* { dg-warning "\\\[-Wstringop-overread" } */ +} + + +void test_strcmp_range (const char *s) +{ + s = signed_value () < 0 ? S (0) : S (1); + T (strcmp (a3[2], s)); + + s = signed_value () < 0 ? S (0) : S (2); + T (strcmp (a3[2], s)); + + s = signed_value () < 0 ? S (0) : S (3); + T (strcmp (a3[2], s)); /* { dg-warning "\\\[-Wstringop-overread" } */ + + s = signed_value () < 0 ? S (1) : S (2); + T (strcmp (a3[2], s)); + + s = signed_value () < 0 ? S (1) : S (3); + T (strcmp (a3[2], s)); /* { dg-warning "\\\[-Wstringop-overread" } */ + + s = signed_value () < 0 ? S (3) : S (4); + T (strcmp (a3[2], s)); /* { dg-warning "\\\[-Wstringop-overread" } */ +} + + +void test_strncmp_cst (void) +{ + T (strncmp (S (0), a3[2], 1)); + T (strncmp (S (1), a3[2], 2)); + T (strncmp (S (2), a3[2], 3)); + T (strncmp (S (3), a3[2], 3)); + T (strncmp (S (3), a3[2], 4)); /* { dg-warning "\\\[-Wstringop-overread" } */ + + T (strncmp (S (9), a3[2], 3)); + T (strncmp (S (9), a3[2], 4)); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strncmp (S (9), a3[2], 5)); /* { dg-warning "\\\[-Wstringop-overread" } */ + + T (strncmp (a3[2], S (0), 1)); + T (strncmp (a3[2], S (1), 2)); + T (strncmp (a3[2], S (2), 3)); + T (strncmp (a3[2], S (3), 3)); + T (strncmp (a3[2], S (3), 4)); /* { dg-warning "\\\[-Wstringop-overread" } */ + + T (strncmp (a3[2], S (9), 3)); + T (strncmp (a3[2], S (9), 4)); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strncmp (a3[2], S (9), 5)); /* { dg-warning "\\\[-Wstringop-overread" } */ +} + +void test_strncmp_range (const char *s) +{ + T (strncmp (a3[2], S (2), UR (0, 3))); + T (strncmp (a3[2], S (2), UR (1, 4))); + T (strncmp (a3[2], S (2), UR (2, 5))); + T (strncmp (a3[2], S (2), UR (3, 6))); + T (strncmp (a3[2], S (2), UR (4, 7))); + + T (strncmp (a3[2], S (5), UR (0, 3))); + T (strncmp (a3[2], S (5), UR (1, 4))); + T (strncmp (a3[2], S (5), UR (2, 5))); + T (strncmp (a3[2], S (5), UR (3, 6))); + T (strncmp (a3[2], S (5), UR (4, 7))); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strncmp (a3[2], S (5), UR (7, 9))); /* { dg-warning "\\\[-Wstringop-overread" } */ + + s = signed_value () < 0 ? S (0) : S (1); + T (strncmp (a3[2], s, UR (1, 3))); + T (strncmp (a3[2], s, UR (2, 5))); + + s = signed_value () < 0 ? S (2) : S (5); + T (strncmp (a3[2], s, UR (1, 3))); + + s = signed_value () < 0 ? S (2) : S (5); + T (strncmp (a3[2], s, UR (1, 4))); + T (strncmp (a3[2], s, UR (2, 5))); + T (strncmp (a3[2], s, UR (3, 6))); + T (strncmp (a3[2], s, UR (4, 7))); /* { dg-warning "\\\[-Wstringop-overread" } */ +} + +void test_strncasecmp (void) +{ + T (strncasecmp (S (0), a3[2], 1)); + T (strncasecmp (S (1), a3[2], 2)); + T (strncasecmp (S (2), a3[2], 3)); + T (strncasecmp (S (3), a3[2], 3)); + T (strncasecmp (S (3), a3[2], 4)); /* { dg-warning "\\\[-Wstringop-overread" } */ + + T (strncasecmp (S (9), a3[2], 3)); + T (strncasecmp (S (9), a3[2], 4)); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strncasecmp (S (9), a3[2], 5)); /* { dg-warning "\\\[-Wstringop-overread" } */ + + T (strncasecmp (a3[2], S (0), 1)); + T (strncasecmp (a3[2], S (1), 2)); + T (strncasecmp (a3[2], S (2), 3)); + T (strncasecmp (a3[2], S (3), 3)); + T (strncasecmp (a3[2], S (3), 4)); /* { dg-warning "\\\[-Wstringop-overread" } */ + + T (strncasecmp (a3[2], S (9), 3)); + T (strncasecmp (a3[2], S (9), 4)); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strncasecmp (a3[2], S (9), 5)); /* { dg-warning "\\\[-Wstringop-overread" } */ +} + +void test_strspn (void) +{ + /* strspn must traverse all characters in the second argument except + when the first string is empty. */ + T (strspn (S (0), a3[2])); + T (strspn (S (1), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strspn (S (2), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strspn (S (3), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strspn (S (9), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */ + + /* Similarly, strspn must traverse all characters in the first argument + except when the second string is empty. */ + T (strspn (a3[2], S (0))); + T (strspn (a3[2], S (1))); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strspn (a3[2], S (2))); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strspn (a3[2], S (3))); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strspn (a3[2], S (9))); /* { dg-warning "\\\[-Wstringop-overread" } */ +} + +void test_strcspn (void) +{ + T (strcspn (S (0), a3[2])); + T (strcspn (S (1), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strcspn (S (2), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strcspn (S (3), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strcspn (S (9), a3[2])); /* { dg-warning "\\\[-Wstringop-overread" } */ + + T (strcspn (a3[2], S (0))); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strcspn (a3[2], S (1))); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strcspn (a3[2], S (2))); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strcspn (a3[2], S (3))); /* { dg-warning "\\\[-Wstringop-overread" } */ + T (strcspn (a3[2], S (9))); /* { dg-warning "\\\[-Wstringop-overread" } */ +} diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-15.c b/gcc/testsuite/c-c++-common/attr-nonstring-15.c new file mode 100644 index 00000000000..9f536a414cf --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-nonstring-15.c @@ -0,0 +1,90 @@ +/* PR 85643 - attribute nonstring fails to squash -Wstringop-truncation + warning + { dg-do compile } + { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */ + +#define strncpy __builtin_strncpy + +struct A { + char a[2][16 + 1]; +}; + +struct B { + char a[2][16] __attribute__ ((__nonstring__)); +}; + +struct B* +test_memarray (const struct A *s) +{ + static struct B b; + strncpy (b.a[1], s->a[1], sizeof b.a[1]); + return &b; +} + +const char* +test_array (const char *s) +{ + static char a[2][80] __attribute__ ((__nonstring__)); + strncpy (a[1], s, sizeof a[1]); + return a[1]; +} + +const char* +test_array_idx (const char *s) +{ + static char a[2][80] __attribute__ ((__nonstring__)); + char *p __attribute__ ((__nonstring__)) = &a[1][20]; + strncpy (p, s, 60); /* { dg-bogus "-Wstringop-truncation" } */ + return a[1]; +} + +const char* +test_array_off (const char *s) +{ + static char a[2][80] __attribute__ ((__nonstring__)); + char *p __attribute__ ((__nonstring__)) = a[1] + 20; + strncpy (p, s, 60); /* { dg-bogus "-Wstringop-truncation" } */ + return a[1]; +} + +struct B* +test_memarray_cstidx_idx (const char *s) +{ + static struct B b[2]; + char *p __attribute__ ((__nonstring__)) = &b[1].a[1][4]; + + /* The destination below is represented as &MEM[(void *)&a + 20B] and + which (in general) doesn't make it possible to determine what member + it refers to. */ + strncpy (p, s, sizeof b[1].a[1] - 4); /* { dg-bogus "-Wstringop-truncation" } */ + return b; +} + +struct B* +test_memarray_cstidx_off (const char *s) +{ + static struct B b[2]; + char *p __attribute__ ((__nonstring__)) = b[1].a[1] + 4; + + /* Same as above. */ + strncpy (p, s, sizeof b[1].a[1] - 4); /* { dg-bogus "-Wstringop-truncation" "" { xfail *-*-*} } */ + return b; +} + +struct B* +test_memarray_varidx_idx (const char *s, int i) +{ + static struct B b[3]; + char *p __attribute__ ((__nonstring__)) = &b[i].a[1][4]; + strncpy (p, s, sizeof b[i].a[1] - 4); + return b; +} + +struct B* +test_memarray_varidx_off (const char *s, int i) +{ + static struct B b[3]; + char *p __attribute__ ((__nonstring__)) = b[i].a[1] + 4; + strncpy (p, s, sizeof b[i].a[1] - 4); + return b; +} diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-16.c b/gcc/testsuite/c-c++-common/attr-nonstring-16.c new file mode 100644 index 00000000000..d89e78cd265 --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-nonstring-16.c @@ -0,0 +1,147 @@ +/* PR middle-end/85602 - -Wsizeof-pointer-memaccess for strncat with size + of source + { dg-do compile } + { dg-options "-O2 -Wno-array-bounds -Wsizeof-pointer-memaccess -Wstringop-truncation -ftrack-macro-expansion=0" } */ + +#include "../gcc.dg/range.h" + +typedef __SIZE_TYPE__ size_t; + +#if __cplusplus +extern "C" { +#endif + +char* strcpy (char*, const char*); +size_t strlen (const char*); +char* strncat (char*, const char*, __SIZE_TYPE__); +char* strncpy (char*, const char*, __SIZE_TYPE__); + +#if __cplusplus +} +#endif + +#define NONSTR __attribute__ ((nonstring)) + +NONSTR char nd3[3][3]; +NONSTR char nd4[4][4]; +NONSTR char nd5[5][5]; + +NONSTR char ns3[3][3]; +NONSTR char ns4[4][4]; +NONSTR char ns5[5][5]; + +NONSTR char* pns; + +void sink (void*, ...); + +#define T(call) sink (call) + +/* Verify that for a nonstring source array of an unknown length + a warning is issued only when the bound exceeds the array size. */ + +void test_strncat_nonstring_cst (char *d) +{ + T (strncat (d, ns3[1], 1)); + T (strncat (d, ns3[1], 2)); + T (strncat (d, ns3[1], 3)); + T (strncat (d, ns3[1], sizeof ns3[2])); + T (strncat (d, ns3[1], 4)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 4" } */ + + T (strncat (d, ns4[1], 1)); + T (strncat (d, ns4[1], 2)); + T (strncat (d, ns4[1], 3)); + T (strncat (d, ns4[1], 4)); + T (strncat (d, ns4[1], sizeof ns4[2])); + T (strncat (d, ns4[1], 5)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */ + + T (strncat (nd3[1], ns3[1], 1)); + T (strncat (nd3[1], ns3[1], 2)); + T (strncat (nd3[1], ns3[1], 3)); /* { dg-warning "specified bound 3 equals destination size" } */ + /* Either of the two warnings below is fine. */ + T (strncat (nd3[1], ns3[1], 4)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 4|specified bound 4 exceeds destination size 3" } */ + + T (strncat (d, pns, sizeof pns)); /* { dg-warning "argument to .sizeof. in .\[^\n\r\]*strncat\[^\n\r\]*. call is the same expression as the source" } */ +} + + +void test_strncat_nonstring_var (char *d, size_t n) +{ + /* In the following the bound coulld apply to either the destination + or the source. The expected use of strncat() is to pass it as + the bound DSIZE - strlen(D) - 1 so the call below is diagnosed. */ + T (strncat (d, ns3[1], n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + + T (strncat (d, ns3[1], UR (0, 1))); + T (strncat (d, ns3[1], UR (1, 2))); + T (strncat (d, ns3[1], UR (2, 3))); + T (strncat (d, ns3[1], UR (3, 4))); /* { dg-warning "argument 2 declared attribute 'nonstring' may be smaller than the specified bound \\\[3, 4]" } */ + T (strncat (d, ns3[1], UR (4, 5))); /* { dg-warning "argument 2 declared attribute 'nonstring' is smaller than the specified bound \\\[4, 5]" } */ + + /* Verify that the call below (the intended use of strncat()) is + also diagnosed. */ + T (strncat (d, ns3[1], 256 - strlen (d) - 1)); /* { dg-warning "argument 2 declared attribute .nonstring." } */ + + T (strncat (nd3[1], ns5[1], UR (0, 1))); + T (strncat (nd3[1], ns5[1], UR (1, 2))); + T (strncat (nd3[1], ns5[1], UR (2, 3))); + T (strncat (nd3[1], ns5[1], UR (3, 4))); + T (strncat (nd3[1], ns5[1], UR (4, 5))); /* { dg-warning "specified bound \\\[4, 5] exceeds destination size 3" } */ + + T (strncat (nd5[1], ns3[1], UR (0, 1))); + T (strncat (nd5[1], ns3[1], UR (1, 2))); + T (strncat (nd5[1], ns3[1], UR (2, 3))); + T (strncat (nd5[1], ns3[1], UR (3, 4))); /* { dg-warning "argument 2 declared attribute 'nonstring' may be smaller than the specified bound \\\[3, 4]" } */ +} + +/* Verify that for a nonstring source array of a known length (i.e., + a nonstring array containing a nul-terminated string) a warning + is issued only for certain truncation. + + The test cases are split up to work around bug 81343 (or one like + it). */ + +void test_strncat_string_1_1 (char *d) +{ + strcpy (ns3[1], "1"); + T (strncat (d, ns3[1], 1)); /* { dg-warning "output truncated before terminating nul copying 1 byte from a string of the same length" } */ +} + +void test_strncat_string_1_2 (char *d) +{ + strcpy (ns3[1], "1"); + T (strncat (d, ns3[1], 2)); +} + +void test_strncat_string_1_3 (char *d) +{ + strcpy (ns3[1], "1"); + T (strncat (d, ns3[1], 3)); +} + +void test_strncat_string_2_1 (char *d) +{ + strcpy (ns3[1], "12"); + T (strncat (d, ns3[1], 1)); /* { dg-warning "output truncated copying 1 byte from a string of length 2" } */ +} + +void test_strncat_string_2_2 (char *d) +{ + strcpy (ns3[1], "12"); + T (strncat (d, ns3[1], 2)); /* { dg-warning "output truncated before terminating nul copying 2 bytes from a string of the same length" } */ +} + +void test_strncat_string_2_3 (char *d) +{ + strcpy (ns3[1], "12"); + T (strncat (d, ns3[1], 3)); +} + + +void test_strcncpy_nonstring_cst (char *d) +{ + T (strncpy (d, ns3[1], 1)); + T (strncpy (d, ns3[1], 2)); + T (strncpy (d, ns3[1], 3)); + T (strncpy (d, ns3[1], sizeof ns3[2])); + T (strncpy (d, ns3[1], 4)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 4" } */ +} diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-7.c b/gcc/testsuite/c-c++-common/attr-nonstring-7.c index a32cbfedfa5..09385a61209 100644 --- a/gcc/testsuite/c-c++-common/attr-nonstring-7.c +++ b/gcc/testsuite/c-c++-common/attr-nonstring-7.c @@ -56,7 +56,7 @@ test_memarray_cstidx_idx (const char *s) /* The destination below is represented as &MEM[(void *)&a + 20B] and which (in general) doesn't make it possible to determine what member it refers to. */ - strncpy (p, s, sizeof b[1].a - 4); /* { dg-bogus "-Wstringop-truncation" "" { xfail *-*-*} } */ + strncpy (p, s, sizeof b[1].a - 4); /* { dg-bogus "-Wstringop-truncation" } */ return b; } diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-9.c b/gcc/testsuite/c-c++-common/attr-nonstring-9.c new file mode 100644 index 00000000000..4fb1e5d69bb --- /dev/null +++ b/gcc/testsuite/c-c++-common/attr-nonstring-9.c @@ -0,0 +1,51 @@ +/* Test to exercise attribute "nonstring" syntax. + { dg-do compile } + { dg-options "-Wattributes" } */ + +#define ATTR(list) __attribute__ (list) +#define NONSTR ATTR ((nonstring)) + +/* Verify it's accepted on char[] arrays. */ +extern NONSTR char nsx_1[][3]; +extern char NONSTR nsx_2[][3]; +extern char nsx_3[][3] NONSTR; + +extern NONSTR char ns1[1][4]; +extern char NONSTR ns2[3][5]; +extern char ns3[5][6] NONSTR; + +extern NONSTR char ns4[1][2][3][4]; +extern char NONSTR ns5[2][3][4][5][6]; +extern char ns6[1][2][3][1][2][3][1][2][3] NONSTR; + +/* Verify it's accepted on char[] pointers. */ +extern NONSTR char (*pns_1)[3]; +extern char NONSTR (*pns_2)[4]; +extern char (*NONSTR pns_3)[5]; + +extern NONSTR char (*pns_4)[1][2]; +extern char NONSTR (*pns_5)[2][3][1][7][4]; +extern char (*NONSTR pns_6)[1][1][1][2][1][1][1][2][1][1][1][2][1][1][7]; + +struct S +{ +/* Verify it's accepted on char[] member pointers. */ + NONSTR char (*mpns_1)[3]; + char NONSTR (*mpns_2)[4]; + char (*NONSTR mpns_3)[5]; + +/* Verify it's accepted on char[] member arrays. */ + NONSTR char mns1[1][2]; + char NONSTR mns3[3][3]; + char mns5[5][4] NONSTR; + +/* Verify it's accepted on char[] flexible array members. */ + char mnsx[][5] NONSTR; +}; + +void func (NONSTR char (*pns1)[2], char NONSTR (*pns2)[3], char (* NONSTR pns3)[4]) +{ + (void)pns1; + (void)pns2; + (void)pns3; +} diff --git a/gcc/tree.cc b/gcc/tree.cc index 4e855b4b2d3..a35b4dbe655 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -15318,15 +15318,17 @@ get_attr_nonstring_decl (tree expr, tree *ref) DECL. */ if (var) decl = var; - else if (TREE_CODE (decl) == ARRAY_REF) - decl = TREE_OPERAND (decl, 0); - else if (TREE_CODE (decl) == COMPONENT_REF) - decl = TREE_OPERAND (decl, 1); - else if (TREE_CODE (decl) == MEM_REF) - return get_attr_nonstring_decl (TREE_OPERAND (decl, 0), ref); + else + { + while (TREE_CODE (decl) == ARRAY_REF) + decl = TREE_OPERAND (decl, 0); + if (TREE_CODE (decl) == COMPONENT_REF) + decl = TREE_OPERAND (decl, 1); + else if (TREE_CODE (decl) == MEM_REF) + return get_attr_nonstring_decl (TREE_OPERAND (decl, 0), ref); + } - if (DECL_P (decl) - && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl))) + if (DECL_P (decl) && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl))) return decl; return NULL_TREE;