diff options
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. */ @@ -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" } */ |