Allow _Alignas in compound literals (C11 DR#444).
authorJoseph Myers <joseph@codesourcery.com>
Thu, 7 Dec 2017 18:47:20 +0000 (18:47 +0000)
committerJoseph Myers <jsm28@gcc.gnu.org>
Thu, 7 Dec 2017 18:47:20 +0000 (18:47 +0000)
C11 DR#444 dealt with how C11 intended to allow alignment specifiers
on struct and union members, but failed to include that in the syntax.
The final resolution of that DR also allows alignment specifiers in
type names in compound literals (in order to apply an increased
alignment to the unnamed object created by the compound literal), but
not other cases of type names.

This patch implements allowing alignment specifiers in compound
literals and adds tests for the resolution of the DR (including that
they are allowed on struct and union members, which GCC already
implemented).  Because the parser has to parse the parenthesized type
name of a compound literal before it can tell that it's a compound
literal (rather than, depending on the context, a cast expression or
sizeof (type-name) or _Alignof (type-name)), this means _Alignas
specifiers are allowed syntactically in those contexts and then an
error is given once it's known to be an invalid use (whereas _Alignas
specifiers are disallowed syntactically in other contexts where type
names can occur and a compound literal is not possible).

Bootstrapped with no regressions on x86_64-pc-linux-gnu.

gcc/c:
* c-decl.c (build_compound_literal): Add parameter alignas_align
and set alignment of decl if nonzero.
* c-parser.c (c_keyword_starts_typename): Allow RID_ALIGNAS.
(c_parser_declspecs): Allow RID_ALIGNAS to follow a type, like a
qualifier.
(c_parser_struct_declaration): Update syntax comment.
(c_parser_type_name): Add alignas_ok argument and pass it to
c_parser_declspecs.
(c_parser_cast_expression): Pass true to c_parser_type_name and
give error if a cast used an _Alignas specifier.
(c_parser_sizeof_expression): Pass true to c_parser_type_name and
give error if sizeof (type-name) used an _Alignas specifier.
(c_parser_alignof_expression): Pass true to c_parser_type_name and
give error if _Alignof (type-name) used an _Alignas specifier.
(c_parser_postfix_expression_after_paren_type): Check specified
alignment for a compound literal and pass it to
build_compound_literal.
* c-parser.h (c_parser_type_name): Update prototype.
* c-tree.h (build_compound_literal): Update prototype.

gcc/testsuite:
* gcc.dg/c11-align-7.c, gcc.dg/c11-align-8.c,
gcc.dg/c11-align-9.c, gcc.dg/gnu11-align-1.c: New tests.
* gcc.dg/c11-align-5.c (test): Update expected error for sizeof
case.

From-SVN: r255482

gcc/c/ChangeLog
gcc/c/c-decl.c
gcc/c/c-parser.c
gcc/c/c-parser.h
gcc/c/c-tree.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/c11-align-5.c
gcc/testsuite/gcc.dg/c11-align-7.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c11-align-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c11-align-9.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu11-align-1.c [new file with mode: 0644]

index 6e11827f39ccd154d91aaffa01dc1737ad81c9a1..14d7f63bf4e3fe3bdd3c4c2274f661ae6f1d4c88 100644 (file)
@@ -1,3 +1,25 @@
+2017-12-07  Joseph Myers  <joseph@codesourcery.com>
+
+       * c-decl.c (build_compound_literal): Add parameter alignas_align
+       and set alignment of decl if nonzero.
+       * c-parser.c (c_keyword_starts_typename): Allow RID_ALIGNAS.
+       (c_parser_declspecs): Allow RID_ALIGNAS to follow a type, like a
+       qualifier.
+       (c_parser_struct_declaration): Update syntax comment.
+       (c_parser_type_name): Add alignas_ok argument and pass it to
+       c_parser_declspecs.
+       (c_parser_cast_expression): Pass true to c_parser_type_name and
+       give error if a cast used an _Alignas specifier.
+       (c_parser_sizeof_expression): Pass true to c_parser_type_name and
+       give error if sizeof (type-name) used an _Alignas specifier.
+       (c_parser_alignof_expression): Pass true to c_parser_type_name and
+       give error if _Alignof (type-name) used an _Alignas specifier.
+       (c_parser_postfix_expression_after_paren_type): Check specified
+       alignment for a compound literal and pass it to
+       build_compound_literal.
+       * c-parser.h (c_parser_type_name): Update prototype.
+       * c-tree.h (build_compound_literal): Update prototype.
+
 2017-12-07  Martin Sebor  <msebor@redhat.com>
 
        PR c/81544
index aaa967874727613e3fc955627ab18829340a53c6..4a36c54d21e91440f2b324b1167872d52de0b28c 100644 (file)
@@ -5272,10 +5272,13 @@ mark_forward_parm_decls (void)
    literal, which may be an incomplete array type completed by the
    initializer; INIT is a CONSTRUCTOR at LOC that initializes the compound
    literal.  NON_CONST is true if the initializers contain something
-   that cannot occur in a constant expression.  */
+   that cannot occur in a constant expression.  If ALIGNAS_ALIGN is nonzero,
+   it is the (valid) alignment for this compound literal, as specified
+   with _Alignas.  */
 
 tree
-build_compound_literal (location_t loc, tree type, tree init, bool non_const)
+build_compound_literal (location_t loc, tree type, tree init, bool non_const,
+                       unsigned int alignas_align)
 {
   /* We do not use start_decl here because we have a type, not a declarator;
      and do not use finish_decl because the decl should be stored inside
@@ -5299,6 +5302,11 @@ build_compound_literal (location_t loc, tree type, tree init, bool non_const)
   DECL_IGNORED_P (decl) = 1;
   TREE_TYPE (decl) = type;
   c_apply_type_quals_to_decl (TYPE_QUALS (strip_array_types (type)), decl);
+  if (alignas_align)
+    {
+      SET_DECL_ALIGN (decl, alignas_align * BITS_PER_UNIT);
+      DECL_USER_ALIGN (decl) = 1;
+    }
   store_init_value (loc, decl, init, NULL_TREE);
 
   if (TREE_CODE (type) == ARRAY_TYPE && !COMPLETE_TYPE_P (type))
index e9267fe9cc107e84990235ed16a004cd022f0997..d39854805048166d34d4f3293b7db345e0b31ffe 100644 (file)
@@ -504,6 +504,7 @@ c_keyword_starts_typename (enum rid keyword)
     case RID_ACCUM:
     case RID_SAT:
     case RID_AUTO_TYPE:
+    case RID_ALIGNAS:
       return true;
     default:
       if (keyword >= RID_FIRST_INT_N
@@ -2594,7 +2595,8 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
         has simply forgotten a semicolon, so we exit.  */
       if ((!typespec_ok || specs->typespec_kind == ctsk_tagdef)
          && c_parser_next_tokens_start_typename (parser, la)
-         && !c_parser_next_token_is_qualifier (parser))
+         && !c_parser_next_token_is_qualifier (parser)
+         && !c_parser_next_token_is_keyword (parser, RID_ALIGNAS))
        break;
 
       if (c_parser_next_token_is (parser, CPP_NAME))
@@ -3225,6 +3227,7 @@ c_parser_struct_or_union_specifier (c_parser *parser)
    specifier-qualifier-list:
      type-specifier specifier-qualifier-list[opt]
      type-qualifier specifier-qualifier-list[opt]
+     alignment-specifier specifier-qualifier-list[opt]
      attributes specifier-qualifier-list[opt]
 
    struct-declarator-list:
@@ -4410,20 +4413,22 @@ c_parser_attributes (c_parser *parser)
   return attrs;
 }
 
-/* Parse a type name (C90 6.5.5, C99 6.7.6, C11 6.7.7).
+/* Parse a type name (C90 6.5.5, C99 6.7.6, C11 6.7.7).  ALIGNAS_OK
+   says whether alignment specifiers are OK (only in cases that might
+   be the type name of a compound literal).
 
    type-name:
      specifier-qualifier-list abstract-declarator[opt]
 */
 
 struct c_type_name *
-c_parser_type_name (c_parser *parser)
+c_parser_type_name (c_parser *parser, bool alignas_ok)
 {
   struct c_declspecs *specs = build_null_declspecs ();
   struct c_declarator *declarator;
   struct c_type_name *ret;
   bool dummy = false;
-  c_parser_declspecs (parser, specs, false, true, true, false, false,
+  c_parser_declspecs (parser, specs, false, true, true, alignas_ok, false,
                      cla_prefer_type);
   if (!specs->declspecs_seen_p)
     {
@@ -7019,7 +7024,7 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after)
       struct c_expr expr;
       matching_parens parens;
       parens.consume_open (parser);
-      type_name = c_parser_type_name (parser);
+      type_name = c_parser_type_name (parser, true);
       parens.skip_until_found_close (parser);
       if (type_name == NULL)
        {
@@ -7035,6 +7040,9 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after)
       if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
        return c_parser_postfix_expression_after_paren_type (parser, type_name,
                                                             cast_loc);
+      if (type_name->specs->alignas_p)
+       error_at (type_name->specs->locations[cdw_alignas],
+                 "alignment specified for type name in cast");
       {
        location_t expr_loc = c_parser_peek_token (parser)->location;
        expr = c_parser_cast_expression (parser, NULL);
@@ -7238,7 +7246,7 @@ c_parser_sizeof_expression (c_parser *parser)
       matching_parens parens;
       parens.consume_open (parser);
       expr_loc = c_parser_peek_token (parser)->location;
-      type_name = c_parser_type_name (parser);
+      type_name = c_parser_type_name (parser, true);
       parens.skip_until_found_close (parser);
       finish = parser->tokens_buf[0].location;
       if (type_name == NULL)
@@ -7260,6 +7268,9 @@ c_parser_sizeof_expression (c_parser *parser)
          goto sizeof_expr;
        }
       /* sizeof ( type-name ).  */
+      if (type_name->specs->alignas_p)
+       error_at (type_name->specs->locations[cdw_alignas],
+                 "alignment specified for type name in %<sizeof%>");
       c_inhibit_evaluation_warnings--;
       in_sizeof--;
       result = c_expr_sizeof_type (expr_loc, type_name);
@@ -7321,7 +7332,7 @@ c_parser_alignof_expression (c_parser *parser)
       matching_parens parens;
       parens.consume_open (parser);
       loc = c_parser_peek_token (parser)->location;
-      type_name = c_parser_type_name (parser);
+      type_name = c_parser_type_name (parser, true);
       end_loc = c_parser_peek_token (parser)->location;
       parens.skip_until_found_close (parser);
       if (type_name == NULL)
@@ -7342,6 +7353,10 @@ c_parser_alignof_expression (c_parser *parser)
          goto alignof_expr;
        }
       /* alignof ( type-name ).  */
+      if (type_name->specs->alignas_p)
+       error_at (type_name->specs->locations[cdw_alignas],
+                 "alignment specified for type name in %qE",
+                 alignof_spelling);
       c_inhibit_evaluation_warnings--;
       in_alignof--;
       ret.value = c_sizeof_or_alignof_type (loc, groktypename (type_name,
@@ -8969,7 +8984,21 @@ c_parser_postfix_expression_after_paren_type (c_parser *parser,
               ? CONSTRUCTOR_NON_CONST (init.value)
               : init.original_code == C_MAYBE_CONST_EXPR);
   non_const |= !type_expr_const;
-  expr.value = build_compound_literal (start_loc, type, init.value, non_const);
+  unsigned int alignas_align = 0;
+  if (type != error_mark_node
+      && type_name->specs->align_log != -1)
+    {
+      alignas_align = 1U << type_name->specs->align_log;
+      if (alignas_align < min_align_of_type (type))
+       {
+         error_at (type_name->specs->locations[cdw_alignas],
+                   "%<_Alignas%> specifiers cannot reduce "
+                   "alignment of compound literal");
+         alignas_align = 0;
+       }
+    }
+  expr.value = build_compound_literal (start_loc, type, init.value, non_const,
+                                      alignas_align);
   set_c_expr_source_range (&expr, init.src_range);
   expr.original_code = ERROR_MARK;
   expr.original_type = NULL;
index 21e40541ce60ecfa9061dd508624a808bde6b738..ff8cdda69174650a078d6584e365b54466678e0e 100644 (file)
@@ -187,6 +187,6 @@ c_parser_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
                     bool *seen_id);
 extern void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool,
                                bool, bool, bool, enum c_lookahead_kind);
-extern struct c_type_name *c_parser_type_name (c_parser *);
+extern struct c_type_name *c_parser_type_name (c_parser *, bool = false);
 
 #endif
index cbc5e0e8bccc1f450cdb36a36dddfaf57df539ef..c542d682f3eee7e96884619fcba1a811e7fa3cac 100644 (file)
@@ -672,7 +672,8 @@ extern void set_init_index (location_t, tree, tree, struct obstack *);
 extern void set_init_label (location_t, tree, location_t, struct obstack *);
 extern void process_init_element (location_t, struct c_expr, bool,
                                  struct obstack *);
-extern tree build_compound_literal (location_t, tree, tree, bool);
+extern tree build_compound_literal (location_t, tree, tree, bool,
+                                   unsigned int);
 extern void check_compound_literal_type (location_t, struct c_type_name *);
 extern tree c_start_case (location_t, location_t, tree, bool);
 extern void c_finish_case (tree, tree);
index 3a22f217a174c5e70638d4b78c4f9f05cbc646a8..dab24f424388e8e06e5c3aea7b0351c6ed438afb 100644 (file)
@@ -1,3 +1,10 @@
+2017-12-07  Joseph Myers  <joseph@codesourcery.com>
+
+       * gcc.dg/c11-align-7.c, gcc.dg/c11-align-8.c,
+       gcc.dg/c11-align-9.c, gcc.dg/gnu11-align-1.c: New tests.
+       * gcc.dg/c11-align-5.c (test): Update expected error for sizeof
+       case.
+
 2017-12-07  Richard Sandiford  <richard.sandiford@linaro.org>
 
        * gcc.target/aarch64/asm-2.c: New test.
index f3a14779ff8f0be0a8d04611998f6db0c6d99ed9..08ec65a8b9fbfb84ad363473806533dd4905721d 100644 (file)
@@ -19,7 +19,7 @@ test (void)
   struct s { int n; };
   __builtin_offsetof (struct s _Alignas (int), n); /* { dg-error "expected" } */
   __typeof (long double _Alignas (0)) e; /* { dg-error "expected" } */
-  sizeof (int _Alignas (int)); /* { dg-error "expected" } */
+  sizeof (int _Alignas (int)); /* { dg-error "specified for type name" } */
   _Alignas (int _Alignas (float)) int t; /* { dg-error "expected" } */
   __builtin_types_compatible_p (signed _Alignas (0), unsigned); /* { dg-error "expected" } */
   int a[_Alignas (int) 10]; /* { dg-error "expected expression before" } */
diff --git a/gcc/testsuite/gcc.dg/c11-align-7.c b/gcc/testsuite/gcc.dg/c11-align-7.c
new file mode 100644 (file)
index 0000000..631986a
--- /dev/null
@@ -0,0 +1,20 @@
+/* Test C11 alignment support.  Test code valid after the resolution
+   of DR#444: alignment specifiers for struct and union members and
+   compound literals.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+#include <stddef.h>
+
+struct s
+{
+  _Alignas (_Alignof (max_align_t)) char c;
+};
+
+union u
+{
+  _Alignas (_Alignof (max_align_t)) char c;
+};
+
+char *p = &(_Alignas (_Alignof (max_align_t)) char) { 1 };
+size_t size = sizeof (_Alignas (_Alignof (max_align_t)) char) { 1 };
diff --git a/gcc/testsuite/gcc.dg/c11-align-8.c b/gcc/testsuite/gcc.dg/c11-align-8.c
new file mode 100644 (file)
index 0000000..a201674
--- /dev/null
@@ -0,0 +1,18 @@
+/* Test C11 alignment support.  Test invalid use of alignment
+   specifiers in type names in cases not permitted by the resolution
+   of DR#444.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+
+#include <stddef.h>
+
+void
+f (void)
+{
+  _Generic (1, int: 1, _Alignas (8) long: 2); /* { dg-error "expected" } */
+  sizeof (_Alignas (8) long); /* { dg-error "specified for type name" } */
+  _Alignof (_Alignas (8) long); /* { dg-error "specified for type name" } */
+  (_Alignas (8) long) 0; /* { dg-error "specified for type name" } */
+  _Atomic (_Alignas (8) long) x; /* { dg-error "expected" } */
+  _Alignas (_Alignas (8) long) long y; /* { dg-error "expected" } */
+}
diff --git a/gcc/testsuite/gcc.dg/c11-align-9.c b/gcc/testsuite/gcc.dg/c11-align-9.c
new file mode 100644 (file)
index 0000000..3c9cf55
--- /dev/null
@@ -0,0 +1,9 @@
+/* Test C11 alignment support.  Test reducing alignment (assumes there
+   are at least some alignment constraints), case of compound literals.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic-errors" } */
+/* { dg-skip-if "no alignment constraints" { "avr-*-*" } } */
+
+#include <stddef.h>
+
+max_align_t *p = &(_Alignas (_Alignof (char)) max_align_t) { 1 }; /* { dg-error "reduce alignment" } */
diff --git a/gcc/testsuite/gcc.dg/gnu11-align-1.c b/gcc/testsuite/gcc.dg/gnu11-align-1.c
new file mode 100644 (file)
index 0000000..50522d7
--- /dev/null
@@ -0,0 +1,8 @@
+/* Test C11 alignment support.  Test code valid after the resolution
+   of DR#444: alignment specifiers for compound literals in _Alignof.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu11" } */
+
+#include <stddef.h>
+
+size_t align = _Alignof (_Alignas (_Alignof (max_align_t)) char) { 1 };