public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Richard Biener <richard.guenther@gmail.com>
To: Joseph Myers <joseph@codesourcery.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: c: C2x constexpr
Date: Sat, 12 Nov 2022 17:12:42 +0100	[thread overview]
Message-ID: <B8C19784-BAA8-4F91-900D-63BF29AE0FFE@gmail.com> (raw)
In-Reply-To: <2dd14b67-f6f-6b30-46a0-8166a1e515c9@codesourcery.com>



> Am 12.11.2022 um 05:56 schrieb Joseph Myers <joseph@codesourcery.com>:
> 
> [Global / middle-end reviewers, note there is a dfp.cc change here
> that needs review.]
> 
> Implement C2x constexpr (a feature based on the C++ one but much more
> minimal, with only constexpr variables, not functions).
> 
> I believe this implementation is fully functional for use of this
> feature.  However, there are several things that seem unclear about
> the specification that I'll need to raise in NB comments.  There are
> also areas where there may be followup bug fixes because the
> implementation doesn't reject some more obscure cases that ought to be
> rejected: cases where a constexpr initializer for floating type meets
> the constraints for a constant expression in initializers but not
> those for an arithmetic constant expression (previously we haven't had
> to track whether something is an arithmetic constant expression in
> detail, unlike with integer constant expressions), and some cases
> where a tag or struct or union member gets declared indirectly in the
> declaration specifiers or declarator of a constexpr declaration, which
> is not permitted (modulo lack of clarity in the specification) for
> underspecified declarations in general (the cases of a declaration in
> the initializer, or a tagged type being directly declared as a type
> specifier, are already detected).
> 
> Cases of ambiguity in the specification include:
> 
> * Many questions (previously raised in WG14 discussions) over the rule
>  about what conversions do or do not involve a change of value that's
>  not allowed in a constexpr initializer, that aren't properly
>  addressed by the normative text (and where the footnote on the
>  subject isn't very clear either, and the examples don't necessarily
>  follow from the normative text).  I've made a series of choices
>  there, that include disallowing all conversions between real and
>  complex types or between binary and decimal floating types in
>  constexpr initializers, that might not necessarily agree with how
>  things end up getting clarified.
> 
>  The dfp.cc change also arises here, to allow quiet NaN initializers
>  of one DFP type to be used in a constexpr initializer for another
>  DFP type (as is possible for signaling NaNs) by ensuring the result
>  of such a conversion is properly marked as canonical (note that most
>  of the DFP code doesn't actually do anything with NaN payloads at
>  all).
> 
> * Various issues with what exactly counts as part of a declaration for
>  the purposes of the rule on underspecified declarations not
>  declaring any identifiers other than ordinary identifiers (and not
>  declaring more than one ordinary identifier, though the latter is
>  undefined behavior).  These include cases where the declaration of a
>  struct / union / enum type appears inside typeof or alignas in the
>  declaration specifiers (the latter also applies with auto), or in
>  the declarator (e.g. an array size or in a parameter declaration).
>  The issues are similar to those involved in C90 DR#115 and C99 DRs
>  #277 and #341; the intent may not be the same in all the different
>  cases involved, but it's not clear that the normative wording in the
>  various places is sufficient to deduce the differences in intent.
> 
> * The wording about producing a compound literal constant using member
>  access is present in one place but another place only applies that
>  to named constants.
> 
> * It's not clear when a structure or union constant (a constexpr
>  variable or compound literal with structure or union type, or a
>  member with such type extracted by a series of member access
>  operations) can itself be used in an initializer (constexpr or
>  otherwise).  Based on general wording for initializers not having
>  been changed, the working draft might only strictly allow it at
>  automatic storage duration (but elsewhere it would be undefined
>  behavior, not a constraint violation, so no diagnostic required) -
>  since that's the only case mentioned where a single expression of
>  structure or union type can be used to initialize an object of such
>  a type.  But it definitely seems to be allowed in even constexpr
>  initializers at automatic storage duration - and since generally
>  constexpr initializers (any storage duration) are *more* constrained
>  than ordinary static storage duration initializers, it would seem
>  odd for it not to be allowed at static storage duration.
> 
> * When you do allow such initializers, it's then not entirely clear
>  how the constraint that constexpr pointer initializers must be null
>  pointer constants should be applied (given that a constexpr object
>  of pointer type is a null pointer but *not* a null pointer
>  constant).  My guess would be that a constexpr struct or union
>  containing such a field should still be allowed as an initializer,
>  but the wording could be read otherwise.
> 
> * It also becomes important with constexpr exactly what kind of
>  constant expression an implicit zero initializer is; the wording for
>  default initialization only really deals with the value of the
>  initializer and not what kind of constant it is.  In particular,
>  this affects whether {} is a valid constexpr initializer for a
>  pointer not of type void *, since the wording only talks about a
>  null pointer, not whether it's a null pointer *constant*.  I assumed
>  that it should be a null pointer constant in that case.
> 
> * It's also not entirely clear whether constexpr can be used in the
>  declaration part of a for loop (which "shall only declare
>  identifiers for objects having storage class auto or register").  I
>  interpreted it as allowed (treating such objects as implicitly auto
>  just like those with no storage class specifiers), but it could also
>  be argued that constexpr is another storage class specifier and so
>  not allowed there.
> 
> Bootstrapped with no regressions for x86_64-pc-linux-gnu.  OK to
> commit (the dfp.cc changes)?

OK,

Richard 


> gcc/
>    * dfp.cc (decimal_from_binary): Convert a canonical NaN to a
>    canonical NaN.
> 
> gcc/c-family/
>    * c-common.cc (c_common_reswords): Use D_C2X instead of D_CXXONLY.
> 
> gcc/c/
>    * c-decl.cc (start_underspecified_init)
>    (finish_underspecified_init): Handle name == NULL_TREE for
>    compound literals.
>    (merge_decls): Merge C_DECL_DECLARED_CONSTEXPR.
>    (shadow_tag_warned): Check for constexpr.
>    (start_decl): Add parameter do_push.
>    (build_compound_literal): Set C_DECL_DECLARED_CONSTEXPR.
>    (grokdeclarator): Handle constexpr.
>    (finish_struct): Set C_TYPE_FIELDS_NON_CONSTEXPR.
>    (declspecs_add_scspec): Handle constexpr.
>    * c-parser.cc (c_token_starts_compound_literal)
>    (c_token_starts_declspecs, c_parser_declaration_or_fndef)
>    (c_parser_declspecs, c_parser_gnu_attribute_any_word)
>    (c_parser_compound_literal_scspecs)
>    (c_parser_postfix_expression_after_paren_type): Handle constexpr.
>    Update calls to start_init.
>    (c_parser_declaration_or_fndef, c_parser_initializer)
>    (c_parser_initval): Pass true for new argument of
>    convert_lvalue_to_rvalue.  Call convert_lvalue_to_rvalue for
>    constexpr compound literals.
>    (c_parser_static_assert_declaration_no_semi)
>    (c_parser_enum_specifier, c_parser_struct_declaration)
>    (c_parser_alignas_specifier, c_parser_initelt, c_parser_label):
>    Call convert_lvalue_to_rvalue on expressions required to be
>    integer constant expressions.
>    (c_parser_omp_declare_reduction): Update call to start_init.
>    * c-tree.h (C_TYPE_FIELDS_NON_CONSTEXPR)
>    (C_DECL_DECLARED_CONSTEXPR): New macros.
>    (struct c_declspecs): Add constexpr_p.
>    (start_decl, convert_lvalue_to_rvalue, start_init): Update
>    prototypes.
>    * c-typeck.cc (require_constant_value, require_constant_elements):
>    Change to bool.
>    (require_constexpr_value, maybe_get_constexpr_init)
>    (constexpr_init_fits_real_type, check_constexpr_init): New.
>    (convert_lvalue_to_rvalue): Add new parameter for_init.  Call
>    maybe_get_constexpr_init.
>    (store_init_value): Update call to digest_init.
>    (digest_init): Add parameters int_const_expr, arith_const_expr and
>    require_constexpr.  Check constexpr initializers.
>    (constructor_top_level): Remove.
>    (struct initializer_stack): Remove top_level.  Add
>    require_constexpr_value.
>    (start_init): Remove parameter top_level.  Add parameters
>    init_require_constant and init_require_constexpr.  Save
>    require_constexpr_value on stack.
>    (pop_init_level): Use a null pointer constant for zero initializer
>    of pointer initialized with {}.
>    (output_init_element): Update call to digest_init.  Avoid passing
>    null pointer constants of pointer type through digest_init a
>    second time when initializing a constexpr object.
> 
> gcc/testsuite/
>    * gcc.dg/c11-keywords-1.c: Also test constexpr.
>    * gcc.dg/c2x-constexpr-1.c, gcc.dg/c2x-constexpr-2a.c,
>    gcc.dg/c2x-constexpr-2b.c, gcc.dg/c2x-constexpr-3.c,
>    gcc.dg/c2x-constexpr-4.c, gcc.dg/c2x-constexpr-5.c,
>    gcc.dg/c2x-constexpr-6.c, gcc.dg/c2x-constexpr-7.c,
>    gcc.dg/c2x-constexpr-8.c, gcc.dg/c2x-constexpr-9.c,
>    gcc.dg/dfp/c2x-constexpr-dfp-1.c,
>    gcc.dg/dfp/c2x-constexpr-dfp-2.c, gcc.dg/gnu2x-constexpr-1.c,
>    gcc.target/i386/excess-precision-11.c,
>    gcc.target/i386/excess-precision-12.c: New tests.
> 
> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index 5890c18bdc3..71507d4cb0a 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -440,7 +440,7 @@ const struct c_common_resword c_common_reswords[] =
>   { "class",        RID_CLASS,    D_CXX_OBJC | D_CXXWARN },
>   { "const",        RID_CONST,    0 },
>   { "consteval",    RID_CONSTEVAL,    D_CXXONLY | D_CXX20 | D_CXXWARN },
> -  { "constexpr",    RID_CONSTEXPR,    D_CXXONLY | D_CXX11 | D_CXXWARN },
> +  { "constexpr",    RID_CONSTEXPR,    D_C2X | D_CXX11 | D_CXXWARN },
>   { "constinit",    RID_CONSTINIT,    D_CXXONLY | D_CXX20 | D_CXXWARN },
>   { "const_cast",    RID_CONSTCAST,    D_CXXONLY | D_CXXWARN },
>   { "continue",        RID_CONTINUE,    0 },
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index a99b7456055..36de77814ba 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -1480,26 +1480,34 @@ static bool in_underspecified_init;
>    means that NAME is shadowed inside its initializer, so neither the
>    definition being initialized, nor any definition from an outer
>    scope, may be referenced during that initializer.  Return state to
> -   be passed to finish_underspecified_init.  */
> +   be passed to finish_underspecified_init.  If NAME is NULL_TREE, the
> +   underspecified object is a (constexpr) compound literal; there is
> +   no shadowing in that case, but all the other restrictions on
> +   underspecified object definitions still apply.  */
> unsigned int
> start_underspecified_init (location_t loc, tree name)
> {
>   bool prev = in_underspecified_init;
>   bool ok;
> -  tree decl = build_decl (loc, VAR_DECL, name, error_mark_node);
> -  C_DECL_UNDERSPECIFIED (decl) = 1;
> -  struct c_scope *scope = current_scope;
> -  struct c_binding *b = I_SYMBOL_BINDING (name);
> -  if (b && B_IN_SCOPE (b, scope))
> -    {
> -      error_at (loc, "underspecified declaration of %qE, which is already "
> -        "declared in this scope", name);
> -      ok = false;
> -    }
> +  if (name == NULL_TREE)
> +    ok = true;
>   else
>     {
> -      bind (name, decl, scope, false, false, loc);
> -      ok = true;
> +      tree decl = build_decl (loc, VAR_DECL, name, error_mark_node);
> +      C_DECL_UNDERSPECIFIED (decl) = 1;
> +      struct c_scope *scope = current_scope;
> +      struct c_binding *b = I_SYMBOL_BINDING (name);
> +      if (b && B_IN_SCOPE (b, scope))
> +    {
> +      error_at (loc, "underspecified declaration of %qE, which is already "
> +            "declared in this scope", name);
> +      ok = false;
> +    }
> +      else
> +    {
> +      bind (name, decl, scope, false, false, loc);
> +      ok = true;
> +    }
>     }
>   in_underspecified_init = true;
>   return ok | (prev << 1);
> @@ -1508,11 +1516,12 @@ start_underspecified_init (location_t loc, tree name)
> /* Finish an underspecified object definition for NAME, before that
>    name is bound to the real declaration instead of a placeholder.
>    PREV_STATE is the value returned by the call to
> -   start_underspecified_init.  */
> +   start_underspecified_init.  If NAME is NULL_TREE, this means a
> +   compound literal, as for start_underspecified_init.  */
> void
> finish_underspecified_init (tree name, unsigned int prev_state)
> {
> -  if (prev_state & 1)
> +  if (name != NULL_TREE && (prev_state & 1))
>     {
>       /* A VAR_DECL was bound to the name to shadow any previous
>     declarations for the name; remove that binding now.  */
> @@ -2745,6 +2754,15 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
>    if (DECL_INITIAL (newdecl) == NULL_TREE)
>     DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl);
> 
> +  /* Merge 'constexpr' information.  */
> +  if (VAR_P (olddecl) && VAR_P (newdecl))
> +    {
> +      if (C_DECL_DECLARED_CONSTEXPR (olddecl))
> +    C_DECL_DECLARED_CONSTEXPR (newdecl) = 1;
> +      else if (C_DECL_DECLARED_CONSTEXPR (newdecl))
> +    C_DECL_DECLARED_CONSTEXPR (olddecl) = 1;
> +    }
> +
>   /* Merge the threadprivate attribute.  */
>   if (VAR_P (olddecl) && C_DECL_THREADPRIVATE_P (olddecl))
>     C_DECL_THREADPRIVATE_P (newdecl) = 1;
> @@ -4944,6 +4962,12 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned)
>       warned = 1;
>     }
> 
> +  if (declspecs->constexpr_p)
> +    {
> +      error ("%<constexpr%> in empty declaration");
> +      warned = 1;
> +    }
> +
>   if (current_scope == file_scope && declspecs->storage_class == csc_auto)
>     {
>       error ("%<auto%> in file-scope empty declaration");
> @@ -5301,7 +5325,7 @@ c_decl_attributes (tree *node, tree attributes, int flags)
>    This is called as soon as the type information and variable name
>    have been parsed, before parsing the initializer if any.
>    Here we create the ..._DECL node, fill in its type,
> -   and put it on the list of decls for the current context.
> +   and (if DO_PUSH) put it on the list of decls for the current context.
>    When nonnull, set *LASTLOC to the location of the prior declaration
>    of the same entity if one exists.
>    The ..._DECL node is returned as the value.
> @@ -5316,7 +5340,8 @@ c_decl_attributes (tree *node, tree attributes, int flags)
> 
> tree
> start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
> -        bool initialized, tree attributes, location_t *lastloc /* = NULL */)
> +        bool initialized, tree attributes, bool do_push /* = true */,
> +        location_t *lastloc /* = NULL */)
> {
>   tree decl;
>   tree tem;
> @@ -5489,15 +5514,20 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
> 
>   /* Add this decl to the current scope.
>      TEM may equal DECL or it may be a previous decl of the same name.  */
> -  tem = pushdecl (decl);
> -
> -  if (initialized && DECL_EXTERNAL (tem))
> +  if (do_push)
>     {
> -      DECL_EXTERNAL (tem) = 0;
> -      TREE_STATIC (tem) = 1;
> -    }
> +      tem = pushdecl (decl);
> +
> +      if (initialized && DECL_EXTERNAL (tem))
> +    {
> +      DECL_EXTERNAL (tem) = 0;
> +      TREE_STATIC (tem) = 1;
> +    }
> 
> -  return tem;
> +      return tem;
> +    }
> +  else
> +    return decl;
> }
> 
> /* Subroutine of finish_decl. TYPE is the type of an uninitialized object
> @@ -6214,6 +6244,7 @@ build_compound_literal (location_t loc, tree type, tree init, bool non_const,
>   DECL_ARTIFICIAL (decl) = 1;
>   DECL_IGNORED_P (decl) = 1;
>   C_DECL_COMPOUND_LITERAL_P (decl) = 1;
> +  C_DECL_DECLARED_CONSTEXPR (decl) = scspecs && scspecs->constexpr_p;
>   TREE_TYPE (decl) = type;
>   if (threadp)
>     set_decl_tls_model (decl, decl_default_tls_model (decl));
> @@ -6501,6 +6532,7 @@ grokdeclarator (const struct c_declarator *declarator,
> {
>   tree type = declspecs->type;
>   bool threadp = declspecs->thread_p;
> +  bool constexprp = declspecs->constexpr_p;
>   enum c_storage_class storage_class = declspecs->storage_class;
>   int constp;
>   int restrictp;
> @@ -6743,6 +6775,7 @@ grokdeclarator (const struct c_declarator *declarator,
> 
>   if (funcdef_flag
>       && (threadp
> +      || constexprp
>      || storage_class == csc_auto
>      || storage_class == csc_register
>      || storage_class == csc_typedef))
> @@ -6759,6 +6792,9 @@ grokdeclarator (const struct c_declarator *declarator,
>    error_at (loc, "function definition declared %qs",
>          declspecs->thread_gnu_p ? "__thread" : "_Thread_local");
>       threadp = false;
> +      /* The parser ensures a constexpr function definition never
> +     reaches here.  */
> +      gcc_assert (!constexprp);
>       if (storage_class == csc_auto
>      || storage_class == csc_register
>      || storage_class == csc_typedef)
> @@ -6766,10 +6802,12 @@ grokdeclarator (const struct c_declarator *declarator,
>     }
>   else if (decl_context != NORMAL && (storage_class != csc_none
>                      || threadp
> +                      || constexprp
>                      || declspecs->c2x_auto_p))
>     {
>       if (decl_context == PARM
>      && storage_class == csc_register
> +      && !constexprp
>      && !declspecs->c2x_auto_p)
>    ;
>       else
> @@ -6796,6 +6834,7 @@ grokdeclarator (const struct c_declarator *declarator,
>        }
>      storage_class = csc_none;
>      threadp = false;
> +      constexprp = false;
>    }
>     }
>   else if (storage_class == csc_extern
> @@ -7843,7 +7882,7 @@ grokdeclarator (const struct c_declarator *declarator,
>       }
>     else if (TREE_CODE (type) == FUNCTION_TYPE)
>       {
> -    if (storage_class == csc_register || threadp)
> +    if (storage_class == csc_register || threadp || constexprp)
>      {
>        error_at (loc, "invalid storage class for function %qE", name);
>      }
> @@ -7943,6 +7982,32 @@ grokdeclarator (const struct c_declarator *declarator,
>    /* An uninitialized decl with `extern' is a reference.  */
>    int extern_ref = !initialized && storage_class == csc_extern;
> 
> +    if (constexprp)
> +      {
> +        /* The type of a constexpr variable must not be variably
> +           modified, volatile, atomic or restrict qualified or
> +           have a member with such a qualifier.  const
> +           qualification is implicitly added, and, at file scope,
> +           has internal linkage.  */
> +        if (variably_modified_type_p (type, NULL_TREE))
> +          error_at (loc, "%<constexpr%> object has variably modified "
> +            "type");
> +        if (type_quals
> +        & (TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT | TYPE_QUAL_ATOMIC))
> +          error_at (loc, "invalid qualifiers for %<constexpr%> object");
> +        else
> +          {
> +        tree type_no_array = strip_array_types (type);
> +        if (RECORD_OR_UNION_TYPE_P (type_no_array)
> +            && C_TYPE_FIELDS_NON_CONSTEXPR (type_no_array))
> +          error_at (loc, "invalid qualifiers for field of "
> +                "%<constexpr%> object");
> +          }
> +        type_quals |= TYPE_QUAL_CONST;
> +        if (current_scope == file_scope)
> +          storage_class = csc_static;
> +      }
> +
>    type = c_build_qualified_type (type, type_quals, orig_qual_type,
>                       orig_qual_indirect);
> 
> @@ -7969,6 +8034,8 @@ grokdeclarator (const struct c_declarator *declarator,
>               VAR_DECL, declarator->u.id.id, type);
>    if (size_varies)
>      C_DECL_VARIABLE_SIZE (decl) = 1;
> +    if (constexprp)
> +      C_DECL_DECLARED_CONSTEXPR (decl) = 1;
> 
>    if (declspecs->inline_p)
>      pedwarn (loc, 0, "variable %q+D declared %<inline%>", decl);
> @@ -9119,13 +9186,13 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
> 
>       DECL_CONTEXT (x) = t;
> 
> +      tree t1 = strip_array_types (TREE_TYPE (x));
>       /* If any field is const, the structure type is pseudo-const.  */
>       if (TREE_READONLY (x))
>    C_TYPE_FIELDS_READONLY (t) = 1;
>       else
>    {
>      /* A field that is pseudo-const makes the structure likewise.  */
> -      tree t1 = strip_array_types (TREE_TYPE (x));
>      if (RECORD_OR_UNION_TYPE_P (t1) && C_TYPE_FIELDS_READONLY (t1))
>        C_TYPE_FIELDS_READONLY (t) = 1;
>    }
> @@ -9133,7 +9200,18 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
>       /* Any field that is volatile means variables of this type must be
>     treated in some ways as volatile.  */
>       if (TREE_THIS_VOLATILE (x))
> -    C_TYPE_FIELDS_VOLATILE (t) = 1;
> +    {
> +      C_TYPE_FIELDS_VOLATILE (t) = 1;
> +      C_TYPE_FIELDS_NON_CONSTEXPR (t) = 1;
> +    }
> +
> +      /* Any field that is volatile, restrict-qualified or atomic
> +     means the type cannot be used for a constexpr object.  */
> +      if (TYPE_QUALS (t1)
> +      & (TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT | TYPE_QUAL_ATOMIC))
> +    C_TYPE_FIELDS_NON_CONSTEXPR (t) = 1;
> +      else if (RECORD_OR_UNION_TYPE_P (t1) && C_TYPE_FIELDS_NON_CONSTEXPR (t1))
> +        C_TYPE_FIELDS_NON_CONSTEXPR (t) = 1;
> 
>       /* Any field of nominal variable size implies structure is too.  */
>       if (C_DECL_VARIABLE_SIZE (x))
> @@ -9335,6 +9413,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
>       TYPE_TRANSPARENT_AGGR (x) = TYPE_TRANSPARENT_AGGR (t);
>       C_TYPE_FIELDS_READONLY (x) = C_TYPE_FIELDS_READONLY (t);
>       C_TYPE_FIELDS_VOLATILE (x) = C_TYPE_FIELDS_VOLATILE (t);
> +      C_TYPE_FIELDS_NON_CONSTEXPR (x) = C_TYPE_FIELDS_NON_CONSTEXPR (t);
>       C_TYPE_VARIABLE_SIZE (x) = C_TYPE_VARIABLE_SIZE (t);
>       C_TYPE_INCOMPLETE_VARS (x) = NULL_TREE;
>     }
> @@ -12266,6 +12345,8 @@ declspecs_add_scspec (location_t loc,
>    error ("%qE used with %<register%>", scspec);
>       else if (specs->storage_class == csc_typedef)
>    error ("%qE used with %<typedef%>", scspec);
> +      else if (specs->constexpr_p)
> +    error ("%qE used with %<constexpr%>", scspec);
>       else
>    {
>      specs->thread_p = true;
> @@ -12323,6 +12404,18 @@ declspecs_add_scspec (location_t loc,
>      specs->c2x_auto_p = false;
>    }
>       break;
> +    case RID_CONSTEXPR:
> +      dupe = specs->constexpr_p;
> +      if (specs->storage_class == csc_extern)
> +    error ("%qE used with %<extern%>", scspec);
> +      else if (specs->storage_class == csc_typedef)
> +    error ("%qE used with %<typedef%>", scspec);
> +      else if (specs->thread_p)
> +    error ("%qE used with %qs", scspec,
> +           specs->thread_gnu_p ? "__thread" : "_Thread_local");
> +      else
> +    specs->constexpr_p = true;
> +      break;
>     default:
>       gcc_unreachable ();
>     }
> @@ -12352,6 +12445,12 @@ declspecs_add_scspec (location_t loc,
>             scspec);
>          specs->thread_p = false;
>        }
> +      if (n != csc_auto && n != csc_register && n != csc_static
> +          && specs->constexpr_p)
> +        {
> +          error ("%<constexpr%> used with %qE", scspec);
> +          specs->constexpr_p = false;
> +        }
>    }
>     }
>   return specs;
> diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
> index d70697b1d63..1d144bba24d 100644
> --- a/gcc/c/c-parser.cc
> +++ b/gcc/c/c-parser.cc
> @@ -677,6 +677,7 @@ c_token_starts_compound_literal (c_token *token)
>     case CPP_KEYWORD:
>       switch (token->keyword)
>    {
> +    case RID_CONSTEXPR:
>    case RID_REGISTER:
>    case RID_STATIC:
>    case RID_THREAD:
> @@ -795,6 +796,7 @@ c_token_starts_declspecs (c_token *token)
>    case RID_ALIGNAS:
>    case RID_ATOMIC:
>    case RID_AUTO_TYPE:
> +    case RID_CONSTEXPR:
>      return true;
>    default:
>      if (token->keyword >= RID_FIRST_INT_N
> @@ -2108,6 +2110,32 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>   bool any_auto_type_p = gnu_auto_type_p || std_auto_type_p;
>   gcc_assert (!(gnu_auto_type_p && std_auto_type_p));
>   const char *auto_type_keyword = gnu_auto_type_p ? "__auto_type" : "auto";
> +  if (specs->constexpr_p)
> +    {
> +      /* An underspecified declaration may not declare tags or members
> +     or structures or unions; it is undefined behavior to declare
> +     the members of an enumeration.  Where the structure, union or
> +     enumeration type is declared within an initializer, this is
> +     diagnosed elsewhere.  Diagnose here the case of declaring
> +     such a type in the type specifiers of a constexpr
> +     declaration.  */
> +      switch (specs->typespec_kind)
> +    {
> +    case ctsk_tagfirstref:
> +    case ctsk_tagfirstref_attrs:
> +      error_at (here, "%qT declared in underspecified object declaration",
> +            specs->type);
> +      break;
> +
> +    case ctsk_tagdef:
> +      error_at (here, "%qT defined in underspecified object declaration",
> +            specs->type);
> +      break;
> +
> +    default:
> +      break;
> +    }
> +    }
>   if (c_parser_next_token_is (parser, CPP_SEMICOLON))
>     {
>       bool handled_assume = false;
> @@ -2257,7 +2285,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>       bool dummy = false;
>       timevar_id_t tv;
>       tree fnbody = NULL_TREE;
> -      tree std_auto_name = NULL_TREE;
> +      tree underspec_name = NULL_TREE;
>       /* Declaring either one or more declarators (in which case we
>     should diagnose if there were no declaration specifiers) or a
>     function definition (in which case the diagnostic for
> @@ -2296,7 +2324,14 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>          c_parser_skip_to_end_of_block_or_statement (parser);
>          return;
>        }
> -      std_auto_name = d->u.id.id;
> +      underspec_name = d->u.id.id;
> +    }
> +      else if (specs->constexpr_p)
> +    {
> +      struct c_declarator *d = declarator;
> +      while (d->kind != cdk_id)
> +        d = d->declarator;
> +      underspec_name = d->u.id.id;
>    }
>       if (c_parser_next_token_is (parser, CPP_EQ)
>      || c_parser_next_token_is (parser, CPP_COMMA)
> @@ -2343,9 +2378,13 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>          rich_location richloc (line_table, init_loc);
>          unsigned int underspec_state = 0;
>          if (std_auto_type_p)
> -            underspec_state = start_underspecified_init (init_loc,
> -                                 std_auto_name);
> -          start_init (NULL_TREE, asm_name, global_bindings_p (), &richloc);
> +            underspec_state =
> +              start_underspecified_init (init_loc, underspec_name);
> +          start_init (NULL_TREE, asm_name,
> +                  (global_bindings_p ()
> +                   || specs->storage_class == csc_static
> +                   || specs->constexpr_p),
> +                  specs->constexpr_p, &richloc);
>          /* A parameter is initialized, which is invalid.  Don't
>             attempt to instrument the initializer.  */
>          int flag_sanitize_save = flag_sanitize;
> @@ -2364,7 +2403,8 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>          else
>            init = c_parser_expr_no_commas (parser, NULL);
>          if (std_auto_type_p)
> -            finish_underspecified_init (std_auto_name, underspec_state);
> +            finish_underspecified_init (underspec_name,
> +                        underspec_state);
>          flag_sanitize = flag_sanitize_save;
>          if (gnu_auto_type_p
>              && TREE_CODE (init.value) == COMPONENT_REF
> @@ -2372,7 +2412,8 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>            error_at (here,
>                  "%<__auto_type%> used with a bit-field"
>                  " initializer");
> -          init = convert_lvalue_to_rvalue (init_loc, init, true, true);
> +          init = convert_lvalue_to_rvalue (init_loc, init, true, true,
> +                           true);
>          tree init_type = TREE_TYPE (init.value);
>          bool vm_type = variably_modified_type_p (init_type,
>                               NULL_TREE);
> @@ -2417,17 +2458,26 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>          else
>        {
>          /* The declaration of the variable is in effect while
> -             its initializer is parsed.  */
> +             its initializer is parsed, except for a constexpr
> +             variable.  */
> +          init_loc = c_parser_peek_token (parser)->location;
> +          rich_location richloc (line_table, init_loc);
> +          unsigned int underspec_state = 0;
> +          if (specs->constexpr_p)
> +            underspec_state =
> +              start_underspecified_init (init_loc, underspec_name);
>          d = start_decl (declarator, specs, true,
> -                  chainon (postfix_attrs, all_prefix_attrs));
> +                  chainon (postfix_attrs,
> +                       all_prefix_attrs),
> +                  !specs->constexpr_p);
>          if (!d)
>            d = error_mark_node;
> -          if (omp_declare_simd_clauses)
> +          if (!specs->constexpr_p && omp_declare_simd_clauses)
>            c_finish_omp_declare_simd (parser, d, NULL_TREE,
>                           omp_declare_simd_clauses);
> -          init_loc = c_parser_peek_token (parser)->location;
> -          rich_location richloc (line_table, init_loc);
> -          start_init (d, asm_name, global_bindings_p (), &richloc);
> +          start_init (d, asm_name,
> +                  TREE_STATIC (d) || specs->constexpr_p,
> +                  specs->constexpr_p, &richloc);
>          /* A parameter is initialized, which is invalid.  Don't
>             attempt to instrument the initializer.  */
>          int flag_sanitize_save = flag_sanitize;
> @@ -2435,6 +2485,15 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>            flag_sanitize = 0;
>          init = c_parser_initializer (parser, d);
>          flag_sanitize = flag_sanitize_save;
> +          if (specs->constexpr_p)
> +            {
> +              finish_underspecified_init (underspec_name,
> +                          underspec_state);
> +              d = pushdecl (d);
> +              if (omp_declare_simd_clauses)
> +            c_finish_omp_declare_simd (parser, d, NULL_TREE,
> +                           omp_declare_simd_clauses);
> +            }
>          finish_init ();
>        }
>          if (oacc_routine_data)
> @@ -2448,18 +2507,19 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>        }
>      else
>        {
> -          if (any_auto_type_p)
> +          if (any_auto_type_p || specs->constexpr_p)
>        {
>          error_at (here,
>                "%qs requires an initialized data declaration",
> -                auto_type_keyword);
> +                any_auto_type_p ? auto_type_keyword : "constexpr");
>          c_parser_skip_to_end_of_block_or_statement (parser);
>          return;
>        }
> 
>          location_t lastloc = UNKNOWN_LOCATION;
>          tree attrs = chainon (postfix_attrs, all_prefix_attrs);
> -          tree d = start_decl (declarator, specs, false, attrs, &lastloc);
> +          tree d = start_decl (declarator, specs, false, attrs, true,
> +                   &lastloc);
>          if (d && TREE_CODE (d) == FUNCTION_DECL)
>        {
>          /* Find the innermost declarator that is neither cdk_id
> @@ -2540,11 +2600,11 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>        }
>      if (c_parser_next_token_is (parser, CPP_COMMA))
>        {
> -          if (any_auto_type_p)
> +          if (any_auto_type_p || specs->constexpr_p)
>        {
>          error_at (here,
>                "%qs may only be used with a single declarator",
> -                auto_type_keyword);
> +                any_auto_type_p ? auto_type_keyword : "constexpr");
>          c_parser_skip_to_end_of_block_or_statement (parser);
>          return;
>        }
> @@ -2577,11 +2637,11 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
>          return;
>        }
>    }
> -      else if (any_auto_type_p)
> +      else if (any_auto_type_p || specs->constexpr_p)
>    {
>      error_at (here,
>            "%qs requires an initialized data declaration",
> -            auto_type_keyword);
> +            any_auto_type_p ? auto_type_keyword : "constexpr");
>      c_parser_skip_to_end_of_block_or_statement (parser);
>      return;
>    }
> @@ -2789,7 +2849,9 @@ c_parser_static_assert_declaration_no_semi (c_parser *parser)
>   if (!parens.require_open (parser))
>     return;
>   location_t value_tok_loc = c_parser_peek_token (parser)->location;
> -  value = c_parser_expr_no_commas (parser, NULL).value;
> +  value = convert_lvalue_to_rvalue (value_tok_loc,
> +                    c_parser_expr_no_commas (parser, NULL),
> +                    true, true).value;
>   value_loc = EXPR_LOC_OR_LOC (value, value_tok_loc);
>   if (c_parser_next_token_is (parser, CPP_COMMA))
>     {
> @@ -3092,6 +3154,7 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
>    case RID_NORETURN:
>    case RID_AUTO:
>    case RID_THREAD:
> +    case RID_CONSTEXPR:
>      if (!scspec_ok)
>        goto out;
>      attrs_ok = true;
> @@ -3462,7 +3525,10 @@ c_parser_enum_specifier (c_parser *parser)
>        {
>          c_parser_consume_token (parser);
>          value_loc = c_parser_peek_token (parser)->location;
> -          enum_value = c_parser_expr_no_commas (parser, NULL).value;
> +          enum_value = convert_lvalue_to_rvalue (value_loc,
> +                             (c_parser_expr_no_commas
> +                              (parser, NULL)),
> +                             true, true).value;
>        }
>      else
>        enum_value = NULL_TREE;
> @@ -3900,7 +3966,11 @@ c_parser_struct_declaration (c_parser *parser)
>      if (c_parser_next_token_is (parser, CPP_COLON))
>        {
>          c_parser_consume_token (parser);
> -          width = c_parser_expr_no_commas (parser, NULL).value;
> +          location_t loc = c_parser_peek_token (parser)->location;
> +          width = convert_lvalue_to_rvalue (loc,
> +                        (c_parser_expr_no_commas
> +                         (parser, NULL)),
> +                        true, true).value;
>        }
>      if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
>        postfix_attrs = c_parser_gnu_attributes (parser);
> @@ -4069,7 +4139,9 @@ c_parser_alignas_specifier (c_parser * parser)
>                    false, true, 1);
>     }
>   else
> -    ret = c_parser_expr_no_commas (parser, NULL).value;
> +    ret = convert_lvalue_to_rvalue (loc,
> +                    c_parser_expr_no_commas (parser, NULL),
> +                    true, true).value;
>   parens.skip_until_found_close (parser);
>   return ret;
> }
> @@ -4817,6 +4889,7 @@ c_parser_gnu_attribute_any_word (c_parser *parser)
>    case RID_TRANSACTION_CANCEL:
>    case RID_ATOMIC:
>    case RID_AUTO_TYPE:
> +    case RID_CONSTEXPR:
>    case RID_INT_N_0:
>    case RID_INT_N_1:
>    case RID_INT_N_2:
> @@ -5538,8 +5611,10 @@ c_parser_initializer (c_parser *parser, tree decl)
>      && !warn_init_self)
>    suppress_warning (decl, OPT_Winit_self);
>       if (TREE_CODE (ret.value) != STRING_CST
> -      && TREE_CODE (ret.value) != COMPOUND_LITERAL_EXPR)
> -    ret = convert_lvalue_to_rvalue (loc, ret, true, true);
> +      && (TREE_CODE (ret.value) != COMPOUND_LITERAL_EXPR
> +          || C_DECL_DECLARED_CONSTEXPR (COMPOUND_LITERAL_EXPR_DECL
> +                        (ret.value))))
> +    ret = convert_lvalue_to_rvalue (loc, ret, true, true, true);
>       return ret;
>     }
> }
> @@ -5685,6 +5760,7 @@ c_parser_initelt (c_parser *parser, struct obstack * braced_init_obstack)
>        }
>      else
>        {
> +          struct c_expr first_expr;
>          tree first, second;
>          location_t ellipsis_loc = UNKNOWN_LOCATION;  /* Quiet warning.  */
>          location_t array_index_loc = UNKNOWN_LOCATION;
> @@ -5728,11 +5804,13 @@ c_parser_initelt (c_parser *parser, struct obstack * braced_init_obstack)
>              rec = objc_get_class_reference (id);
>              goto parse_message_args;
>            }
> -          first = c_parser_expr_no_commas (parser, NULL).value;
> -          mark_exp_read (first);
> +          array_index_loc = c_parser_peek_token (parser)->location;
> +          first_expr = c_parser_expr_no_commas (parser, NULL);
> +          mark_exp_read (first_expr.value);
>          if (c_parser_next_token_is (parser, CPP_ELLIPSIS)
>              || c_parser_next_token_is (parser, CPP_CLOSE_SQUARE))
>            goto array_desig_after_first;
> +          first = first_expr.value;
>          /* Expression receiver.  So far only one part
>             without commas has been parsed; there might be
>             more of the expression.  */
> @@ -5767,14 +5845,21 @@ c_parser_initelt (c_parser *parser, struct obstack * braced_init_obstack)
>        }
>          c_parser_consume_token (parser);
>          array_index_loc = c_parser_peek_token (parser)->location;
> -          first = c_parser_expr_no_commas (parser, NULL).value;
> -          mark_exp_read (first);
> +          first_expr = c_parser_expr_no_commas (parser, NULL);
> +          mark_exp_read (first_expr.value);
>        array_desig_after_first:
> +          first_expr = convert_lvalue_to_rvalue (array_index_loc,
> +                             first_expr,
> +                             true, true);
> +          first = first_expr.value;
>          if (c_parser_next_token_is (parser, CPP_ELLIPSIS))
>        {
>          ellipsis_loc = c_parser_peek_token (parser)->location;
>          c_parser_consume_token (parser);
> -          second = c_parser_expr_no_commas (parser, NULL).value;
> +          second = convert_lvalue_to_rvalue (ellipsis_loc,
> +                             (c_parser_expr_no_commas
> +                              (parser, NULL)),
> +                             true, true).value;
>          mark_exp_read (second);
>        }
>          else
> @@ -5847,8 +5932,10 @@ c_parser_initval (c_parser *parser, struct c_expr *after,
>       init = c_parser_expr_no_commas (parser, after);
>       if (init.value != NULL_TREE
>      && TREE_CODE (init.value) != STRING_CST
> -      && TREE_CODE (init.value) != COMPOUND_LITERAL_EXPR)
> -    init = convert_lvalue_to_rvalue (loc, init, true, true);
> +      && (TREE_CODE (init.value) != COMPOUND_LITERAL_EXPR
> +          || C_DECL_DECLARED_CONSTEXPR (COMPOUND_LITERAL_EXPR_DECL
> +                        (init.value))))
> +    init = convert_lvalue_to_rvalue (loc, init, true, true, true);
>     }
>   process_init_element (loc, init, false, braced_init_obstack);
> }
> @@ -6205,7 +6292,9 @@ c_parser_label (c_parser *parser, tree std_attrs)
>     {
>       tree exp1, exp2;
>       c_parser_consume_token (parser);
> -      exp1 = c_parser_expr_no_commas (parser, NULL).value;
> +      exp1 = convert_lvalue_to_rvalue (loc1,
> +                       c_parser_expr_no_commas (parser, NULL),
> +                       true, true).value;
>       if (c_parser_next_token_is (parser, CPP_COLON))
>    {
>      c_parser_consume_token (parser);
> @@ -6214,7 +6303,10 @@ c_parser_label (c_parser *parser, tree std_attrs)
>       else if (c_parser_next_token_is (parser, CPP_ELLIPSIS))
>    {
>      c_parser_consume_token (parser);
> -      exp2 = c_parser_expr_no_commas (parser, NULL).value;
> +      exp2 = convert_lvalue_to_rvalue (loc1,
> +                       c_parser_expr_no_commas (parser,
> +                                    NULL),
> +                       true, true).value;
>      if (c_parser_require (parser, CPP_COLON, "expected %<:%>"))
>        label = do_case (loc1, exp1, exp2, std_attrs);
>    }
> @@ -8411,6 +8503,7 @@ c_parser_compound_literal_scspecs (c_parser *parser)
>     {
>       switch (c_parser_peek_token (parser)->keyword)
>    {
> +    case RID_CONSTEXPR:
>    case RID_REGISTER:
>    case RID_STATIC:
>    case RID_THREAD:
> @@ -10697,17 +10790,71 @@ c_parser_postfix_expression_after_paren_type (c_parser *parser,
>   location_t start_loc;
>   tree type_expr = NULL_TREE;
>   bool type_expr_const = true;
> +  bool constexpr_p = scspecs ? scspecs->constexpr_p : false;
> +  unsigned int underspec_state = 0;
>   check_compound_literal_type (type_loc, type_name);
>   rich_location richloc (line_table, type_loc);
> -  start_init (NULL_TREE, NULL, 0, &richloc);
> -  type = groktypename (type_name, &type_expr, &type_expr_const);
>   start_loc = c_parser_peek_token (parser)->location;
> +  if (constexpr_p)
> +    {
> +      underspec_state = start_underspecified_init (start_loc, NULL_TREE);
> +      /* A constexpr compound literal is subject to the constraints on
> +     underspecified declarations, which may not declare tags or
> +     members or structures or unions; it is undefined behavior to
> +     declare the members of an enumeration.  Where the structure,
> +     union or enumeration type is declared within the compound
> +     literal initializer, this is diagnosed elsewhere as a result
> +     of the above call to start_underspecified_init.  Diagnose
> +     here the case of declaring such a type in the type specifiers
> +     of the compound literal.  */
> +      switch (type_name->specs->typespec_kind)
> +    {
> +    case ctsk_tagfirstref:
> +    case ctsk_tagfirstref_attrs:
> +      error_at (type_loc, "%qT declared in %<constexpr%> compound literal",
> +            type_name->specs->type);
> +      break;
> +
> +    case ctsk_tagdef:
> +      error_at (type_loc, "%qT defined in %<constexpr%> compound literal",
> +            type_name->specs->type);
> +      break;
> +
> +    default:
> +      break;
> +    }
> +    }
> +  start_init (NULL_TREE, NULL,
> +          (global_bindings_p ()
> +           || (scspecs && scspecs->storage_class == csc_static)
> +           || constexpr_p), constexpr_p, &richloc);
> +  type = groktypename (type_name, &type_expr, &type_expr_const);
>   if (type != error_mark_node && C_TYPE_VARIABLE_SIZE (type))
>     {
>       error_at (type_loc, "compound literal has variable size");
>       type = error_mark_node;
>     }
> +  if (constexpr_p && type != error_mark_node)
> +    {
> +      tree type_no_array = strip_array_types (type);
> +      /* The type of a constexpr object must not be variably modified
> +     (which applies to all compound literals), volatile, atomic or
> +     restrict qualified or have a member with such a qualifier.
> +     const qualification is implicitly added.  */
> +      if (TYPE_QUALS (type_no_array)
> +      & (TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT | TYPE_QUAL_ATOMIC))
> +    error_at (type_loc, "invalid qualifiers for %<constexpr%> object");
> +      else if (RECORD_OR_UNION_TYPE_P (type_no_array)
> +           && C_TYPE_FIELDS_NON_CONSTEXPR (type_no_array))
> +    error_at (type_loc, "invalid qualifiers for field of "
> +          "%<constexpr%> object");
> +      type = c_build_qualified_type (type,
> +                     (TYPE_QUALS (type_no_array)
> +                      | TYPE_QUAL_CONST));
> +    }
>   init = c_parser_braced_init (parser, type, false, NULL, NULL_TREE);
> +  if (constexpr_p)
> +    finish_underspecified_init (NULL_TREE, underspec_state);
>   finish_init ();
>   maybe_warn_string_init (type_loc, type, init);
> 
> @@ -23194,7 +23341,7 @@ c_parser_omp_declare_reduction (c_parser *parser, enum pragma_context context)
>          tree st = push_stmt_list ();
>          location_t loc = c_parser_peek_token (parser)->location;
>          rich_location richloc (line_table, loc);
> -          start_init (omp_priv, NULL_TREE, 0, &richloc);
> +          start_init (omp_priv, NULL_TREE, false, false, &richloc);
>          struct c_expr init = c_parser_initializer (parser, omp_priv);
>          finish_init ();
>          finish_decl (omp_priv, loc, init.value,
> diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
> index 8116e5cc984..c287124c990 100644
> --- a/gcc/c/c-tree.h
> +++ b/gcc/c/c-tree.h
> @@ -34,6 +34,11 @@ along with GCC; see the file COPYING3.  If not see
> /* In a RECORD_TYPE or UNION_TYPE, nonzero if any component is volatile.  */
> #define C_TYPE_FIELDS_VOLATILE(TYPE) TREE_LANG_FLAG_2 (TYPE)
> 
> +/* In a RECORD_TYPE or UNION_TYPE, nonzero if any component is
> +   volatile, restrict-qualified or atomic; that is, has a type not
> +   permitted for a constexpr object.  */
> +#define C_TYPE_FIELDS_NON_CONSTEXPR(TYPE) TREE_LANG_FLAG_4 (TYPE)
> +
> /* In a RECORD_TYPE or UNION_TYPE or ENUMERAL_TYPE
>    nonzero if the definition of the type has already started.  */
> #define C_TYPE_BEING_DEFINED(TYPE) TYPE_LANG_FLAG_0 (TYPE)
> @@ -104,6 +109,10 @@ along with GCC; see the file COPYING3.  If not see
>    definition.  */
> #define C_DECL_UNDERSPECIFIED(DECL) DECL_LANG_FLAG_7 (DECL)
> 
> +/* Set on VAR_DECLs declared as 'constexpr'.  */
> +#define C_DECL_DECLARED_CONSTEXPR(DECL) \
> +  DECL_LANG_FLAG_8 (VAR_DECL_CHECK (DECL))
> +
> /* Nonzero for a decl which either doesn't exist or isn't a prototype.
>    N.B. Could be simplified if all built-in decls had complete prototypes
>    (but this is presently difficult because some of them need FILE*).  */
> @@ -439,6 +448,8 @@ struct c_declspecs {
>      no type specifier appears later in these declaration
>      specifiers.  */
>   BOOL_BITFIELD c2x_auto_p : 1;
> +  /* Whether "constexpr" was specified.  */
> +  BOOL_BITFIELD constexpr_p : 1;
>   /* The address space that the declaration belongs to.  */
>   addr_space_t address_space;
> };
> @@ -662,7 +673,7 @@ extern void shadow_tag_warned (const struct c_declspecs *, int);
> extern tree start_enum (location_t, struct c_enum_contents *, tree, tree);
> extern bool start_function (struct c_declspecs *, struct c_declarator *, tree);
> extern tree start_decl (struct c_declarator *, struct c_declspecs *, bool,
> -            tree, location_t * = NULL);
> +            tree, bool = true, location_t * = NULL);
> extern tree start_struct (location_t, enum tree_code, tree,
>              class c_struct_parse_info **);
> extern void store_parm_decls (void);
> @@ -733,7 +744,7 @@ extern struct c_expr default_function_array_conversion (location_t,
> extern struct c_expr default_function_array_read_conversion (location_t,
>                                 struct c_expr);
> extern struct c_expr convert_lvalue_to_rvalue (location_t, struct c_expr,
> -                           bool, bool);
> +                           bool, bool, bool = false);
> extern tree decl_constant_value_1 (tree, bool);
> extern void mark_exp_read (tree);
> extern tree composite_type (tree, tree);
> @@ -756,7 +767,7 @@ extern tree c_cast_expr (location_t, struct c_type_name *, tree);
> extern tree build_c_cast (location_t, tree, tree);
> extern void store_init_value (location_t, tree, tree, tree);
> extern void maybe_warn_string_init (location_t, tree, struct c_expr);
> -extern void start_init (tree, tree, int, rich_location *);
> +extern void start_init (tree, tree, bool, bool, rich_location *);
> extern void finish_init (void);
> extern void really_start_incremental_init (tree);
> extern void finish_implicit_inits (location_t, struct obstack *);
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index 636098444b4..e06f052eb46 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -84,8 +84,9 @@ location_t c_last_sizeof_loc;
>    initializer" message within this initializer.  */
> static int found_missing_braces;
> 
> -static int require_constant_value;
> -static int require_constant_elements;
> +static bool require_constant_value;
> +static bool require_constant_elements;
> +static bool require_constexpr_value;
> 
> static bool null_pointer_constant_p (const_tree);
> static tree qualify_type (tree, tree);
> @@ -109,7 +110,8 @@ static void push_member_name (tree);
> static int spelling_length (void);
> static char *print_spelling (char *);
> static void warning_init (location_t, int, const char *);
> -static tree digest_init (location_t, tree, tree, tree, bool, bool, int);
> +static tree digest_init (location_t, tree, tree, tree, bool, bool, bool, bool,
> +             bool, bool);
> static void output_init_element (location_t, tree, tree, bool, tree, tree, bool,
>                 bool, struct obstack *);
> static void output_pending_init_elements (int, struct obstack *);
> @@ -2133,20 +2135,91 @@ really_atomic_lvalue (tree expr)
>   return true;
> }
> 
> +/* If EXPR is a named constant (C2x) derived from a constexpr variable
> +   - that is, a reference to such a variable, or a member extracted by
> +   a sequence of structure and union (but not array) member accesses
> +   (where union member accesses must access the same member as
> +   initialized) - then return the corresponding initializer;
> +   otherwise, return NULL_TREE.  */
> +
> +static tree
> +maybe_get_constexpr_init (tree expr)
> +{
> +  tree decl = NULL_TREE;
> +  if (TREE_CODE (expr) == VAR_DECL)
> +    decl = expr;
> +  else if (TREE_CODE (expr) == COMPOUND_LITERAL_EXPR)
> +    decl = COMPOUND_LITERAL_EXPR_DECL (expr);
> +  if (decl
> +      && C_DECL_DECLARED_CONSTEXPR (decl)
> +      && DECL_INITIAL (decl) != NULL_TREE
> +      && !error_operand_p (DECL_INITIAL (decl)))
> +    return DECL_INITIAL (decl);
> +  if (TREE_CODE (expr) != COMPONENT_REF)
> +    return NULL_TREE;
> +  tree inner = maybe_get_constexpr_init (TREE_OPERAND (expr, 0));
> +  if (inner == NULL_TREE)
> +    return NULL_TREE;
> +  while ((CONVERT_EXPR_P (inner) || TREE_CODE (inner) == NON_LVALUE_EXPR)
> +     && !error_operand_p (inner)
> +     && (TYPE_MAIN_VARIANT (TREE_TYPE (inner))
> +         == TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (inner, 0)))))
> +    inner = TREE_OPERAND (inner, 0);
> +  if (TREE_CODE (inner) != CONSTRUCTOR)
> +    return NULL_TREE;
> +  tree field = TREE_OPERAND (expr, 1);
> +  unsigned HOST_WIDE_INT cidx;
> +  tree cfield, cvalue;
> +  bool have_other_init = false;
> +  FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (inner), cidx, cfield, cvalue)
> +    {
> +      if (cfield == field)
> +    return cvalue;
> +      have_other_init = true;
> +    }
> +  if (TREE_CODE (TREE_TYPE (inner)) == UNION_TYPE
> +      && (have_other_init || field != TYPE_FIELDS (TREE_TYPE (inner))))
> +    return NULL_TREE;
> +  /* Return a default initializer.  */
> +  if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (expr)))
> +    return build_constructor (TREE_TYPE (expr), NULL);
> +  return build_zero_cst (TREE_TYPE (expr));
> +}
> +
> /* Convert expression EXP (location LOC) from lvalue to rvalue,
>    including converting functions and arrays to pointers if CONVERT_P.
> -   If READ_P, also mark the expression as having been read.  */
> +   If READ_P, also mark the expression as having been read.  If
> +   FOR_INIT, constexpr expressions of structure and union type should
> +   be replaced by the corresponding CONSTRUCTOR; otherwise, only
> +   constexpr scalars (including elements of structures and unions) are
> +   replaced by their initializers.  */
> 
> struct c_expr
> convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
> -              bool convert_p, bool read_p)
> +              bool convert_p, bool read_p, bool for_init)
> {
> +  bool force_non_npc = false;
>   if (read_p)
>     mark_exp_read (exp.value);
>   if (convert_p)
>     exp = default_function_array_conversion (loc, exp);
>   if (!VOID_TYPE_P (TREE_TYPE (exp.value)))
>     exp.value = require_complete_type (loc, exp.value);
> +  if (for_init || !RECORD_OR_UNION_TYPE_P (TREE_TYPE (exp.value)))
> +    {
> +      tree init = maybe_get_constexpr_init (exp.value);
> +      if (init != NULL_TREE)
> +    {
> +      /* A named constant of pointer type or type nullptr_t is not
> +         a null pointer constant even if the initializer is
> +         one.  */
> +      if (TREE_CODE (init) == INTEGER_CST
> +          && !INTEGRAL_TYPE_P (TREE_TYPE (init))
> +          && integer_zerop (init))
> +        force_non_npc = true;
> +      exp.value = init;
> +    }
> +    }
>   if (really_atomic_lvalue (exp.value))
>     {
>       vec<tree, va_gc> *params;
> @@ -2187,6 +2260,8 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
>   if (convert_p && !error_operand_p (exp.value)
>       && (TREE_CODE (TREE_TYPE (exp.value)) != ARRAY_TYPE))
>     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
> +  if (force_non_npc)
> +    exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), exp.value);
>   return exp;
> }
> 
> @@ -6050,7 +6125,7 @@ build_c_cast (location_t loc, tree type, tree expr)
>      if (!maybe_const)
>        t = c_wrap_maybe_const (t, true);
>      t = digest_init (loc, type, t,
> -               NULL_TREE, false, true, 0);
> +               NULL_TREE, false, false, false, true, false, false);
>      TREE_CONSTANT (t) = TREE_CONSTANT (value);
>      return t;
>    }
> @@ -7851,6 +7926,8 @@ store_init_value (location_t init_loc, tree decl, tree init, tree origtype)
> {
>   tree value, type;
>   bool npc = false;
> +  bool int_const_expr = false;
> +  bool arith_const_expr = false;
> 
>   /* If variable's type was invalidly declared, just ignore it.  */
> 
> @@ -7861,9 +7938,19 @@ store_init_value (location_t init_loc, tree decl, tree init, tree origtype)
>   /* Digest the specified initializer into an expression.  */
> 
>   if (init)
> -    npc = null_pointer_constant_p (init);
> -  value = digest_init (init_loc, type, init, origtype, npc,
> -                     true, TREE_STATIC (decl));
> +    {
> +      npc = null_pointer_constant_p (init);
> +      int_const_expr = (TREE_CODE (init) == INTEGER_CST
> +            && !TREE_OVERFLOW (init)
> +            && INTEGRAL_TYPE_P (TREE_TYPE (init)));
> +      /* Not fully determined before folding.  */
> +      arith_const_expr = true;
> +    }
> +  bool constexpr_p = (TREE_CODE (decl) == VAR_DECL
> +              && C_DECL_DECLARED_CONSTEXPR (decl));
> +  value = digest_init (init_loc, type, init, origtype, npc, int_const_expr,
> +               arith_const_expr, true,
> +               TREE_STATIC (decl) || constexpr_p, constexpr_p);
> 
>   /* Store the expression if valid; else report error.  */
> 
> @@ -8033,12 +8120,151 @@ print_spelling (char *buffer)
>   return buffer;
> }
> 
> +/* Check whether INIT, a floating or integer constant, is
> +   representable in TYPE, a real floating type with the same radix.
> +   Return true if OK, false if not.  */
> +static bool
> +constexpr_init_fits_real_type (tree type, tree init)
> +{
> +  gcc_assert (TREE_CODE (type) == REAL_TYPE);
> +  gcc_assert (TREE_CODE (init) == INTEGER_CST || TREE_CODE (init) == REAL_CST);
> +  if (TREE_CODE (init) == REAL_CST
> +      && TYPE_MODE (TREE_TYPE (init)) == TYPE_MODE (type))
> +    /* Same mode, no conversion required.  */
> +    return true;
> +  if (TREE_CODE (init) == INTEGER_CST)
> +    {
> +      tree converted = build_real_from_int_cst (type, init);
> +      bool fail = false;
> +      wide_int w = real_to_integer (&TREE_REAL_CST (converted), &fail,
> +                    TYPE_PRECISION (TREE_TYPE (init)));
> +      return !fail && wi::eq_p (w, wi::to_wide (init));
> +    }
> +  /* exact_real_truncate is not quite right here, since it doesn't
> +     allow even an exact conversion to subnormal values.  */
> +  REAL_VALUE_TYPE t;
> +  real_convert (&t, TYPE_MODE (type), &TREE_REAL_CST (init));
> +  return real_identical (&t, &TREE_REAL_CST (init));
> +}
> +
> +/* Check whether INIT (location LOC) is valid as a 'constexpr'
> +   initializer for type TYPE, and give an error if not.  INIT has
> +   already been folded and verified to be constant.
> +   NULL_POINTER_CONSTANT, INT_CONST_EXPR and ARITH_CONST_EXPR say
> +   whether it is a null pointer constant, integer constant expression
> +   or arithmetic constant expression, respectively.  If TYPE is not a
> +   scalar type, this function does nothing.  */
> +
> +static void
> +check_constexpr_init (location_t loc, tree type, tree init,
> +              bool null_pointer_constant, bool int_const_expr,
> +              bool arith_const_expr)
> +{
> +  if (POINTER_TYPE_P (type))
> +    {
> +      /* The initializer must be a null pointer constant.  */
> +      if (!null_pointer_constant)
> +    error_at (loc, "%<constexpr%> pointer initializer is not a "
> +          "null pointer constant");
> +      return;
> +    }
> +  if (INTEGRAL_TYPE_P (type))
> +    {
> +      /* The initializer must be an integer constant expression,
> +     representable in the target type.  */
> +      if (!int_const_expr)
> +    error_at (loc, "%<constexpr%> integer initializer is not an "
> +          "integer constant expression");
> +      if (!int_fits_type_p (init, type))
> +    error_at (loc, "%<constexpr%> initializer not representable in "
> +          "type of object");
> +      return;
> +    }
> +  /* We don't apply any extra checks to extension types such as vector
> +     or fixed-point types.  */
> +  if (TREE_CODE (type) != REAL_TYPE && TREE_CODE (type) != COMPLEX_TYPE)
> +    return;
> +  if (!arith_const_expr)
> +    {
> +      error_at (loc, "%<constexpr%> initializer is not an arithmetic "
> +        "constant expression");
> +      return;
> +    }
> +  /* We don't apply any extra checks to complex integers.  */
> +  if (TREE_CODE (type) == COMPLEX_TYPE
> +      && TREE_CODE (TREE_TYPE (type)) != REAL_TYPE)
> +    return;
> +  /* Both the normative text and the relevant footnote are unclear, as
> +     of the C2x CD, about what exactly counts as a change of value in
> +     floating-point cases.  Here, we consider all conversions between
> +     binary and decimal types (even of infinities and NaNs, where
> +     quantum exponents are not involved) as involving a change of
> +     value, and likewise for conversions between real and complex
> +     types (even when the complex constant has imaginary part positive
> +     zero), and conversions of signaling NaN to a different machine
> +     mode.  But we allow exact conversions of integers to binary or
> +     decimal floating types, and exact conversions between different
> +     binary types or different decimal types, where "exact" in the
> +     decimal case requires the quantum exponent to be preserved.  */
> +  if (TREE_CODE (TREE_TYPE (init)) == COMPLEX_TYPE
> +      && TREE_CODE (type) == REAL_TYPE)
> +    {
> +      error_at (loc, "%<constexpr%> initializer for a real type is of "
> +        "complex type");
> +      return;
> +    }
> +  if (TREE_CODE (type) == COMPLEX_TYPE
> +      && TREE_CODE (TREE_TYPE (init)) != COMPLEX_TYPE)
> +    {
> +      error_at (loc, "%<constexpr%> initializer for a complex type is of "
> +        "real type");
> +      return;
> +    }
> +  if (TREE_CODE (type) == REAL_TYPE
> +      && TREE_CODE (TREE_TYPE (init)) == REAL_TYPE)
> +    {
> +      if (DECIMAL_FLOAT_TYPE_P (type)
> +      && !DECIMAL_FLOAT_TYPE_P (TREE_TYPE (init)))
> +    {
> +      error_at (loc, "%<constexpr%> initializer for a decimal "
> +            "floating-point type is of binary type");
> +      return;
> +    }
> +      else if (DECIMAL_FLOAT_TYPE_P (TREE_TYPE (init))
> +           && !DECIMAL_FLOAT_TYPE_P (type))
> +    {
> +      error_at (loc, "%<constexpr%> initializer for a binary "
> +            "floating-point type is of decimal type");
> +      return;
> +    }
> +    }
> +  bool fits;
> +  if (TREE_CODE (type) == COMPLEX_TYPE)
> +    {
> +      gcc_assert (TREE_CODE (init) == COMPLEX_CST);
> +      fits = (constexpr_init_fits_real_type (TREE_TYPE (type),
> +                         TREE_REALPART (init))
> +          && constexpr_init_fits_real_type (TREE_TYPE (type),
> +                        TREE_IMAGPART (init)));
> +    }
> +  else
> +    fits = constexpr_init_fits_real_type (type, init);
> +  if (!fits)
> +    error_at (loc, "%<constexpr%> initializer not representable in "
> +          "type of object");
> +}
> +
> /* Digest the parser output INIT as an initializer for type TYPE.
>    Return a C expression of type TYPE to represent the initial value.
> 
>    If ORIGTYPE is not NULL_TREE, it is the original type of INIT.
> 
> -   NULL_POINTER_CONSTANT is true if INIT is a null pointer constant.
> +   NULL_POINTER_CONSTANT is true if INIT is a null pointer constant,
> +   INT_CONST_EXPR is true if INIT is an integer constant expression,
> +   and ARITH_CONST_EXPR is true if INIT is, or might be, an arithmetic
> +   constant expression, false if it has already been determined in the
> +   caller that it is not (but folding may have made the value passed here
> +   indistinguishable from an arithmetic constant expression).
> 
>    If INIT is a string constant, STRICT_STRING is true if it is
>    unparenthesized or we should not warn here for it being parenthesized.
> @@ -8047,12 +8273,14 @@ print_spelling (char *buffer)
>    INIT_LOC is the location of the INIT.
> 
>    REQUIRE_CONSTANT requests an error if non-constant initializers or
> -   elements are seen.  */
> +   elements are seen.  REQUIRE_CONSTEXPR means the stricter requirements
> +   on initializers for 'constexpr' objects apply.  */
> 
> static tree
> digest_init (location_t init_loc, tree type, tree init, tree origtype,
> -             bool null_pointer_constant, bool strict_string,
> -         int require_constant)
> +             bool null_pointer_constant, bool int_const_expr,
> +         bool arith_const_expr, bool strict_string,
> +         bool require_constant, bool require_constexpr)
> {
>   enum tree_code code = TREE_CODE (type);
>   tree inside_init = init;
> @@ -8075,6 +8303,20 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype,
>    }
>       inside_init = c_fully_fold (inside_init, require_constant, &maybe_const);
>     }
> +  /* TODO: this may not detect all cases of expressions folding to
> +     constants that are not arithmetic constant expressions.  */
> +  if (!maybe_const)
> +    arith_const_expr = false;
> +  else if (!INTEGRAL_TYPE_P (TREE_TYPE (inside_init))
> +      && TREE_CODE (TREE_TYPE (inside_init)) != REAL_TYPE
> +      && TREE_CODE (TREE_TYPE (inside_init)) != COMPLEX_TYPE)
> +    arith_const_expr = false;
> +  else if (TREE_CODE (inside_init) != INTEGER_CST
> +      && TREE_CODE (inside_init) != REAL_CST
> +      && TREE_CODE (inside_init) != COMPLEX_CST)
> +    arith_const_expr = false;
> +  else if (TREE_OVERFLOW (inside_init))
> +    arith_const_expr = false;
> 
>   /* Initialization of an array of chars from a string constant
>      optionally enclosed in braces.  */
> @@ -8132,6 +8374,25 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype,
>          return error_mark_node;
>             }
> 
> +      if (require_constexpr
> +          && TYPE_UNSIGNED (typ1) != TYPE_UNSIGNED (typ2))
> +        {
> +          /* Check if all characters of the string can be
> +         represented in the type of the constexpr object being
> +         initialized.  */
> +          unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (inside_init);
> +          const unsigned char *p =
> +        (const unsigned char *) TREE_STRING_POINTER (inside_init);
> +          gcc_assert (CHAR_TYPE_SIZE == 8 && CHAR_BIT == 8);
> +          for (unsigned i = 0; i < len; i++)
> +        if (p[i] > 127)
> +          {
> +            error_init (init_loc, "%<constexpr%> initializer not "
> +                "representable in type of object");
> +            break;
> +          }
> +        }
> +
>      if (TYPE_DOMAIN (type) != NULL_TREE
>          && TYPE_SIZE (type) != NULL_TREE
>          && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST)
> @@ -8294,6 +8555,10 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype,
>       else if (require_constant && !maybe_const)
>    pedwarn_init (init_loc, OPT_Wpedantic,
>              "initializer element is not a constant expression");
> +      else if (require_constexpr)
> +    check_constexpr_init (init_loc, type, inside_init,
> +                  null_pointer_constant, int_const_expr,
> +                  arith_const_expr);
> 
>       /* Added to enable additional -Wsuggest-attribute=format warnings.  */
>       if (TREE_CODE (TREE_TYPE (inside_init)) == POINTER_TYPE)
> @@ -8312,6 +8577,7 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype,
>       || code == POINTER_TYPE || code == ENUMERAL_TYPE || code == BOOLEAN_TYPE
>       || code == COMPLEX_TYPE || code == VECTOR_TYPE)
>     {
> +      tree unconverted_init = inside_init;
>       if (TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE
>      && (TREE_CODE (init) == STRING_CST
>          || TREE_CODE (init) == COMPOUND_LITERAL_EXPR))
> @@ -8345,6 +8611,10 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype,
>       else if (require_constant && !maybe_const)
>    pedwarn_init (init_loc, OPT_Wpedantic,
>              "initializer element is not a constant expression");
> +      else if (require_constexpr)
> +    check_constexpr_init (init_loc, type, unconverted_init,
> +                  null_pointer_constant, int_const_expr,
> +                  arith_const_expr);
> 
>       return inside_init;
>     }
> @@ -8444,9 +8714,6 @@ static int constructor_depth;
>    such as (struct foo) {...}.  */
> static tree constructor_decl;
> 
> -/* Nonzero if this is an initializer for a top-level decl.  */
> -static int constructor_top_level;
> -
> /* Nonzero if there were any member designators in this initializer.  */
> static int constructor_designated;
> 
> @@ -8523,9 +8790,9 @@ struct initializer_stack
>   struct spelling *spelling;
>   struct spelling *spelling_base;
>   int spelling_size;
> -  char top_level;
>   char require_constant_value;
>   char require_constant_elements;
> +  char require_constexpr_value;
>   char designated;
>   rich_location *missing_brace_richloc;
> };
> @@ -8535,7 +8802,8 @@ static struct initializer_stack *initializer_stack;
> /* Prepare to parse and output the initializer for variable DECL.  */
> 
> void
> -start_init (tree decl, tree asmspec_tree ATTRIBUTE_UNUSED, int top_level,
> +start_init (tree decl, tree asmspec_tree ATTRIBUTE_UNUSED,
> +        bool init_require_constant, bool init_require_constexpr,
>        rich_location *richloc)
> {
>   const char *locus;
> @@ -8544,13 +8812,13 @@ start_init (tree decl, tree asmspec_tree ATTRIBUTE_UNUSED, int top_level,
>   p->decl = constructor_decl;
>   p->require_constant_value = require_constant_value;
>   p->require_constant_elements = require_constant_elements;
> +  p->require_constexpr_value = require_constexpr_value;
>   p->constructor_stack = constructor_stack;
>   p->constructor_range_stack = constructor_range_stack;
>   p->elements = constructor_elements;
>   p->spelling = spelling;
>   p->spelling_base = spelling_base;
>   p->spelling_size = spelling_size;
> -  p->top_level = constructor_top_level;
>   p->next = initializer_stack;
>   p->missing_brace_richloc = richloc;
>   p->designated = constructor_designated;
> @@ -8558,13 +8826,13 @@ start_init (tree decl, tree asmspec_tree ATTRIBUTE_UNUSED, int top_level,
> 
>   constructor_decl = decl;
>   constructor_designated = 0;
> -  constructor_top_level = top_level;
> 
> +  require_constant_value = init_require_constant;
> +  require_constexpr_value = init_require_constexpr;
>   if (decl != NULL_TREE && decl != error_mark_node)
>     {
> -      require_constant_value = TREE_STATIC (decl);
>       require_constant_elements
> -    = ((TREE_STATIC (decl) || (pedantic && !flag_isoc99))
> +    = ((init_require_constant || (pedantic && !flag_isoc99))
>       /* For a scalar, you can always use any value to initialize,
>          even within braces.  */
>       && AGGREGATE_TYPE_P (TREE_TYPE (decl)));
> @@ -8572,8 +8840,7 @@ start_init (tree decl, tree asmspec_tree ATTRIBUTE_UNUSED, int top_level,
>     }
>   else
>     {
> -      require_constant_value = 0;
> -      require_constant_elements = 0;
> +      require_constant_elements = false;
>       locus = _("(anonymous)");
>     }
> 
> @@ -8611,6 +8878,7 @@ finish_init (void)
>   constructor_decl = p->decl;
>   require_constant_value = p->require_constant_value;
>   require_constant_elements = p->require_constant_elements;
> +  require_constexpr_value = p->require_constexpr_value;
>   constructor_stack = p->constructor_stack;
>   constructor_designated = p->designated;
>   constructor_range_stack = p->constructor_range_stack;
> @@ -8618,7 +8886,6 @@ finish_init (void)
>   spelling = p->spelling;
>   spelling_base = p->spelling_base;
>   spelling_size = p->spelling_size;
> -  constructor_top_level = p->top_level;
>   initializer_stack = p->next;
>   XDELETE (p);
> }
> @@ -9096,6 +9363,10 @@ pop_init_level (location_t loc, int implicit,
>    {
>      if (constructor_erroneous || constructor_type == error_mark_node)
>        ret.value = error_mark_node;
> +      else if (TREE_CODE (constructor_type) == POINTER_TYPE)
> +        /* Ensure this is a null pointer constant in the case of a
> +           'constexpr' object initialized with {}.  */
> +        ret.value = build_zero_cst (ptr_type_node);
>      else
>        ret.value = build_zero_cst (constructor_type);
>    }
> @@ -9844,7 +10115,7 @@ output_init_element (location_t loc, tree value, tree origtype,
> {
>   tree semantic_type = NULL_TREE;
>   bool maybe_const = true;
> -  bool npc;
> +  bool npc, int_const_expr, arith_const_expr;
> 
>   if (type == error_mark_node || value == error_mark_node)
>     {
> @@ -9875,12 +10146,31 @@ output_init_element (location_t loc, tree value, tree origtype,
>     }
> 
>   npc = null_pointer_constant_p (value);
> +  int_const_expr = (TREE_CODE (value) == INTEGER_CST
> +            && !TREE_OVERFLOW (value)
> +            && INTEGRAL_TYPE_P (TREE_TYPE (value)));
> +  /* Not fully determined before folding.  */
> +  arith_const_expr = true;
>   if (TREE_CODE (value) == EXCESS_PRECISION_EXPR)
>     {
>       semantic_type = TREE_TYPE (value);
>       value = TREE_OPERAND (value, 0);
>     }
>   value = c_fully_fold (value, require_constant_value, &maybe_const);
> +  /* TODO: this may not detect all cases of expressions folding to
> +     constants that are not arithmetic constant expressions.  */
> +  if (!maybe_const)
> +    arith_const_expr = false;
> +  else if (!INTEGRAL_TYPE_P (TREE_TYPE (value))
> +      && TREE_CODE (TREE_TYPE (value)) != REAL_TYPE
> +      && TREE_CODE (TREE_TYPE (value)) != COMPLEX_TYPE)
> +    arith_const_expr = false;
> +  else if (TREE_CODE (value) != INTEGER_CST
> +      && TREE_CODE (value) != REAL_CST
> +      && TREE_CODE (value) != COMPLEX_CST)
> +    arith_const_expr = false;
> +  else if (TREE_OVERFLOW (value))
> +    arith_const_expr = false;
> 
>   if (value == error_mark_node)
>     constructor_erroneous = 1;
> @@ -9903,8 +10193,18 @@ output_init_element (location_t loc, tree value, tree origtype,
>   tree new_value = value;
>   if (semantic_type)
>     new_value = build1 (EXCESS_PRECISION_EXPR, semantic_type, value);
> -  new_value = digest_init (loc, type, new_value, origtype, npc, strict_string,
> -               require_constant_value);
> +  /* In the case of braces around a scalar initializer, the result of
> +     this initializer processing goes through digest_init again at the
> +     outer level.  In the case of a constexpr initializer for a
> +     pointer, avoid converting a null pointer constant to something
> +     that is not a null pointer constant to avoid a spurious error
> +     from that second processing.  */
> +  if (!require_constexpr_value
> +      || !npc
> +      || TREE_CODE (constructor_type) != POINTER_TYPE)
> +    new_value = digest_init (loc, type, new_value, origtype, npc,
> +                 int_const_expr, arith_const_expr, strict_string,
> +                 require_constant_value, require_constexpr_value);
>   if (new_value == error_mark_node)
>     {
>       constructor_erroneous = 1;
> @@ -9929,6 +10229,11 @@ output_init_element (location_t loc, tree value, tree origtype,
>       && (require_constant_value || require_constant_elements))
>     pedwarn_init (loc, OPT_Wpedantic,
>          "initializer element is not a constant expression");
> +  /* digest_init has already carried out the additional checks
> +     required for 'constexpr' initializers (using the information
> +     passed to it about whether the original initializer was certain
> +     kinds of constant expression), so that check does not need to be
> +     repeated here.  */
> 
>   /* Issue -Wc++-compat warnings about initializing a bitfield with
>      enum type.  */
> diff --git a/gcc/dfp.cc b/gcc/dfp.cc
> index 7c1db7d4ebf..084ceb70a7d 100644
> --- a/gcc/dfp.cc
> +++ b/gcc/dfp.cc
> @@ -364,6 +364,12 @@ decimal_from_binary (REAL_VALUE_TYPE *to, const REAL_VALUE_TYPE *from)
>   /* We convert to string, then to decNumber then to decimal128.  */
>   real_to_decimal (string, from, sizeof (string), 0, 1);
>   decimal_real_from_string (to, string);
> +  /* When a canonical NaN is originally created, it is not marked as
> +     decimal.  Ensure the result of converting to another decimal type
> +     (which passes through this function) is also marked as
> +     canonical.  */
> +  if (from->cl == rvc_nan && from->canonical)
> +    to->canonical = 1;
> }
> 
> /* Helper function to real.cc:do_compare() to handle decimal internal
> diff --git a/gcc/testsuite/gcc.dg/c11-keywords-1.c b/gcc/testsuite/gcc.dg/c11-keywords-1.c
> index 974ccfc75ca..997c1b0aff0 100644
> --- a/gcc/testsuite/gcc.dg/c11-keywords-1.c
> +++ b/gcc/testsuite/gcc.dg/c11-keywords-1.c
> @@ -5,6 +5,7 @@
> int alignas;
> int alignof;
> int bool;
> +int constexpr;
> int false;
> int true;
> int static_assert;
> diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-1.c b/gcc/testsuite/gcc.dg/c2x-constexpr-1.c
> new file mode 100644
> index 00000000000..f7f64e2d300
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-constexpr-1.c
> @@ -0,0 +1,312 @@
> +/* Test C2x constexpr.  Valid code, compilation tests.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors" } */
> +
> +#include <float.h>
> +
> +constexpr int v1 = 1;
> +static_assert (v1 == 1);
> +extern typeof (v1) *pci;
> +extern const int *pci;
> +extern typeof (&(constexpr int) {}) pci;
> +/* Redeclaring a constexpr object is OK (although it can't be declared before
> +   the definition without undefined behavior).  */
> +extern const int v1;
> +static_assert (v1 == 1);
> +unsigned int constexpr v2 = 2;
> +static_assert (v2 == 2);
> +extern typeof (v2) *pcui;
> +extern const unsigned int *pcui;
> +static constexpr char v3 = 3;
> +static_assert (v3 == 3);
> +extern typeof (v3) *pcc;
> +extern const char *pcc;
> +constexpr void *v4 = 0;
> +extern typeof (v4) *pcpv;
> +extern void *const *pcpv;
> +constexpr int *v5 = nullptr;
> +extern typeof (v5) *pcpi;
> +extern int *const *pcpi;
> +constexpr double v6 = 3.5;
> +extern typeof (v6) *pcd;
> +extern const double *pcd;
> +auto constexpr v7 = 1.0;
> +extern typeof (v7) *pcd;
> +constexpr auto v8 = 1.5f;
> +extern typeof (v8) *pcf;
> +extern const float *pcf;
> +constexpr static long v9 = 2ULL;
> +static_assert (v9 == 2);
> +extern typeof (v9) *pcl;
> +extern const long *pcl;
> +const short *v10 = &(constexpr short) { 10 };
> +/* Qualifiers that aren't permitted on a constexpr object itself are OK in a
> +   pointer target.  */
> +constexpr volatile int *v11 = nullptr;
> +extern typeof (v11) *pcpvi;
> +extern volatile int *const *pcpvi;
> +constexpr _Atomic int *v12 = nullptr;
> +extern typeof (v12) *pcpai;
> +extern _Atomic int *const *pcpai;
> +constexpr int *restrict *v13 = nullptr;
> +extern typeof (v13) cprpi;
> +extern int *restrict *const cprpi;
> +typedef int *P;
> +constexpr restrict P *v14 = nullptr;
> +extern typeof (v14) cprpi;
> +struct s15 { volatile int a; _Atomic int b; int *restrict p; };
> +constexpr struct s15 *v16 = nullptr;
> +constexpr char v17[3] = { 1, 2, 3 };
> +struct s18 { int a; int *b; double c; };
> +constexpr struct s18 v19 = { 12345ULL, 0, 19.0L };
> +static_assert (v19.a == 12345);
> +union u20 { int a; float b; };
> +constexpr union u20 v21 = { 1 };
> +static_assert (v21.a == 1);
> +constexpr union u20 v22 = { .b = 23.0 };
> +constexpr float v23 = (float) (1.0f / 3.0f);
> +constexpr double v24 = (double) (1.0 / 3.0);
> +constexpr struct s18 v25 = { 0, 0, (double) (1.0 / 3.0) };
> +static_assert (v25.a == 0);
> +constexpr char v26[] = "abc\xfe";
> +constexpr unsigned char v27[] = u8"xyz\xff";
> +constexpr unsigned char v28[] = "\x12\x7f";
> +constexpr signed char v29[] = "\x34\x66";
> +constexpr double v30 = (int) (double) 3.0 - (long) (double) 2.0;
> +constexpr int v31 = 1 + 2 + (int) 3.0;
> +static_assert (v31 == 6);
> +constexpr typeof (nullptr) v32 = nullptr;
> +constexpr _Complex double v33 = __builtin_complex (1.0f, 3.0f / 2.0f);
> +constexpr float v34 = 1234.0L;
> +constexpr char v35 = 127ULL;
> +#if FLT_MIN_EXP == -125 && FLT_MANT_DIG == 24
> +constexpr float v36 = 0x1p-149;
> +constexpr float _Complex v37 = __builtin_complex (0x1p-149, 0x1p127);
> +constexpr float v38 = 0xffffffUL;
> +constexpr float v39 = -0xffffffL;
> +constexpr float v40 = 0xffffff0L;
> +constexpr float v41 = 1ULL << 63;
> +#endif
> +#if DBL_MIN_EXP == -1021 && DBL_MANT_DIG == 53
> +constexpr double v42 = 0x1p-1074L;
> +constexpr _Complex double v43 = __builtin_complex (0x1p1023L, 0x1p-1074L);
> +constexpr double v44 = 0x1fffffffffffffULL;
> +constexpr double v45 = -0x1fffffffffffffLL;
> +constexpr double v46 = 0x3ffffffffffffeULL;
> +constexpr double v47 = 1ULL << 63;
> +#endif
> +constexpr void *v48 = (void *) 0;
> +constexpr int *v49 = (void *) 0L;
> +constexpr long *v50 = 0LL;
> +constexpr int v51 = {};
> +static_assert (v51 == 0);
> +constexpr float v52 = {};
> +constexpr long double v53 = {};
> +constexpr int *v54 = {};
> +constexpr void *v55 = {};
> +constexpr typeof (nullptr) v56 = {};
> +struct s57 { int *p; };
> +union u58 { int *p; };
> +constexpr int *v59 = 0;
> +constexpr int *v60 = { 0 };
> +constexpr struct s57 v61 = { 0 };
> +constexpr struct s57 v62 = { { } }; /* { dg-warning "braces around scalar initializer" } */
> +constexpr struct s57 v63 = { { 0 } }; /* { dg-warning "braces around scalar initializer" } */
> +constexpr union u58 v64 = { 0 };
> +constexpr union u58 v65 = { { } }; /* { dg-warning "braces around scalar initializer" } */
> +constexpr union u58 v66 = { { 0 } }; /* { dg-warning "braces around scalar initializer" } */
> +struct s67 { int a; float b; void *c; int *d; typeof (nullptr) e; int f; int g[2]; };
> +struct s68 { struct s67 x; };
> +union u69 { int a; float b; void *c; int *d; struct s68 e; };
> +struct s70 { union u69 x; };
> +constexpr struct s67 v71 = { 1, 2.0, 0, 0, nullptr, 7, { 3, 4 } };
> +static_assert (v71.a == 1);
> +static_assert (v71.f == 7);
> +constexpr struct s67 v72 = v71;
> +static_assert (v72.a == 1);
> +static_assert (v72.f == 7);
> +extern const struct s67 v71;
> +constexpr auto v73 = v71;
> +static_assert (v73.a == 1);
> +static_assert (v73.f == 7);
> +auto v74 = v71;
> +constexpr struct s68 v75 = { v72 };
> +static_assert (v75.x.a == 1);
> +static_assert (v75.x.f == 7);
> +constexpr union u69 v76 = { };
> +static_assert (v76.a == 0);
> +constexpr union u69 v77 = { .e = v75 };
> +static_assert (v77.e.x.a == 1);
> +static_assert (v77.e.x.f == 7);
> +constexpr union u69 v78 = { .a = 1 };
> +static_assert (v78.a == 1);
> +constexpr union u69 v79 = { .e = { v72 } };
> +static_assert (v79.e.x.a == 1);
> +static_assert (v79.e.x.f == 7);
> +enum e80 { E80 = v79.e.x.f };
> +static_assert (E80 == 7);
> +constexpr struct s70 v81 = { v79 };
> +static_assert (v81.x.e.x.f == 7);
> +constexpr struct s68 v82 = { (constexpr struct s67) { 5, 6, 0, 0, nullptr, 9, { 1, 2 } } };
> +static_assert (v82.x.a == 5);
> +static_assert (v82.x.f == 9);
> +constexpr auto v83 = (constexpr int) { (constexpr int) { 0 } };
> +/* These are null pointers but not null pointer constants.  */
> +constexpr typeof (nullptr) v84 = nullptr;
> +constexpr void *v85 = 0;
> +int *v86 = v85;
> +int *v87 = v84;
> +typeof (1 ? v85 : (int *) 0) v88;
> +extern void *v88;
> +typeof (1 ? (void *) 0 : (int *) 0) v89;
> +extern int *v89;
> +constexpr struct s68 v90 = { };
> +static_assert (v90.x.a == 0);
> +static_assert (v90.x.f == 0);
> +constexpr int v91 = { 123 };
> +static_assert (v91 == 123);
> +constexpr int v92 = { v91 };
> +static_assert (v92 == 123);
> +/* Verify that constexpr values can be used in various contexts requiring
> +   (integer) constant expressions.  */
> +struct s93 { int x : v79.e.x.f; };
> +constexpr int v94 = alignof (int);
> +alignas (v94) int v95;
> +constexpr int v97[100] = { [v82.x.f] = 7 };
> +static int v98[v94];
> +
> +void
> +f0 ()
> +{
> +  constexpr int fv0 = 3;
> +  static_assert (fv0 == 3);
> +  auto constexpr int fv1 = 4;
> +  static_assert (fv1 == 4);
> +  register constexpr float fv2 = 1.0;
> +  constexpr auto int fv3 = 123;
> +  static_assert (fv3 == 123);
> +  constexpr register void *fv4 = (void *) 0;
> +  const int *fv5 = &(constexpr int) { 234 };
> +  const int *fv6 = &(constexpr static int) { 234 };
> +  const int *fv7 = &(static constexpr int) { 234 };
> +  typeof ((constexpr register int) { 234 }) *fv8;
> +  typeof ((register constexpr int) { 234 }) *fv9;
> +  int fv10 = (constexpr int) { 1 } + sizeof (struct fs *);
> +  constexpr auto fv11 = (constexpr int) { (constexpr int) { 0 } };
> +  static_assert (fv11 == 0);
> +  constexpr char fv12[3] = { 1, 2, 3 };
> +  (constexpr short [4]) { 9, 8, 7, -6 };
> +  constexpr struct s18 fv13 = { 1234ULL, 0, 13.0f };
> +  (constexpr struct s18) { 123, (void *) 0, 11 };
> +  constexpr union u20 fv14 = { 2 };
> +  (constexpr union u20) { 5 };
> +  constexpr union u20 fv15 = { .b = 15.0 };
> +  (constexpr union u20) { .b = 20 };
> +  (constexpr float) { (float) (1.0f / 3.0f) };
> +  (constexpr double) { (double) (1.0 / 3.0) };
> +  (constexpr struct s18) { 0, 0, (double) (1.0 / 3.0) };
> +  (constexpr char []) { "abc\xfe" };
> +  (constexpr unsigned char []) { u8"xyz\xff" };
> +  (constexpr unsigned char []) { "\x12\x7f" };
> +  (constexpr signed char []) { "\x34\x66" };
> +  (constexpr double) { (int) (double) 3.0 - (long) (double) 2.0 };
> +  (constexpr int) { 1 + 2 + (int) 3.0 };
> +  (constexpr typeof (nullptr)) { nullptr };
> +  (constexpr _Complex double) { __builtin_complex (1.0f, 3.0f / 2.0f) };
> +  (constexpr float) { 1234.0L };
> +  (constexpr char) { 127ULL };
> +#if FLT_MIN_EXP == -125 && FLT_MANT_DIG == 24
> +  (constexpr float) { 0x1p-149 };
> +  (constexpr float _Complex) { __builtin_complex (0x1p-149, 0x1p127) };
> +  (constexpr float) { 0xffffffUL };
> +  (constexpr float) { -0xffffffL };
> +  (constexpr float) { 0xffffff0L };
> +  (constexpr float) { 1ULL << 63 };
> +#endif
> +#if DBL_MIN_EXP == -1021 && DBL_MANT_DIG == 53
> +  (constexpr double) { 0x1p-1074L };
> +  (constexpr _Complex double) { __builtin_complex (0x1p1023L, 0x1p-1074L) };
> +  (constexpr double) { 0x1fffffffffffffULL };
> +  (constexpr double) { -0x1fffffffffffffLL };
> +  (constexpr double) { 0x3ffffffffffffeULL };
> +  (constexpr double) { 1ULL << 63 };
> +#endif
> +  (constexpr void *) { (void *) 0 };
> +  (constexpr int *) { (void *) 0L };
> +  (constexpr long *) { 0LL };
> +  (constexpr int) {};
> +  (constexpr float) {};
> +  (constexpr long double) {};
> +  (constexpr int *) {};
> +  (constexpr void *) {};
> +  (constexpr typeof (nullptr)) {};
> +  (constexpr int *) { 0 };
> +  (constexpr struct s57) { 0 };
> +  (constexpr struct s57) { { } }; /* { dg-warning "braces around scalar initializer" } */
> +  (constexpr struct s57) { { 0 } }; /* { dg-warning "braces around scalar initializer" } */
> +  (constexpr union u58) { 0 };
> +  (constexpr union u58) { { } }; /* { dg-warning "braces around scalar initializer" } */
> +  (constexpr union u58) { { 0 } }; /* { dg-warning "braces around scalar initializer" } */
> +  /* It's not entirely clear if constexpr declarations are allowed in this
> +     position in a for loop; presume they are, as implicitly auto just as if no
> +     storage class specifiers were used.  */
> +  for (constexpr int fv16 = 1;;)
> +    break;
> +  constexpr struct s67 fv17 = { 1, 2.0, 0, 0, nullptr, 7, { 3, 4 } };
> +  static_assert (fv17.a == 1);
> +  static_assert (fv17.f == 7);
> +  constexpr struct s67 fv18 = fv17;
> +  static_assert (fv18.a == 1);
> +  static_assert (fv18.f == 7);
> +  constexpr auto fv19 = fv17;
> +  static_assert (fv19.a == 1);
> +  static_assert (fv19.f == 7);
> +  auto fv20 = fv17;
> +  constexpr struct s68 fv21 = { fv18 };
> +  static_assert (fv21.x.a == 1);
> +  static_assert (fv21.x.f == 7);
> +  constexpr union u69 fv22 = { };
> +  static_assert (fv22.a == 0);
> +  constexpr union u69 fv23 = { .e = fv21 };
> +  static_assert (fv23.e.x.a == 1);
> +  static_assert (fv23.e.x.f == 7);
> +  constexpr union u69 fv24 = { .a = 1 };
> +  static_assert (fv24.a == 1);
> +  constexpr union u69 fv25 = { .e = { fv18 } };
> +  static_assert (fv25.e.x.a == 1);
> +  static_assert (fv25.e.x.f == 7);
> +  enum fe80 { FE80 = fv25.e.x.f };
> +  static_assert (FE80 == 7);
> +  constexpr struct s70 fv26 = { fv25 };
> +  static_assert (fv26.x.e.x.f == 7);
> +  constexpr struct s68 fv27 = { (constexpr struct s67) { 5, 6, 0, 0, nullptr, 9, { 1, 2 } } };
> +  static_assert (fv27.x.a == 5);
> +  static_assert (fv27.x.f == 9);
> +  constexpr struct s68 fv28 = { };
> +  static_assert (fv28.x.a == 0);
> +  static_assert (fv28.x.f == 0);
> +  constexpr int fv29 = { 123 };
> +  static_assert (fv29 == 123);
> +  constexpr int fv30 = { fv29 };
> +  static_assert (fv30 == 123);
> +  static_assert ((constexpr struct s67) { 1, 2.0, 0, 0, nullptr, 7, { 3, 4 } }.f == 7);
> +  static_assert ((constexpr struct s68) { fv18 }.x.a == 1);
> +  static_assert ((constexpr union u69) { }.a == 0);
> +  static_assert ((constexpr union u69) { .e = fv21 }.e.x.f == 7);
> +  static_assert ((constexpr union u69) { .a = 1 }.a == 1);
> +  static_assert ((constexpr union u69) { .e = { fv18 } }.e.x.a == 1);
> +  static_assert ((constexpr struct s70) { fv25 }.x.e.x.f == 7);
> +  static_assert ((constexpr struct s68) { (constexpr struct s67) { 5, 6, 0, 0, nullptr, 9, { 1, 2 } } }.x.f == 9);
> +  static_assert ((constexpr struct s68) { }.x.f == 0);
> +  /* Verify that constexpr values can be used in various contexts requiring
> +     (integer) constant expressions.  */
> +  struct fs93 { int x : fv25.e.x.f; };
> +  constexpr int fv31 = alignof (int);
> +  alignas (fv31) int fv32;
> +  constexpr int fv33[100] = { [fv27.x.f] = 7 };
> +  static int fv34[fv31];
> +  switch (fv0)
> +    {
> +    case fv27.x.f: ;
> +    }
> +}
> diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-2a.c b/gcc/testsuite/gcc.dg/c2x-constexpr-2a.c
> new file mode 100644
> index 00000000000..f74e2ec53bb
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-constexpr-2a.c
> @@ -0,0 +1,37 @@
> +/* Test C2x constexpr.  Valid code, execution test.  */
> +/* { dg-do link } */
> +/* { dg-options "-std=c2x -pedantic-errors" } */
> +/* { dg-additional-sources "c2x-constexpr-2b.c" } */
> +
> +extern void abort (void);
> +extern void exit (int);
> +
> +/* constexpr objects at file scope have internal linkage.  */
> +constexpr int a = 2;
> +
> +struct s { int a; float b; int c[3]; };
> +constexpr struct s s1 = { 2, 3, { 4, 5, 6 } };
> +constexpr struct s s2 = s1;
> +struct s s3 = s2;
> +
> +void
> +check (const struct s *p)
> +{
> +  if (p->a != 2 || p->b != 3 || p->c[0] != 4 || p->c[1] != 5 || p->c[2] != 6)
> +    abort ();
> +}
> +
> +int
> +main ()
> +{
> +  constexpr struct s s4 = s1;
> +  struct s s5 = s4;
> +  constexpr struct s s6 = { s1.a, s2.b, { 4, 5, 6 } };
> +  check (&s1);
> +  check (&s2);
> +  check (&s3);
> +  check (&s4);
> +  check (&s5);
> +  check (&s6);
> +  exit (0);
> +}
> diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-2b.c b/gcc/testsuite/gcc.dg/c2x-constexpr-2b.c
> new file mode 100644
> index 00000000000..04058b3f559
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-constexpr-2b.c
> @@ -0,0 +1,6 @@
> +/* Test C2x constexpr.  Second file for link test.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors" } */
> +
> +/* constexpr objects at file scope have internal linkage.  */
> +constexpr int a = 3;
> diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-3.c b/gcc/testsuite/gcc.dg/c2x-constexpr-3.c
> new file mode 100644
> index 00000000000..16e56db2835
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-constexpr-3.c
> @@ -0,0 +1,228 @@
> +/* Test C2x constexpr.  Invalid code.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors" } */
> +
> +extern constexpr int v0 = 0; /* { dg-error "'constexpr' used with 'extern'" } */
> +/* { dg-warning "initialized and declared 'extern'" "initialized extern" { target *-*-* } .-1 } */
> +constexpr extern int v1 = 0; /* { dg-error "'constexpr' used with 'extern'" } */
> +/* { dg-warning "initialized and declared 'extern'" "initialized extern" { target *-*-* } .-1 } */
> +typedef constexpr int v2; /* { dg-error "'constexpr' used with 'typedef'" } */
> +constexpr typedef int v3; /* { dg-error "'constexpr' used with 'typedef'" } */
> +thread_local constexpr int v4 = 0; /* { dg-error "'constexpr' used with '_Thread_local'" } */
> +constexpr thread_local int v5 = 0; /* { dg-error "'thread_local' used with 'constexpr'" } */
> +constexpr constexpr int v6 = 1; /* { dg-error "duplicate 'constexpr'" } */
> +constexpr struct v7; /* { dg-error "'constexpr' in empty declaration" } */
> +/* { dg-error "'struct v7' declared in underspecified object declaration" "underspecified" { target *-*-* } .-1 } */
> +constexpr union v8; /* { dg-error "'constexpr' in empty declaration" } */
> +/* { dg-error "'union v8' declared in underspecified object declaration" "underspecified" { target *-*-* } .-1 } */
> +constexpr struct v9 { int a; }; /* { dg-error "'constexpr' in empty declaration" } */
> +/* { dg-error "'struct v9' defined in underspecified object declaration" "underspecified" { target *-*-* } .-1 } */
> +constexpr union v10 { int a; }; /* { dg-error "'constexpr' in empty declaration" } */
> +/* { dg-error "'union v10' defined in underspecified object declaration" "underspecified" { target *-*-* } .-1 } */
> +constexpr; /* { dg-error "'constexpr' in empty declaration" } */
> +constexpr int; /* { dg-error "empty declaration" } */
> +constexpr const; /* { dg-error "empty declaration" } */
> +constexpr int v11; /* { dg-error "initialized data declaration" } */
> +constexpr int v12 { } /* { dg-error "initialized data declaration" } */
> +constexpr int v13 = 1, v14 = 2; /* { dg-error "single declarator" } */
> +constexpr int v15 = sizeof (struct v16 *); /* { dg-error "declared in underspecified object initializer" } */
> +constexpr int v17 = sizeof (union v18 *); /* { dg-error "declared in underspecified object initializer" } */
> +constexpr int v19 = sizeof (struct v20 { int a; }); /* { dg-error "defined in underspecified object initializer" } */
> +constexpr int v21 = sizeof (struct { int a; }); /* { dg-error "defined in underspecified object initializer" } */
> +constexpr int v22 = sizeof (union v23 { int a; }); /* { dg-error "defined in underspecified object initializer" } */
> +constexpr int v24 = sizeof (union { int a; }); /* { dg-error "defined in underspecified object initializer" } */
> +constexpr int v25 = sizeof (enum v26 { A }); /* { dg-error "defined in underspecified object initializer" } */
> +/* The following case is undefined behavior (so doesn't actually require a
> +   diagnostic).  */
> +constexpr int v27 = sizeof (enum { B }); /* { dg-error "defined in underspecified object initializer" } */
> +/* Examples with a forward declaration, then definition inside constexpr.  */
> +struct v28;
> +constexpr int v29 = sizeof (struct v28 { int a; }); /* { dg-error "defined in underspecified object initializer" } */
> +union v30;
> +constexpr int v31 = sizeof (union v30 { int a; }); /* { dg-error "defined in underspecified object initializer" } */
> +constexpr int v32 = sizeof (v32); /* { dg-error "underspecified 'v32' referenced in its initializer" } */
> +static const int v33;
> +constexpr const int v33 = 1; /* { dg-error "underspecified declaration of 'v33', which is already declared in this scope" } */
> +constexpr void v34 () {} /* { dg-error "'constexpr' requires an initialized data declaration" } */
> +void v35 (constexpr int v36); /* { dg-error "storage class specified for parameter 'v36'" } */
> +void v37 (constexpr short); /* { dg-error "storage class specified for unnamed parameter" } */
> +void v38 (constexpr register int v39); /* { dg-error "storage class specified for parameter 'v39'" } */
> +void v40 (constexpr register short); /* { dg-error "storage class specified for unnamed parameter" } */
> +/* The following case is undefined behavior (presumably to allow for possible
> +   future support for constexpr functions), but should clearly be diagnosed
> +   when such functions aren't actually supported.  */
> +constexpr int v41 (); /* { dg-error "'constexpr' requires an initialized data declaration" } */
> +typedef volatile long t42;
> +typedef int *restrict t43;
> +typedef _Atomic int t44;
> +struct t45 { struct { struct { t42 a[2]; } a; } a; };
> +struct t46 { struct { struct { int z; int *restrict a; } a[3]; } a; };
> +struct t47 { short x; struct { struct { _Atomic long a; } a; } a[4][5]; };
> +constexpr t42 v48 = {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +constexpr t43 v49 = {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +constexpr t44 v50 = {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +constexpr volatile double v51 = {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +constexpr int *restrict v52 = {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +constexpr _Atomic (short) v53 = {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +constexpr long *volatile v54 = {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +constexpr struct t45 v55 = {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +constexpr struct t46 v56 = {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +constexpr struct t47 v57 = {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +union t58 { struct { union { t42 a[1]; } a; } a; };
> +union t59 { struct { union { int z; int *restrict a; } a; } a; };
> +union t60 { short x; union { struct { _Atomic long a; } a[3]; } a; };
> +constexpr union t58 v61 = {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +constexpr union t59 v62 = {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +constexpr union t60 v63 = {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +constexpr t42 v64[1][2][3] = {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +constexpr volatile int v65[1][2][3] = {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +constexpr struct t45 v66[2][2][4] = {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +constexpr union t60 v67[2][2][4] = {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +int v68 = 0;
> +constexpr int v69 = v68; /* { dg-error "initializer element is not constant" } */
> +double exp (double);
> +constexpr double v70 = exp (0); /* { dg-error "initializer element is not a constant expression" } */
> +struct s71 { int a; double b; };
> +constexpr struct s71 v72 = { 0, exp (0) }; /* { dg-error "initializer element is not a constant expression" } */
> +/* { dg-error "'constexpr' initializer is not an arithmetic constant expression" "arithmetic" { target *-*-* } .-1 } */
> +constexpr struct s71 v73 = { v68, 0 }; /* { dg-error "initializer element is not constant" } */
> +union u74 { int a; double b; };
> +constexpr union u74 v75 = { v68 }; /* { dg-error "initializer element is not constant" } */
> +constexpr union u74 v76 = { .b = exp (0) }; /* { dg-error "initializer element is not a constant expression" } */
> +/* { dg-error "'constexpr' initializer is not an arithmetic constant expression" "arithmetic" { target *-*-* } .-1 } */
> +constexpr struct s77 *v77 = 0; /* { dg-error "'struct s77' declared in underspecified object declaration" } */
> +constexpr union u78 *v78 = 0; /* { dg-error "'union u78' declared in underspecified object declaration" } */
> +constexpr struct s79 { int a; } v79 = { 0 }; /* { dg-error "'struct s79' defined in underspecified object declaration" } */
> +constexpr union u80 { int a; } v80 = { 0 }; /* { dg-error "'union u80' defined in underspecified object declaration" } */
> +constexpr enum e81 { E81 } v81 = E81; /* { dg-error "'enum e81' defined in underspecified object declaration" } */
> +constexpr enum { E82 } v82 = E82; /* { dg-error "defined in underspecified object declaration" } */
> +struct s83 constexpr *v83 = 0; /* { dg-error "'struct s83' declared in underspecified object declaration" } */
> +union u84 constexpr *v84 = 0; /* { dg-error "'union u84' declared in underspecified object declaration" } */
> +struct s85 { int a; } constexpr v85 = { 0 }; /* { dg-error "'struct s85' defined in underspecified object declaration" } */
> +union u86 { int a; } constexpr v86 = { 0 }; /* { dg-error "'union u86' defined in underspecified object declaration" } */
> +enum e87 { E87 } constexpr v87 = E87; /* { dg-error "'enum e87' defined in underspecified object declaration" } */
> +enum { E88 } constexpr v88 = E88; /* { dg-error "defined in underspecified object declaration" } */
> +constexpr int *v89 = (int *) 0; /* { dg-error "'constexpr' pointer initializer is not a null pointer constant" } */
> +constexpr void *v90 = (void *) (void *) 0; /* { dg-error "'constexpr' pointer initializer is not a null pointer constant" } */
> +constexpr int v91 = (int) (double) 1.0; /* { dg-error "constexpr' integer initializer is not an integer constant expression" } */
> +constexpr struct s71 v92 = { (int) (double) 1.0, 0 }; /* { dg-error "constexpr' integer initializer is not an integer constant expression" } */
> +struct s93 { void *p; };
> +constexpr struct s93 v94 = { (int *) 0 }; /* { dg-error "'constexpr' pointer initializer is not a null pointer constant" } */
> +constexpr int v95 = (unsigned int) -1; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr unsigned char v96 = -1; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr signed char v97 = 1234567LL; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +/* { dg-warning "overflow in conversion" "overflow warning" { target *-*-* } .-1 } */
> +/* Disallow all real/complex conversions (the C2x CD is unclear about
> +   real-to-complex and about complex-to-real with imaginary part positive 0, if
> +   the real parts can be exactly represented in the relevant types).  */
> +constexpr double v98 = __builtin_complex (1.0, 0.0); /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
> +constexpr double v99 = __builtin_complex (1.0, 1.0); /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
> +constexpr double v100 = __builtin_complex (1.0, -0.0); /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
> +constexpr _Complex double v101 = 1.0; /* { dg-error "'constexpr' initializer for a complex type is of real type" } */
> +constexpr float v102 = (unsigned long long) -1; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr double v103 = (unsigned long long) -1; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr float v104 = __LONG_LONG_MAX__; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr double v105 = __LONG_LONG_MAX__; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr signed char v106[] = u8"\xff"; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +/* Only the initialized (possibly by default) element of a constexpr union is a
> +   named constant.  */
> +union u107 { int a; int b; };
> +constexpr union u107 v108 = { };
> +constexpr union u107 v109 = { .a = 0 };
> +constexpr union u107 v110 = { .b = 0 };
> +constexpr int v111 = v108.b; /* { dg-error "initializer" } */
> +constexpr int v112 = v109.b; /* { dg-error "initializer" } */
> +constexpr int v113 = v110.a; /* { dg-error "initializer" } */
> +/* A reference to an array in a constexpr object is converted to a pointer as
> +   usual, so in particular is not equivalent to directly using a string literal
> +   initializer extracted from the initializer of that object.  */
> +struct s114 { char c[10]; };
> +constexpr struct s114 v115 = { "abc" };
> +constexpr struct s114 v116 = { v115.c }; /* { dg-error "initializer" } */
> +/* { dg-error "integer from pointer" "conversion" { target *-*-* } .-1 } */
> +
> +void
> +f0 ()
> +{
> +  (constexpr constexpr int) { 1 }; /* { dg-error "duplicate 'constexpr'" } */
> +  (constexpr thread_local int) { 1 }; /* { dg-error "'thread_local' used with 'constexpr'" } */
> +  (thread_local constexpr static int) { 1 }; /* { dg-error "'constexpr' used with '_Thread_local'" } */
> +  (constexpr int) { sizeof (struct fs1 *) }; /* { dg-error "declared in underspecified object initializer" } */
> +  (constexpr int) { sizeof (union fs2 *) }; /* { dg-error "declared in underspecified object initializer" } */
> +  (constexpr int) { sizeof (struct fs3 { int a; }) }; /* { dg-error "defined in underspecified object initializer" } */
> +  (constexpr int) { sizeof (struct { int a; }) }; /* { dg-error "defined in underspecified object initializer" } */
> +  (constexpr int) { sizeof (union fs4 { int a; }) }; /* { dg-error "defined in underspecified object initializer" } */
> +  (constexpr int) { sizeof (union { int a; }) }; /* { dg-error "defined in underspecified object initializer" } */
> +  (constexpr int) { sizeof (enum fs5 { A }) }; /* { dg-error "defined in underspecified object initializer" } */
> +  /* The following case is undefined behavior (so doesn't actually require a
> +     diagnostic).  */
> +  (constexpr int) { sizeof (enum { B }) }; /* { dg-error "defined in underspecified object initializer" } */
> +  /* Examples with a forward declaration, then definition inside constexpr.  */
> +  struct fs6;
> +  (constexpr int) { sizeof (struct fs6 { int a; }) }; /* { dg-error "defined in underspecified object initializer" } */
> +  union fs7;
> +  (constexpr int) { sizeof (union fs7 { int a; }) }; /* { dg-error "defined in underspecified object initializer" } */
> +  constexpr int fv32 = sizeof (fv32); /* { dg-error "underspecified 'fv32' referenced in its initializer" } */
> +  /* Test entering then exiting nested underspecified initializers.  */
> +  constexpr int x = (constexpr int) { 1 } + sizeof (struct fs8 *); /* { dg-error "declared in underspecified object initializer" } */
> +  auto y = (constexpr int) { 1 } + sizeof (struct fs9 *); /* { dg-error "declared in underspecified object initializer" } */
> +  extern const int z; /* { dg-message "previous declaration" } */
> +  constexpr const int z = 1; /* { dg-error "underspecified declaration of 'z', which is already declared in this scope" } */
> +  /* { dg-error "declaration of 'z' with no linkage follows extern declaration" "linkage error" { target *-*-* } .-1 } */
> +  int non_const = 1;
> +  typedef int VLA[non_const];
> +  constexpr VLA *pnc = nullptr; /* { dg-error "'constexpr' object has variably modified type" } */
> +  (constexpr t42) {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +  (constexpr t43) {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +  (constexpr t44) {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +  (constexpr volatile double) {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +  (constexpr int *restrict) {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +  (constexpr _Atomic (short)) {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +  (constexpr long *volatile) {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +  (constexpr struct t45) {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +  (constexpr struct t46) {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +  (constexpr struct t47) {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +  (constexpr union t58) {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +  (constexpr union t59) {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +  (constexpr union t60) {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +  (constexpr t42 [1][2][3]) {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +  (constexpr volatile int [1][2][3]) {}; /* { dg-error "invalid qualifiers for 'constexpr' object" } */
> +  (constexpr struct t45 [2][2][4]) {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +  (constexpr union t60 [2][2][4]) {}; /* { dg-error "invalid qualifiers for field of 'constexpr' object" } */
> +  (constexpr int) { v68 }; /* { dg-error "initializer element is not constant" } */
> +  (constexpr double) { exp (0) }; /* { dg-error "initializer element is not a constant expression" } */
> +  /* { dg-error "'constexpr' initializer is not an arithmetic constant expression" "arithmetic" { target *-*-* } .-1 } */
> +  (constexpr struct s71) { 0, exp (0) }; /* { dg-error "initializer element is not a constant expression" } */
> +  /* { dg-error "'constexpr' initializer is not an arithmetic constant expression" "arithmetic" { target *-*-* } .-1 } */
> +  (constexpr struct s71) { v68, 0 }; /* { dg-error "initializer element is not constant" } */
> +  (constexpr union u74) { v68 }; /* { dg-error "initializer element is not constant" } */
> +  (constexpr union u74) { .b = exp (0) }; /* { dg-error "initializer element is not a constant expression" } */
> +  /* { dg-error "'constexpr' initializer is not an arithmetic constant expression" "arithmetic" { target *-*-* } .-1 } */
> +  (constexpr struct fs10 *) { 0 }; /* { dg-error "declared in 'constexpr' compound literal" } */
> +  (constexpr union fs11 *) { 0 }; /* { dg-error "declared in 'constexpr' compound literal" } */
> +  (constexpr struct fs12 { int a; }) { 0 }; /* { dg-error "defined in 'constexpr' compound literal" } */
> +  (constexpr union fs13 { int a; }) { 0 }; /* { dg-error "defined in 'constexpr' compound literal" } */
> +  (constexpr enum fs14 { FS14 }) { FS14 }; /* { dg-error "defined in 'constexpr' compound literal" } */
> +  (constexpr enum { FS15 }) { FS15 }; /* { dg-error "defined in 'constexpr' compound literal" } */
> +  (constexpr int *) { (int *) 0 }; /* { dg-error "'constexpr' pointer initializer is not a null pointer constant" } */
> +  (constexpr void *) { (void *) (void *) 0 }; /* { dg-error "'constexpr' pointer initializer is not a null pointer constant" } */
> +  (constexpr int) { (int) (double) 1.0 }; /* { dg-error "constexpr' integer initializer is not an integer constant expression" } */
> +  (constexpr struct s71) { (int) (double) 1.0, 0 }; /* { dg-error "constexpr' integer initializer is not an integer constant expression" } */
> +  (constexpr struct s93) { (int *) 0 }; /* { dg-error "'constexpr' pointer initializer is not a null pointer constant" } */
> +  (constexpr int) { (unsigned int) -1 }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr unsigned char) { -1 }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr signed char) { 1234567LL }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  /* { dg-warning "overflow in conversion" "overflow warning" { target *-*-* } .-1 } */
> +  (constexpr double) { __builtin_complex (1.0, 0.0) }; /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
> +  (constexpr double) { __builtin_complex (1.0, 1.0) }; /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
> +  (constexpr double) { __builtin_complex (1.0, -0.0) }; /* { dg-error "'constexpr' initializer for a real type is of complex type" } */
> +  (constexpr _Complex double) { 1.0 }; /* { dg-error "'constexpr' initializer for a complex type is of real type" } */
> +  (constexpr float) { (unsigned long long) -1 }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr double) { (unsigned long long) -1 }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr float) { __LONG_LONG_MAX__ }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr double) { __LONG_LONG_MAX__ }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr signed char []) { u8"\xff" }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  constexpr typeof (nullptr) not_npc = nullptr;
> +  int *ptr = 0;
> +  (void) (ptr == not_npc); /* { dg-error "invalid operands" } */
> +}
> diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-4.c b/gcc/testsuite/gcc.dg/c2x-constexpr-4.c
> new file mode 100644
> index 00000000000..2a42af890a2
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-constexpr-4.c
> @@ -0,0 +1,21 @@
> +/* Test C2x constexpr.  Valid code, compilation tests, signed char.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors -fsigned-char" } */
> +
> +constexpr char v1[] = "\x00\xff";
> +constexpr signed char v2[] = "\x7f\x80";
> +constexpr unsigned char v3[] = "\x00\x7f";
> +constexpr char v4[] = u8"\x00\x7f";
> +constexpr signed char v5[] = u8"\x7f\x00";
> +constexpr unsigned char v6[] = u8"\x00\xff";
> +
> +void
> +f0 ()
> +{
> +  (constexpr char []) { "\x00\xff" };
> +  (constexpr signed char []) { "\x7f\x80" };
> +  (constexpr unsigned char []) { "\x00\x7f" };
> +  (constexpr char []) { u8"\x00\x7f" };
> +  (constexpr signed char []) { u8"\x7f\x00" };
> +  (constexpr unsigned char []) { u8"\x00\xff" };
> +}
> diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-5.c b/gcc/testsuite/gcc.dg/c2x-constexpr-5.c
> new file mode 100644
> index 00000000000..6febd2ee67f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-constexpr-5.c
> @@ -0,0 +1,21 @@
> +/* Test C2x constexpr.  Valid code, compilation tests, unsigned char.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors -funsigned-char" } */
> +
> +constexpr char v1[] = "\x00\xff";
> +constexpr signed char v2[] = "\x7f\x00";
> +constexpr unsigned char v3[] = "\x80\x7f";
> +constexpr char v4[] = u8"\x00\xff";
> +constexpr signed char v5[] = u8"\x7f\x00";
> +constexpr unsigned char v6[] = u8"\x00\xff";
> +
> +void
> +f0 ()
> +{
> +  (constexpr char []) { "\x00\xff" };
> +  (constexpr signed char []) { "\x7f\x00" };
> +  (constexpr unsigned char []) { "\x80\x7f" };
> +  (constexpr char []) { u8"\x00\xff" };
> +  (constexpr signed char []) { u8"\x7f\x00" };
> +  (constexpr unsigned char []) { u8"\x00\xff" };
> +}
> diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-6.c b/gcc/testsuite/gcc.dg/c2x-constexpr-6.c
> new file mode 100644
> index 00000000000..a86124a9974
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-constexpr-6.c
> @@ -0,0 +1,15 @@
> +/* Test C2x constexpr.  Invalid code, compilation tests, signed char.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors -fsigned-char" } */
> +
> +constexpr unsigned char v3[] = "\x00\xff"; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr char v4[] = u8"\x00\xff"; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr signed char v5[] = u8"\x00\xff"; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +
> +void
> +f0 ()
> +{
> +  (constexpr unsigned char []) { "\x00\xff" }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr char []) { u8"\x00\xff" }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr signed char []) { u8"\x00\xff" }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +}
> diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-7.c b/gcc/testsuite/gcc.dg/c2x-constexpr-7.c
> new file mode 100644
> index 00000000000..5282d923182
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-constexpr-7.c
> @@ -0,0 +1,13 @@
> +/* Test C2x constexpr.  Invalid code, compilation tests, unsigned char.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors -funsigned-char" } */
> +
> +constexpr signed char v2[] = "\x00\xff"; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr signed char v5[] = u8"\x00\xff"; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +
> +void
> +f0 ()
> +{
> +  (constexpr signed char []) { "\x00\xff" }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr signed char []) { u8"\x00\xff" }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +}
> diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-8.c b/gcc/testsuite/gcc.dg/c2x-constexpr-8.c
> new file mode 100644
> index 00000000000..c7119c97a69
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-constexpr-8.c
> @@ -0,0 +1,23 @@
> +/* Test C2x constexpr.  Valid code, compilation tests, IEEE arithmetic.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors" } */
> +/* { dg-add-options ieee } */
> +/* { dg-require-effective-target inff } */
> +
> +constexpr float fi = __builtin_inf ();
> +constexpr double di = __builtin_inff ();
> +constexpr float fn = __builtin_nan ("");
> +constexpr double dn = __builtin_nanf ("");
> +constexpr float fns = __builtin_nansf ("");
> +constexpr double dns = __builtin_nans ("");
> +
> +void
> +f0 (void)
> +{
> +  (constexpr float) { __builtin_inf () };
> +  (constexpr double) { __builtin_inff () };
> +  (constexpr float) { __builtin_nan ("") };
> +  (constexpr double) { __builtin_nanf ("") };
> +  (constexpr float) { __builtin_nansf ("") };
> +  (constexpr double) { __builtin_nans ("") };
> +}
> diff --git a/gcc/testsuite/gcc.dg/c2x-constexpr-9.c b/gcc/testsuite/gcc.dg/c2x-constexpr-9.c
> new file mode 100644
> index 00000000000..c62fc738fa0
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-constexpr-9.c
> @@ -0,0 +1,39 @@
> +/* Test C2x constexpr.  Invalid code, compilation tests, IEEE arithmetic.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors" } */
> +/* { dg-add-options ieee } */
> +/* { dg-require-effective-target inff } */
> +
> +/* A conversion from signaling NaN to quiet NaN in a different format is not
> +   valid for constexpr.  */
> +constexpr float fns = __builtin_nans (""); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr double dns = __builtin_nansf (""); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +
> +/* Test out-of-range values.  */
> +constexpr float fu = __DBL_MIN__; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr float fo = __DBL_MAX__; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr float fp = 0x1.ffffffp0; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +
> +constexpr _Complex float cfur = __builtin_complex (__DBL_MIN__, 0.0); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Complex float cfor = __builtin_complex (__DBL_MAX__, 0.0); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Complex float cfpr = __builtin_complex (0x1.ffffffp0, 0.0); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +
> +constexpr _Complex float cfui = __builtin_complex (0.0, __DBL_MIN__); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Complex float cfoi = __builtin_complex (0.0, __DBL_MAX__); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Complex float cfpi = __builtin_complex (0.0, 0x1.ffffffp0); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +
> +void
> +f0 ()
> +{
> +  (constexpr float) { __builtin_nans ("") }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr double) { __builtin_nansf ("") }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr float) { __DBL_MIN__ }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr float) { __DBL_MAX__ }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr float) { 0x1.ffffffp0 }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Complex float) { __builtin_complex (__DBL_MIN__, 0.0) }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Complex float) { __builtin_complex (__DBL_MAX__, 0.0) }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Complex float) { __builtin_complex (0x1.ffffffp0, 0.0) }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Complex float) { __builtin_complex (0.0, __DBL_MIN__) }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Complex float) { __builtin_complex (0.0, __DBL_MAX__) }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Complex float) { __builtin_complex (0.0, 0x1.ffffffp0) }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +}
> diff --git a/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-1.c b/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-1.c
> new file mode 100644
> index 00000000000..568f1428b40
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-1.c
> @@ -0,0 +1,79 @@
> +/* Test C2x constexpr.  Valid code, compilation tests, DFP.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors" } */
> +
> +constexpr _Decimal32 v1 = __DEC32_MIN__;
> +constexpr _Decimal32 v2 = __DEC32_SUBNORMAL_MIN__;
> +constexpr _Decimal32 v3 = -__DEC32_MAX__;
> +constexpr _Decimal64 v4 = __DEC32_MIN__;
> +constexpr _Decimal64 v5 = __DEC32_SUBNORMAL_MIN__;
> +constexpr _Decimal64 v6 = -__DEC32_MAX__;
> +constexpr _Decimal64 v7 = __DEC64_MIN__;
> +constexpr _Decimal64 v8 = __DEC64_SUBNORMAL_MIN__;
> +constexpr _Decimal64 v9 = -__DEC64_MAX__;
> +constexpr _Decimal128 v10 = __DEC32_MIN__;
> +constexpr _Decimal128 v11 = __DEC32_SUBNORMAL_MIN__;
> +constexpr _Decimal128 v12 = -__DEC32_MAX__;
> +constexpr _Decimal128 v13 = __DEC64_MIN__;
> +constexpr _Decimal128 v14 = __DEC64_SUBNORMAL_MIN__;
> +constexpr _Decimal128 v15 = -__DEC64_MAX__;
> +constexpr _Decimal128 v16 = __DEC128_MIN__;
> +constexpr _Decimal128 v17 = __DEC128_SUBNORMAL_MIN__;
> +constexpr _Decimal128 v18 = -__DEC128_MAX__;
> +constexpr _Decimal32 v19 = 1234567L;
> +constexpr _Decimal32 v20 = -123456700000LL;
> +constexpr _Decimal64 v21 = 1234567890123456LL;
> +constexpr _Decimal64 v22 = -123456789012345600LL;
> +constexpr _Decimal128 v23 = (unsigned long long) -1;
> +constexpr _Decimal32 v24 = 1e-101DL;
> +constexpr _Decimal64 v25 = 1e-398DL;
> +constexpr _Decimal32 v26 = __builtin_infd128 ();
> +constexpr _Decimal128 v27 = __builtin_infd32 ();
> +constexpr _Decimal64 v28 = __builtin_nand128 ("");
> +constexpr _Decimal128 v29 = __builtin_nand32 ("");
> +constexpr _Decimal32 v30 = __builtin_nansd32 ("");
> +constexpr _Decimal64 v31 = __builtin_nansd64 ("");
> +constexpr _Decimal128 v32 = __builtin_nansd128 ("");
> +constexpr _Decimal32 v33 = {};
> +constexpr _Decimal64 v34 = {};
> +constexpr _Decimal128 v35 = {};
> +
> +void
> +f0 ()
> +{
> +  (constexpr _Decimal32) { __DEC32_MIN__ };
> +  (constexpr _Decimal32) { __DEC32_SUBNORMAL_MIN__ };
> +  (constexpr _Decimal32) { -__DEC32_MAX__ };
> +  (constexpr _Decimal64) { __DEC32_MIN__ };
> +  (constexpr _Decimal64) { __DEC32_SUBNORMAL_MIN__ };
> +  (constexpr _Decimal64) { -__DEC32_MAX__ };
> +  (constexpr _Decimal64) { __DEC64_MIN__ };
> +  (constexpr _Decimal64) { __DEC64_SUBNORMAL_MIN__ };
> +  (constexpr _Decimal64) { -__DEC64_MAX__ };
> +  (constexpr _Decimal128) { __DEC32_MIN__ };
> +  (constexpr _Decimal128) { __DEC32_SUBNORMAL_MIN__ };
> +  (constexpr _Decimal128) { -__DEC32_MAX__ };
> +  (constexpr _Decimal128) { __DEC64_MIN__ };
> +  (constexpr _Decimal128) { __DEC64_SUBNORMAL_MIN__ };
> +  (constexpr _Decimal128) { -__DEC64_MAX__ };
> +  (constexpr _Decimal128) { __DEC128_MIN__ };
> +  (constexpr _Decimal128) { __DEC128_SUBNORMAL_MIN__ };
> +  (constexpr _Decimal128) { -__DEC128_MAX__ };
> +  (constexpr _Decimal32) { 1234567L };
> +  (constexpr _Decimal32) { -123456700000LL };
> +  (constexpr _Decimal64) { 1234567890123456LL };
> +  (constexpr _Decimal64) { -123456789012345600LL };
> +  (constexpr _Decimal128) { (unsigned long long) -1 };
> +  (constexpr _Decimal32) { 1e-101DL };
> +  (constexpr _Decimal64) { 1e-398DL };
> +  (constexpr _Decimal32) { __builtin_infd128 () };
> +  (constexpr _Decimal128) { __builtin_infd32 () };
> +  (constexpr _Decimal64) { __builtin_nand128 ("") };
> +  (constexpr _Decimal128) { __builtin_nand32 ("") };
> +  (constexpr _Decimal32) { __builtin_nansd32 ("") };
> +  (constexpr _Decimal64) { __builtin_nansd64 ("") };
> +  (constexpr _Decimal128) { __builtin_nansd128 ("") };
> +  (constexpr _Decimal32) {};
> +  (constexpr _Decimal64) {};
> +  (constexpr _Decimal128) {};
> +}
> diff --git a/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-2.c b/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-2.c
> new file mode 100644
> index 00000000000..8b1ecf23908
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-2.c
> @@ -0,0 +1,48 @@
> +/* Test C2x constexpr.  Invalid code, compilation tests, DFP.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors" } */
> +
> +/* Test conversions between binary and decimal.  */
> +constexpr _Decimal32 v1 = 0.0; /* { dg-error "'constexpr' initializer for a decimal floating-point type is of binary type" } */
> +constexpr double v2 = 0.0DF; /* { dg-error "'constexpr' initializer for a binary floating-point type is of decimal type" } */
> +
> +/* A conversion from signaling NaN to quiet NaN in a different format is not
> +   valid for constexpr.  */
> +constexpr _Decimal32 v3 = __builtin_nansd64 (""); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Decimal32 v4 = __builtin_nansd128 (""); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Decimal64 v5 = __builtin_nansd32 (""); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Decimal64 v6 = __builtin_nansd128 (""); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Decimal128 v7 = __builtin_nansd32 (""); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Decimal128 v8 = __builtin_nansd64 (""); /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +
> +/* Test out-of-range values, including integers.  */
> +constexpr _Decimal32 v9 = 12345678; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Decimal64 v10 = (unsigned long long) -1; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Decimal32 v11 = __DEC64_MIN__; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Decimal64 v12 = -__DEC128_MAX__; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Decimal64 v13 = 12345678901234567890.DL; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +
> +/* Test cases where the value can be represented, but the quantum exponent
> +   cannot.  */
> +constexpr _Decimal32 v14 = 0e-200DD; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr _Decimal32 v15 = 0e200DL; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +
> +void
> +f0 ()
> +{
> +  (constexpr _Decimal32) { 0.0 }; /* { dg-error "'constexpr' initializer for a decimal floating-point type is of binary type" } */
> +  (constexpr double) { 0.0DF }; /* { dg-error "'constexpr' initializer for a binary floating-point type is of decimal type" } */
> +  (constexpr _Decimal32) { __builtin_nansd64 ("") }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal32) { __builtin_nansd128 ("") }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal64) { __builtin_nansd32 ("") }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal64) { __builtin_nansd128 ("") }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal128) { __builtin_nansd32 ("") }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal128) { __builtin_nansd64 ("") }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal32) { 12345678 }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal64) { (unsigned long long) -1 }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal32) { __DEC64_MIN__ }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal64) { -__DEC128_MAX__ }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal64) { 12345678901234567890.DL }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal32) { 0e-200DD }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +  (constexpr _Decimal32) { 0e200DL }; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +}
> diff --git a/gcc/testsuite/gcc.dg/gnu2x-constexpr-1.c b/gcc/testsuite/gcc.dg/gnu2x-constexpr-1.c
> new file mode 100644
> index 00000000000..6078f0807e3
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/gnu2x-constexpr-1.c
> @@ -0,0 +1,17 @@
> +/* Test C2x constexpr.  Valid code using GNU extensions, compilation tests.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=gnu2x" } */
> +
> +struct s { struct { int x, y; } x; };
> +constexpr struct s v = { { 123, 150 } };
> +int k;
> +constexpr int a[200] = { [v.x.x ... v.x.y] = 7 };
> +
> +void
> +f ()
> +{
> +  switch (k)
> +    {
> +    case v.x.x ... v.x.y: ;
> +    }
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-11.c b/gcc/testsuite/gcc.target/i386/excess-precision-11.c
> new file mode 100644
> index 00000000000..b83ecaec6e9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/excess-precision-11.c
> @@ -0,0 +1,8 @@
> +/* Test C2x constexpr.  Valid code, compilation tests, excess precision.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors -mfpmath=387 -fexcess-precision=standard" } */
> +
> +constexpr long double ld = 1.0 / 3.0;
> +constexpr long double ld2 = 1.1;
> +constexpr double d = (double) (1.0 / 3.0);
> +constexpr double d2 = (double) 1.1;
> diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-12.c b/gcc/testsuite/gcc.target/i386/excess-precision-12.c
> new file mode 100644
> index 00000000000..b44f0b5bf76
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/excess-precision-12.c
> @@ -0,0 +1,6 @@
> +/* Test C2x constexpr.  Invalid code, compilation tests, excess precision.  */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors -mfpmath=387 -fexcess-precision=standard" } */
> +
> +constexpr double d = 1.0 / 3.0; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> +constexpr double d2 = 1.1; /* { dg-error "'constexpr' initializer not representable in type of object" } */
> 
> -- 
> Joseph S. Myers
> joseph@codesourcery.com

      reply	other threads:[~2022-11-12 16:13 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-12  4:55 Joseph Myers
2022-11-12 16:12 ` Richard Biener [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=B8C19784-BAA8-4F91-900D-63BF29AE0FFE@gmail.com \
    --to=richard.guenther@gmail.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=joseph@codesourcery.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).