aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/c-family/c-common.cc2
-rw-r--r--gcc/c/c-decl.cc153
-rw-r--r--gcc/c/c-parser.cc223
-rw-r--r--gcc/c/c-tree.h17
-rw-r--r--gcc/c/c-typeck.cc361
-rw-r--r--gcc/dfp.cc6
-rw-r--r--gcc/testsuite/gcc.dg/c11-keywords-1.c1
-rw-r--r--gcc/testsuite/gcc.dg/c2x-constexpr-1.c312
-rw-r--r--gcc/testsuite/gcc.dg/c2x-constexpr-2a.c37
-rw-r--r--gcc/testsuite/gcc.dg/c2x-constexpr-2b.c6
-rw-r--r--gcc/testsuite/gcc.dg/c2x-constexpr-3.c228
-rw-r--r--gcc/testsuite/gcc.dg/c2x-constexpr-4.c21
-rw-r--r--gcc/testsuite/gcc.dg/c2x-constexpr-5.c21
-rw-r--r--gcc/testsuite/gcc.dg/c2x-constexpr-6.c15
-rw-r--r--gcc/testsuite/gcc.dg/c2x-constexpr-7.c13
-rw-r--r--gcc/testsuite/gcc.dg/c2x-constexpr-8.c23
-rw-r--r--gcc/testsuite/gcc.dg/c2x-constexpr-9.c39
-rw-r--r--gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-1.c79
-rw-r--r--gcc/testsuite/gcc.dg/dfp/c2x-constexpr-dfp-2.c48
-rw-r--r--gcc/testsuite/gcc.dg/gnu2x-constexpr-1.c17
-rw-r--r--gcc/testsuite/gcc.target/i386/excess-precision-11.c8
-rw-r--r--gcc/testsuite/gcc.target/i386/excess-precision-12.c6
22 files changed, 1539 insertions, 97 deletions
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 5890c18..71507d4 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 a99b745..36de778 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 d70697b..1d144bb 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 8116e5c..c287124 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 6360984..e06f052 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 7c1db7d..084ceb7 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 974ccfc..997c1b0 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 0000000..f7f64e2
--- /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 0000000..f74e2ec
--- /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 0000000..04058b3
--- /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 0000000..16e56db
--- /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 0000000..2a42af8
--- /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 0000000..6febd2e
--- /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 0000000..a86124a
--- /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 0000000..5282d92
--- /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 0000000..c7119c9
--- /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 0000000..c62fc73
--- /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 0000000..568f142
--- /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 0000000..8b1ecf2
--- /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 0000000..6078f08
--- /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 0000000..b83ecae
--- /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 0000000..b44f0b5
--- /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" } */