c: C2x constexpr

Message ID 2dd14b67-f6f-6b30-46a0-8166a1e515c9@codesourcery.com
State New
Headers
Series c: C2x constexpr |

Commit Message

Joseph Myers Nov. 12, 2022, 4:55 a.m. UTC
  [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)?

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.
  

Comments

Richard Biener Nov. 12, 2022, 4:12 p.m. UTC | #1
> 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
  

Patch

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" } */