diff options
author | Joseph Myers <joseph@codesourcery.com> | 2022-11-12 17:03:21 +0000 |
---|---|---|
committer | Joseph Myers <joseph@codesourcery.com> | 2022-11-12 17:03:21 +0000 |
commit | b556d1773db7174c71c466d9b3cafc25c7d6c825 (patch) | |
tree | b8d88b114a9e5eb1e143544124c477da19aaa5f9 /gcc/c | |
parent | f232715d15618e91c90eb210e23de10909590944 (diff) | |
download | gcc-b556d1773db7174c71c466d9b3cafc25c7d6c825.zip gcc-b556d1773db7174c71c466d9b3cafc25c7d6c825.tar.gz gcc-b556d1773db7174c71c466d9b3cafc25c7d6c825.tar.bz2 |
c: C2x constexpr
Implement C2x constexpr (a feature based on the C++ one but much more
minimal, with only constexpr variables, not functions).
I believe this implementation is fully functional for use of this
feature. However, there are several things that seem unclear about
the specification that I'll need to raise in NB comments. There are
also areas where there may be followup bug fixes because the
implementation doesn't reject some more obscure cases that ought to be
rejected: cases where a constexpr initializer for floating type meets
the constraints for a constant expression in initializers but not
those for an arithmetic constant expression (previously we haven't had
to track whether something is an arithmetic constant expression in
detail, unlike with integer constant expressions), and some cases
where a tag or struct or union member gets declared indirectly in the
declaration specifiers or declarator of a constexpr declaration, which
is not permitted (modulo lack of clarity in the specification) for
underspecified declarations in general (the cases of a declaration in
the initializer, or a tagged type being directly declared as a type
specifier, are already detected).
Cases of ambiguity in the specification include:
* Many questions (previously raised in WG14 discussions) over the rule
about what conversions do or do not involve a change of value that's
not allowed in a constexpr initializer, that aren't properly
addressed by the normative text (and where the footnote on the
subject isn't very clear either, and the examples don't necessarily
follow from the normative text). I've made a series of choices
there, that include disallowing all conversions between real and
complex types or between binary and decimal floating types in
constexpr initializers, that might not necessarily agree with how
things end up getting clarified.
The dfp.cc change also arises here, to allow quiet NaN initializers
of one DFP type to be used in a constexpr initializer for another
DFP type (as is possible for signaling NaNs) by ensuring the result
of such a conversion is properly marked as canonical (note that most
of the DFP code doesn't actually do anything with NaN payloads at
all).
* Various issues with what exactly counts as part of a declaration for
the purposes of the rule on underspecified declarations not
declaring any identifiers other than ordinary identifiers (and not
declaring more than one ordinary identifier, though the latter is
undefined behavior). These include cases where the declaration of a
struct / union / enum type appears inside typeof or alignas in the
declaration specifiers (the latter also applies with auto), or in
the declarator (e.g. an array size or in a parameter declaration).
The issues are similar to those involved in C90 DR#115 and C99 DRs
#277 and #341; the intent may not be the same in all the different
cases involved, but it's not clear that the normative wording in the
various places is sufficient to deduce the differences in intent.
* The wording about producing a compound literal constant using member
access is present in one place but another place only applies that
to named constants.
* It's not clear when a structure or union constant (a constexpr
variable or compound literal with structure or union type, or a
member with such type extracted by a series of member access
operations) can itself be used in an initializer (constexpr or
otherwise). Based on general wording for initializers not having
been changed, the working draft might only strictly allow it at
automatic storage duration (but elsewhere it would be undefined
behavior, not a constraint violation, so no diagnostic required) -
since that's the only case mentioned where a single expression of
structure or union type can be used to initialize an object of such
a type. But it definitely seems to be allowed in even constexpr
initializers at automatic storage duration - and since generally
constexpr initializers (any storage duration) are *more* constrained
than ordinary static storage duration initializers, it would seem
odd for it not to be allowed at static storage duration.
* When you do allow such initializers, it's then not entirely clear
how the constraint that constexpr pointer initializers must be null
pointer constants should be applied (given that a constexpr object
of pointer type is a null pointer but *not* a null pointer
constant). My guess would be that a constexpr struct or union
containing such a field should still be allowed as an initializer,
but the wording could be read otherwise.
* It also becomes important with constexpr exactly what kind of
constant expression an implicit zero initializer is; the wording for
default initialization only really deals with the value of the
initializer and not what kind of constant it is. In particular,
this affects whether {} is a valid constexpr initializer for a
pointer not of type void *, since the wording only talks about a
null pointer, not whether it's a null pointer *constant*. I assumed
that it should be a null pointer constant in that case.
* It's also not entirely clear whether constexpr can be used in the
declaration part of a for loop (which "shall only declare
identifiers for objects having storage class auto or register"). I
interpreted it as allowed (treating such objects as implicitly auto
just like those with no storage class specifiers), but it could also
be argued that constexpr is another storage class specifier and so
not allowed there.
Bootstrapped with no regressions for x86_64-pc-linux-gnu.
gcc/
* dfp.cc (decimal_from_binary): Convert a canonical NaN to a
canonical NaN.
gcc/c-family/
* c-common.cc (c_common_reswords): Use D_C2X instead of D_CXXONLY.
gcc/c/
* c-decl.cc (start_underspecified_init)
(finish_underspecified_init): Handle name == NULL_TREE for
compound literals.
(merge_decls): Merge C_DECL_DECLARED_CONSTEXPR.
(shadow_tag_warned): Check for constexpr.
(start_decl): Add parameter do_push.
(build_compound_literal): Set C_DECL_DECLARED_CONSTEXPR.
(grokdeclarator): Handle constexpr.
(finish_struct): Set C_TYPE_FIELDS_NON_CONSTEXPR.
(declspecs_add_scspec): Handle constexpr.
* c-parser.cc (c_token_starts_compound_literal)
(c_token_starts_declspecs, c_parser_declaration_or_fndef)
(c_parser_declspecs, c_parser_gnu_attribute_any_word)
(c_parser_compound_literal_scspecs)
(c_parser_postfix_expression_after_paren_type): Handle constexpr.
Update calls to start_init.
(c_parser_declaration_or_fndef, c_parser_initializer)
(c_parser_initval): Pass true for new argument of
convert_lvalue_to_rvalue. Call convert_lvalue_to_rvalue for
constexpr compound literals.
(c_parser_static_assert_declaration_no_semi)
(c_parser_enum_specifier, c_parser_struct_declaration)
(c_parser_alignas_specifier, c_parser_initelt, c_parser_label):
Call convert_lvalue_to_rvalue on expressions required to be
integer constant expressions.
(c_parser_omp_declare_reduction): Update call to start_init.
* c-tree.h (C_TYPE_FIELDS_NON_CONSTEXPR)
(C_DECL_DECLARED_CONSTEXPR): New macros.
(struct c_declspecs): Add constexpr_p.
(start_decl, convert_lvalue_to_rvalue, start_init): Update
prototypes.
* c-typeck.cc (require_constant_value, require_constant_elements):
Change to bool.
(require_constexpr_value, maybe_get_constexpr_init)
(constexpr_init_fits_real_type, check_constexpr_init): New.
(convert_lvalue_to_rvalue): Add new parameter for_init. Call
maybe_get_constexpr_init.
(store_init_value): Update call to digest_init.
(digest_init): Add parameters int_const_expr, arith_const_expr and
require_constexpr. Check constexpr initializers.
(constructor_top_level): Remove.
(struct initializer_stack): Remove top_level. Add
require_constexpr_value.
(start_init): Remove parameter top_level. Add parameters
init_require_constant and init_require_constexpr. Save
require_constexpr_value on stack.
(pop_init_level): Use a null pointer constant for zero initializer
of pointer initialized with {}.
(output_init_element): Update call to digest_init. Avoid passing
null pointer constants of pointer type through digest_init a
second time when initializing a constexpr object.
gcc/testsuite/
* gcc.dg/c11-keywords-1.c: Also test constexpr.
* gcc.dg/c2x-constexpr-1.c, gcc.dg/c2x-constexpr-2a.c,
gcc.dg/c2x-constexpr-2b.c, gcc.dg/c2x-constexpr-3.c,
gcc.dg/c2x-constexpr-4.c, gcc.dg/c2x-constexpr-5.c,
gcc.dg/c2x-constexpr-6.c, gcc.dg/c2x-constexpr-7.c,
gcc.dg/c2x-constexpr-8.c, gcc.dg/c2x-constexpr-9.c,
gcc.dg/dfp/c2x-constexpr-dfp-1.c,
gcc.dg/dfp/c2x-constexpr-dfp-2.c, gcc.dg/gnu2x-constexpr-1.c,
gcc.target/i386/excess-precision-11.c,
gcc.target/i386/excess-precision-12.c: New tests.
Diffstat (limited to 'gcc/c')
-rw-r--r-- | gcc/c/c-decl.cc | 153 | ||||
-rw-r--r-- | gcc/c/c-parser.cc | 223 | ||||
-rw-r--r-- | gcc/c/c-tree.h | 17 | ||||
-rw-r--r-- | gcc/c/c-typeck.cc | 361 |
4 files changed, 658 insertions, 96 deletions
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. */ |