> Am 12.11.2022 um 05:56 schrieb Joseph Myers <jos...@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
> jos...@codesourcery.com