From d50310408f54e38031f34931e591c63ff36fee09 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Tue, 10 Nov 2020 17:17:19 -0500 Subject: [PATCH] c++: Implement C++20 'using enum'. [PR91367] This feature allows the programmer to import enumerator names into the current scope so later mentions don't need to use the fully-qualified name. These usings are not subject to the usual restrictions on using-decls: in particular, they can move between class and non-class scopes, and between classes that are not related by inheritance. This last caused difficulty for our normal approach to using-decls within a class hierarchy, as we assume that the class where we looked up a used declaration is derived from the class where it was first declared. So to simplify things, in that case we make a clone of the CONST_DECL in the using class. Thanks to Nathan for the start of this work: in particular, the lookup_using_decl rewrite. The changes to dwarf2out revealed an existing issue with the D front-end: we were doing the wrong thing for importing a D CONST_DECL, because dwarf2out_imported_module_or_decl_1 was looking through it to its type, expecting it to be an enumerator, but in one case in thread.d, the constant had type int. Adding the ability to import a C++ enumerator also fixed that, but that led to a crash in force_decl_die, which didn't know what to do with a CONST_DECL. So now it does. Co-authored-by: Nathan Sidwell gcc/cp/ChangeLog: * cp-tree.h (USING_DECL_UNRELATED_P): New. (CONST_DECL_USING_P): New. * class.c (handle_using_decl): If USING_DECL_UNRELATED_P, clone the CONST_DECL. * name-lookup.c (supplement_binding_1): A clone hides its using-declaration. (lookup_using_decl): Rewrite to separate lookup and validation. (do_class_using_decl): Adjust. (finish_nonmember_using_decl): Adjust. * parser.c (make_location): Add cp_token overload. (finish_using_decl): Split out from... (cp_parser_using_declaration): ...here. Don't look through enums. (cp_parser_using_enum): New. (cp_parser_block_declaration): Call it. (cp_parser_member_declaration): Call it. * semantics.c (finish_id_expression_1): Handle enumerator used from class scope. gcc/ChangeLog: * dwarf2out.c (gen_enumeration_type_die): Call equate_decl_number_to_die for enumerators. (gen_member_die): Don't move enumerators to their enclosing class. (dwarf2out_imported_module_or_decl_1): Allow importing individual enumerators. (force_decl_die): Handle CONST_DECL. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/inh-ctor28.C: Adjust expected diagnostic. * g++.dg/cpp0x/inh-ctor33.C: Likewise. * g++.dg/cpp0x/using-enum-1.C: Add comment. * g++.dg/cpp0x/using-enum-2.C: Allowed in C++20. * g++.dg/cpp0x/using-enum-3.C: Likewise. * g++.dg/cpp1z/class-deduction69.C: Adjust diagnostic. * g++.dg/inherit/using5.C: Likewise. * g++.dg/cpp2a/using-enum-1.C: New test. * g++.dg/cpp2a/using-enum-2.C: New test. * g++.dg/cpp2a/using-enum-3.C: New test. * g++.dg/cpp2a/using-enum-4.C: New test. * g++.dg/cpp2a/using-enum-5.C: New test. * g++.dg/cpp2a/using-enum-6.C: New test. * g++.dg/debug/dwarf2/using-enum.C: New test. --- gcc/cp/class.c | 17 ++ gcc/cp/cp-tree.h | 11 + gcc/cp/name-lookup.c | 256 +++++++++++------- gcc/cp/parser.c | 145 ++++++++-- gcc/cp/semantics.c | 14 +- gcc/dwarf2out.c | 16 +- gcc/testsuite/g++.dg/cpp0x/inh-ctor28.C | 2 +- gcc/testsuite/g++.dg/cpp0x/inh-ctor33.C | 2 +- gcc/testsuite/g++.dg/cpp0x/using-enum-1.C | 3 + gcc/testsuite/g++.dg/cpp0x/using-enum-2.C | 11 +- gcc/testsuite/g++.dg/cpp0x/using-enum-3.C | 15 +- .../g++.dg/cpp1z/class-deduction69.C | 2 +- gcc/testsuite/g++.dg/cpp2a/using-enum-1.C | 62 +++++ gcc/testsuite/g++.dg/cpp2a/using-enum-2.C | 48 ++++ gcc/testsuite/g++.dg/cpp2a/using-enum-3.C | 6 + gcc/testsuite/g++.dg/cpp2a/using-enum-4.C | 13 + gcc/testsuite/g++.dg/cpp2a/using-enum-5.C | 132 +++++++++ gcc/testsuite/g++.dg/cpp2a/using-enum-6.C | 5 + .../g++.dg/debug/dwarf2/using-enum.C | 21 ++ gcc/testsuite/g++.dg/inherit/using5.C | 2 +- 20 files changed, 648 insertions(+), 135 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-4.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-5.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-6.C create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/using-enum.C diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 7c34d9466fc..ec47b0698ab 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -1331,6 +1331,23 @@ handle_using_decl (tree using_decl, tree t) add_method (t, *iter, true); alter_access (t, *iter, access); } + else if (USING_DECL_UNRELATED_P (using_decl)) + { + /* C++20 using enum can import non-inherited enumerators into class + scope. We implement that by making a copy of the CONST_DECL for which + CONST_DECL_USING_P is true. */ + gcc_assert (TREE_CODE (decl) == CONST_DECL); + + tree copy = copy_decl (decl); + DECL_CONTEXT (copy) = t; + DECL_ARTIFICIAL (copy) = true; + /* We emitted debug info for the USING_DECL above; make sure we don't + also emit anything for this clone. */ + DECL_IGNORED_P (copy) = true; + DECL_SOURCE_LOCATION (copy) = DECL_SOURCE_LOCATION (using_decl); + finish_member_declaration (copy); + DECL_ABSTRACT_ORIGIN (copy) = decl; + } else alter_access (t, decl, access); } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 63724c0e84f..9ae6ff5f7a2 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -529,6 +529,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; TEMPLATE_DECL_COMPLEX_ALIAS_P (in TEMPLATE_DECL) DECL_INSTANTIATING_NSDMI_P (in a FIELD_DECL) LABEL_DECL_CDTOR (in LABEL_DECL) + USING_DECL_UNRELATED_P (in USING_DECL) 3: DECL_IN_AGGR_P. 4: DECL_C_BIT_FIELD (in a FIELD_DECL) DECL_ANON_UNION_VAR_P (in a VAR_DECL) @@ -3409,6 +3410,16 @@ struct GTY(()) lang_decl { /* Non zero if the using decl refers to a dependent type. */ #define USING_DECL_TYPENAME_P(NODE) DECL_LANG_FLAG_1 (USING_DECL_CHECK (NODE)) +/* True if member using decl NODE refers to a non-inherited NODE. */ +#define USING_DECL_UNRELATED_P(NODE) DECL_LANG_FLAG_2 (USING_DECL_CHECK (NODE)) + +/* True iff the CONST_DECL is a class-scope clone from C++20 using enum, + created by handle_using_decl. */ +#define CONST_DECL_USING_P(NODE) \ + (TREE_CODE (NODE) == CONST_DECL \ + && TREE_CODE (TREE_TYPE (NODE)) == ENUMERAL_TYPE \ + && DECL_CONTEXT (NODE) != TREE_TYPE (NODE)) + /* In a FUNCTION_DECL, this is nonzero if this function was defined in the class definition. We have saved away the text of the function, but have not yet processed it. */ diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 410ec595c82..bf05e7bbcd1 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -2125,6 +2125,10 @@ supplement_binding_1 (cxx_binding *binding, tree decl) region to refer only to the namespace to which it already refers. */ ok = false; + else if (TREE_CODE (bval) == USING_DECL + && CONST_DECL_USING_P (decl)) + /* Let the clone hide the using-decl that introduced it. */ + binding->value = decl; else { if (!error_operand_p (bval)) @@ -4540,43 +4544,66 @@ push_class_level_binding (tree name, tree x) /* Process and lookup a using decl SCOPE::lookup.name, filling in lookup.values & lookup.type. Return true if ok. */ -static bool +static tree lookup_using_decl (tree scope, name_lookup &lookup) { tree current = current_scope (); bool dependent_p = false; + tree binfo = NULL_TREE; + base_kind b_kind = bk_not_base; + + /* Because C++20 breaks the invariant that only member using-decls + refer to members and only non-member using-decls refer to + non-members, we first do the lookups, and then do validation that + what we found is ok. */ + + if (TREE_CODE (scope) == ENUMERAL_TYPE + && cxx_dialect < cxx20 + && UNSCOPED_ENUM_P (scope) + && !TYPE_FUNCTION_SCOPE_P (scope)) + { + /* PR c++/60265 argued that since C++11 added explicit enum scope, we + should allow it as meaning the enclosing scope. I don't see any + justification for this in C++11, but let's keep allowing it. */ + tree ctx = CP_TYPE_CONTEXT (scope); + if (CLASS_TYPE_P (ctx) == CLASS_TYPE_P (current)) + scope = ctx; + } if (TREE_CODE (scope) == NAMESPACE_DECL) { /* Naming a namespace member. */ - if (TYPE_P (current)) + qualified_namespace_lookup (scope, &lookup); + + if (TYPE_P (current) + && (!lookup.value + || lookup.type + || cxx_dialect < cxx20 + || TREE_CODE (lookup.value) != CONST_DECL)) { error ("using-declaration for non-member at class scope"); - return false; + return NULL_TREE; } - - qualified_namespace_lookup (scope, &lookup); } else if (TREE_CODE (scope) == ENUMERAL_TYPE) { - error ("using-declaration may not name enumerator %<%E::%D%>", - scope, lookup.name); - return false; + /* Naming an enumeration member. */ + if (cxx_dialect < cxx20) + error ("% with enumeration scope %q#T " + "only available with %<-std=c++20%> or %<-std=gnu++20%>", + scope); + lookup.value = lookup_enumerator (scope, lookup.name); } else { - /* Naming a class member. */ - if (!TYPE_P (current)) - { - error ("using-declaration for member at non-class scope"); - return false; - } + /* Naming a class member. This is awkward in C++20, because we + might be naming an enumerator of an unrelated class. */ - /* Make sure the name is not invalid */ + /* You cannot using-decl a destructor. */ if (TREE_CODE (lookup.name) == BIT_NOT_EXPR) { error ("%<%T::%D%> names destructor", scope, lookup.name); - return false; + return NULL_TREE; } /* Using T::T declares inheriting ctors, even if T is a typedef. */ @@ -4584,91 +4611,150 @@ lookup_using_decl (tree scope, name_lookup &lookup) && (lookup.name == TYPE_IDENTIFIER (scope) || constructor_name_p (lookup.name, scope))) { + if (!TYPE_P (current)) + { + error ("non-member using-decl names constructor of %qT", scope); + return NULL_TREE; + } maybe_warn_cpp0x (CPP0X_INHERITING_CTORS); lookup.name = ctor_identifier; CLASSTYPE_NON_AGGREGATE (current) = true; } - /* Cannot introduce a constructor name. */ - if (constructor_name_p (lookup.name, current)) + if (!MAYBE_CLASS_TYPE_P (scope)) + ; + else if (TYPE_P (current)) { - error ("%<%T::%D%> names constructor in %qT", - scope, lookup.name, current); - return false; - } - - /* Member using decls finish processing when completing the - class. */ - /* From [namespace.udecl]: - - A using-declaration used as a member-declaration shall refer - to a member of a base class of the class being defined. + dependent_p = dependent_scope_p (scope); + if (!dependent_p) + { + binfo = lookup_base (current, scope, ba_any, &b_kind, tf_none); + gcc_checking_assert (b_kind >= bk_not_base); - In general, we cannot check this constraint in a template - because we do not know the entire set of base classes of the - current class type. Morover, if SCOPE is dependent, it might - match a non-dependent base. */ + if (lookup.name == ctor_identifier) + { + /* Even if there are dependent bases, SCOPE will not + be direct base, no matter. */ + if (b_kind < bk_proper_base || !binfo_direct_p (binfo)) + { + error ("%qT is not a direct base of %qT", scope, current); + return NULL_TREE; + } + } + else if (b_kind < bk_proper_base) + binfo = TYPE_BINFO (scope); + else if (IDENTIFIER_CONV_OP_P (lookup.name) + && dependent_type_p (TREE_TYPE (lookup.name))) + dependent_p = true; + } + } + else + binfo = TYPE_BINFO (scope); - dependent_p = dependent_scope_p (scope); if (!dependent_p) { - base_kind b_kind; - tree binfo = lookup_base (current, scope, ba_any, &b_kind, - tf_warning_or_error); - if (b_kind < bk_proper_base) + if (binfo) + lookup.value = lookup_member (binfo, lookup.name, /*protect=*/2, + /*want_type=*/false, tf_none); + + tree saved_value = lookup.value; + if (lookup.value + && b_kind < bk_proper_base) { - /* If there are dependent bases, scope might resolve at - instantiation time, even if it isn't exactly one of - the dependent bases. */ - if (b_kind == bk_same_type || !any_dependent_bases_p ()) + if (cxx_dialect >= cxx20 + && TREE_CODE (lookup.value) == CONST_DECL) { - error_not_base_type (scope, current); - return false; + /* Using an unrelated enum; check access here rather + than separately for class and non-class using. */ + perform_or_defer_access_check + (binfo, lookup.value, lookup.value, tf_warning_or_error); + /* And then if this is a copy from handle_using_decl, look + through to the original enumerator. */ + if (CONST_DECL_USING_P (lookup.value)) + lookup.value = DECL_ABSTRACT_ORIGIN (lookup.value); } - /* Treat as-if dependent. */ - dependent_p = true; + else + lookup.value = NULL_TREE; } - else if (lookup.name == ctor_identifier && !binfo_direct_p (binfo)) + + if (!lookup.value) { - error ("cannot inherit constructors from indirect base %qT", - scope); - return false; + if (!TYPE_P (current)) + { + error ("using-declaration for member at non-class scope"); + return NULL_TREE; + } + + if (b_kind < bk_proper_base) + { + if (b_kind == bk_not_base && any_dependent_bases_p ()) + /* Treat as-if dependent. */ + dependent_p = true; + else + { + auto_diagnostic_group g; + error_not_base_type (scope, current); + if (saved_value && DECL_IMPLICIT_TYPEDEF_P (saved_value) + && (TREE_CODE (TREE_TYPE (saved_value)) + == ENUMERAL_TYPE)) + inform (input_location, + "did you mean %?", + scope, lookup.name); + return NULL_TREE; + } + } } - else if (IDENTIFIER_CONV_OP_P (lookup.name) - && dependent_type_p (TREE_TYPE (lookup.name))) - dependent_p = true; - else - lookup.value = lookup_member (binfo, lookup.name, 0, - false, tf_warning_or_error); } } - if (!dependent_p) + /* Did we find anything sane? */ + if (dependent_p) + ; + else if (!lookup.value) { - if (!lookup.value) - { - error ("%qD has not been declared in %qE", lookup.name, scope); - return false; - } + error ("%qD has not been declared in %qD", lookup.name, scope); + return NULL_TREE; + } + else if (TREE_CODE (lookup.value) == TREE_LIST + /* We can (independently) have ambiguous implicit typedefs. */ + || (lookup.type && TREE_CODE (lookup.type) == TREE_LIST)) + { + error ("reference to %qD is ambiguous", lookup.name); + print_candidates (TREE_CODE (lookup.value) == TREE_LIST + ? lookup.value : lookup.type); + return NULL_TREE; + } + else if (TREE_CODE (lookup.value) == NAMESPACE_DECL) + { + error ("using-declaration may not name namespace %qD", lookup.value); + return NULL_TREE; + } - if (TREE_CODE (lookup.value) == TREE_LIST - /* We can (independently) have ambiguous implicit typedefs. */ - || (lookup.type && TREE_CODE (lookup.type) == TREE_LIST)) - { - error ("reference to %qD is ambiguous", lookup.name); - print_candidates (TREE_CODE (lookup.value) == TREE_LIST - ? lookup.value : lookup.type); - return false; - } + if (TYPE_P (current)) + { + /* In class scope. */ - if (TREE_CODE (lookup.value) == NAMESPACE_DECL) + /* Cannot introduce a constructor name. */ + if (constructor_name_p (lookup.name, current)) { - error ("using-declaration may not name namespace %qD", lookup.value); - return false; + error ("%<%T::%D%> names constructor in %qT", + scope, lookup.name, current); + return NULL_TREE; } + + if (lookup.value && BASELINK_P (lookup.value)) + /* The binfo from which the functions came does not matter. */ + lookup.value = BASELINK_FUNCTIONS (lookup.value); } - return true; + tree using_decl = build_lang_decl (USING_DECL, lookup.name, NULL_TREE); + USING_DECL_SCOPE (using_decl) = scope; + USING_DECL_DECLS (using_decl) = lookup.value; + DECL_DEPENDENT_P (using_decl) = dependent_p; + if (TYPE_P (current) && b_kind == bk_not_base) + USING_DECL_UNRELATED_P (using_decl) = true; + + return using_decl; } /* Process "using SCOPE::NAME" in a class scope. Return the @@ -4682,20 +4768,7 @@ do_class_using_decl (tree scope, tree name) return NULL_TREE; name_lookup lookup (name); - if (!lookup_using_decl (scope, lookup)) - return NULL_TREE; - - tree found = lookup.value; - if (found && BASELINK_P (found)) - /* The binfo from which the functions came does not matter. */ - found = BASELINK_FUNCTIONS (found); - - tree using_decl = build_lang_decl (USING_DECL, lookup.name, NULL_TREE); - USING_DECL_SCOPE (using_decl) = scope; - USING_DECL_DECLS (using_decl) = found; - DECL_DEPENDENT_P (using_decl) = !found; - - return using_decl; + return lookup_using_decl (scope, lookup); } @@ -5076,7 +5149,8 @@ finish_nonmember_using_decl (tree scope, tree name) name_lookup lookup (name); - if (!lookup_using_decl (scope, lookup)) + tree using_decl = lookup_using_decl (scope, lookup); + if (!using_decl) return; /* Emit debug info. */ @@ -5105,8 +5179,6 @@ finish_nonmember_using_decl (tree scope, tree name) } else { - tree using_decl = build_lang_decl (USING_DECL, lookup.name, NULL_TREE); - USING_DECL_SCOPE (using_decl) = scope; add_decl_expr (using_decl); cxx_binding *binding = find_local_binding (current_binding_level, name); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 68f1cfa23f2..42f705266bb 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -808,6 +808,14 @@ make_location (location_t caret, location_t start, cp_lexer *lexer) return make_location (caret, start, t->location); } +/* Overload for make_location taking tokens instead of locations. */ + +static inline location_t +make_location (cp_token *caret, cp_token *start, cp_token *end) +{ + return make_location (caret->location, start->location, end->location); +} + /* nonzero if we are presently saving tokens. */ static inline int @@ -2233,6 +2241,8 @@ static bool cp_parser_using_declaration (cp_parser *, bool); static void cp_parser_using_directive (cp_parser *); +static void cp_parser_using_enum + (cp_parser *); static tree cp_parser_alias_declaration (cp_parser *); static void cp_parser_asm_definition @@ -13726,6 +13736,8 @@ cp_parser_block_declaration (cp_parser *parser, token2 = cp_lexer_peek_nth_token (parser->lexer, 2); if (token2->keyword == RID_NAMESPACE) cp_parser_using_directive (parser); + else if (token2->keyword == RID_ENUM) + cp_parser_using_enum (parser); /* If the second token after 'using' is '=', then we have an alias-declaration. */ else if (cxx_dialect >= cxx11 @@ -20010,6 +20022,31 @@ cp_parser_qualified_namespace_specifier (cp_parser* parser) return cp_parser_namespace_name (parser); } +/* Subroutine of cp_parser_using_declaration. */ + +static tree +finish_using_decl (tree qscope, tree identifier, bool typename_p = false) +{ + tree decl = NULL_TREE; + if (at_class_scope_p ()) + { + /* Create the USING_DECL. */ + decl = do_class_using_decl (qscope, identifier); + + if (check_for_bare_parameter_packs (decl)) + return error_mark_node; + + if (decl && typename_p) + USING_DECL_TYPENAME_P (decl) = 1; + + /* Add it to the list of members in this class. */ + finish_member_declaration (decl); + } + else + finish_nonmember_using_decl (qscope, identifier); + return decl; +} + /* Parse a using-declaration, or, if ACCESS_DECLARATION_P is true, an access declaration. @@ -20029,7 +20066,6 @@ cp_parser_using_declaration (cp_parser* parser, cp_token *token; bool typename_p = false; bool global_scope_p; - tree decl; tree identifier; tree qscope; int oldcount = errorcount; @@ -20088,9 +20124,6 @@ cp_parser_using_declaration (cp_parser* parser, /*is_declaration=*/true); if (!qscope) qscope = global_namespace; - else if (UNSCOPED_ENUM_P (qscope) - && !TYPE_FUNCTION_SCOPE_P (qscope)) - qscope = CP_TYPE_CONTEXT (qscope); cp_warn_deprecated_use_scopes (qscope); @@ -20138,25 +20171,13 @@ cp_parser_using_declaration (cp_parser* parser, "a template-id may not appear in a using-declaration"); else { - if (at_class_scope_p ()) - { - /* Create the USING_DECL. */ - decl = do_class_using_decl (qscope, identifier); - - if (decl && typename_p) - USING_DECL_TYPENAME_P (decl) = 1; + tree decl = finish_using_decl (qscope, identifier, typename_p); - if (check_for_bare_parameter_packs (decl)) - { - cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); - return false; - } - else - /* Add it to the list of members in this class. */ - finish_member_declaration (decl); + if (decl == error_mark_node) + { + cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); + return false; } - else - finish_nonmember_using_decl (qscope, identifier); } if (!access_declaration_p @@ -20182,6 +20203,76 @@ cp_parser_using_declaration (cp_parser* parser, return true; } +/* C++20 using enum declaration. + + using-enum-declaration : + using elaborated-enum-specifier ; */ + +static void +cp_parser_using_enum (cp_parser *parser) +{ + cp_parser_require_keyword (parser, RID_USING, RT_USING); + + /* Using cp_parser_elaborated_type_specifier rejects typedef-names, which + breaks one of the motivating examples in using-enum-5.C. + cp_parser_simple_type_specifier seems to be closer to what we actually + want, though that hasn't been properly specified yet. */ + + /* Consume 'enum'. */ + gcc_checking_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_ENUM)); + cp_lexer_consume_token (parser->lexer); + + cp_token *start = cp_lexer_peek_token (parser->lexer); + + tree type = (cp_parser_simple_type_specifier + (parser, NULL, CP_PARSER_FLAGS_TYPENAME_OPTIONAL)); + + cp_token *end = cp_lexer_previous_token (parser->lexer); + + if (type == error_mark_node + || !cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) + { + cp_parser_skip_to_end_of_block_or_statement (parser); + return; + } + if (TREE_CODE (type) == TYPE_DECL) + type = TREE_TYPE (type); + + /* The elaborated-enum-specifier shall not name a dependent type and the type + shall have a reachable enum-specifier. */ + const char *msg = nullptr; + if (cxx_dialect < cxx20) + msg = _("% " + "only available with %<-std=c++20%> or %<-std=gnu++20%>"); + else if (dependent_type_p (type)) + msg = _("% of dependent type %qT"); + else if (TREE_CODE (type) != ENUMERAL_TYPE) + msg = _("% of non-enumeration type %q#T"); + else if (!COMPLETE_TYPE_P (type)) + msg = _("% of incomplete type %qT"); + else if (OPAQUE_ENUM_P (type)) + msg = _("% of %qT before its enum-specifier"); + if (msg) + { + location_t loc = make_location (start, start, end); + auto_diagnostic_group g; + error_at (loc, msg, type); + loc = location_of (type); + if (cxx_dialect < cxx20 || loc == input_location) + ; + else if (OPAQUE_ENUM_P (type)) + inform (loc, "opaque-enum-declaration here"); + else + inform (loc, "declared here"); + } + + /* A using-enum-declaration introduces the enumerator names of the named + enumeration as if by a using-declaration for each enumerator. */ + if (TREE_CODE (type) == ENUMERAL_TYPE) + for (tree v = TYPE_VALUES (type); v; v = TREE_CHAIN (v)) + finish_using_decl (type, DECL_NAME (TREE_VALUE (v))); +} + /* Parse an alias-declaration. alias-declaration: @@ -25279,12 +25370,10 @@ cp_parser_member_declaration (cp_parser* parser) if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING)) { if (cxx_dialect < cxx11) - { - /* Parse the using-declaration. */ - cp_parser_using_declaration (parser, - /*access_declaration_p=*/false); - return; - } + /* Parse the using-declaration. */ + cp_parser_using_declaration (parser, /*access_declaration_p=*/false); + else if (cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_ENUM)) + cp_parser_using_enum (parser); else { tree decl; @@ -25305,8 +25394,8 @@ cp_parser_member_declaration (cp_parser* parser) else cp_parser_using_declaration (parser, /*access_declaration_p=*/false); - return; } + return; } /* Check for @defs. */ diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 0389198dab8..5ff70ff4844 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -4019,9 +4019,17 @@ finish_id_expression_1 (tree id_expression, if (context != current_class_type) { tree path = currently_open_derived_class (context); - perform_or_defer_access_check (TYPE_BINFO (path), - decl, decl, - tf_warning_or_error); + if (!path) + /* PATH can be null for using an enum of an unrelated + class; we checked its access in lookup_using_decl. + + ??? Should this case make a clone instead, like + handle_using_decl? */ + gcc_assert (TREE_CODE (decl) == CONST_DECL); + else + perform_or_defer_access_check (TYPE_BINFO (path), + decl, decl, + tf_warning_or_error); } } diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 4452b9fa89e..0e8436e51c4 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -22193,6 +22193,9 @@ gen_enumeration_type_die (tree type, dw_die_ref context_die) dw_die_ref enum_die = new_die (DW_TAG_enumerator, type_die, link); tree value = TREE_VALUE (link); + if (DECL_P (value)) + equate_decl_number_to_die (value, enum_die); + gcc_assert (!ENUM_IS_OPAQUE (type)); add_name_attribute (enum_die, IDENTIFIER_POINTER (TREE_PURPOSE (link))); @@ -25247,6 +25250,10 @@ gen_member_die (tree type, dw_die_ref context_die) splice = false; } } + else if (child->die_tag == DW_TAG_enumerator) + /* Enumerators remain under their enumeration even if + their names are introduced in the enclosing scope. */ + splice = false; if (splice) splice_child_die (context_die, child); @@ -26158,6 +26165,13 @@ force_decl_die (tree decl) decl_die = comp_unit_die (); break; + case CONST_DECL: + /* Enumerators shouldn't need force_decl_die. */ + gcc_assert (DECL_CONTEXT (decl) == NULL_TREE + || TREE_CODE (DECL_CONTEXT (decl)) != ENUMERAL_TYPE); + gen_decl_die (decl, NULL, NULL, context_die); + break; + case TRANSLATION_UNIT_DECL: decl_die = comp_unit_die (); break; @@ -26743,7 +26757,7 @@ dwarf2out_imported_module_or_decl_1 (tree decl, else xloc = expand_location (input_location); - if (TREE_CODE (decl) == TYPE_DECL || TREE_CODE (decl) == CONST_DECL) + if (TREE_CODE (decl) == TYPE_DECL) { at_import_die = force_type_die (TREE_TYPE (decl)); /* For namespace N { typedef void T; } using N::T; base_type_die diff --git a/gcc/testsuite/g++.dg/cpp0x/inh-ctor28.C b/gcc/testsuite/g++.dg/cpp0x/inh-ctor28.C index 90a06c610f3..59801a1d7bc 100644 --- a/gcc/testsuite/g++.dg/cpp0x/inh-ctor28.C +++ b/gcc/testsuite/g++.dg/cpp0x/inh-ctor28.C @@ -4,4 +4,4 @@ struct A {}; struct B : virtual A {}; struct C : virtual A {}; -struct D : B,C { using A::A; }; // { dg-error "indirect" } +struct D : B,C { using A::A; }; // { dg-error "not a direct base" } diff --git a/gcc/testsuite/g++.dg/cpp0x/inh-ctor33.C b/gcc/testsuite/g++.dg/cpp0x/inh-ctor33.C index 95b7812f054..4e612907a42 100644 --- a/gcc/testsuite/g++.dg/cpp0x/inh-ctor33.C +++ b/gcc/testsuite/g++.dg/cpp0x/inh-ctor33.C @@ -10,7 +10,7 @@ public: class Y : public X { }; class Z : public Y { - using X::X; // { dg-error "cannot inherit constructors from indirect base .X." } + using X::X; // { dg-error ".X. is not a direct base of .Z." } }; int main() diff --git a/gcc/testsuite/g++.dg/cpp0x/using-enum-1.C b/gcc/testsuite/g++.dg/cpp0x/using-enum-1.C index 9904d59aaaa..bf251babeae 100644 --- a/gcc/testsuite/g++.dg/cpp0x/using-enum-1.C +++ b/gcc/testsuite/g++.dg/cpp0x/using-enum-1.C @@ -1,6 +1,9 @@ // PR c++/60265 // { dg-do compile { target c++11 } } +// [namespace.udecl]/7 shall not name a scoped enumerator. +// (so unscoped enumerator is ok) + namespace A { enum E { V }; diff --git a/gcc/testsuite/g++.dg/cpp0x/using-enum-2.C b/gcc/testsuite/g++.dg/cpp0x/using-enum-2.C index faa38179305..8ea70d79d4d 100644 --- a/gcc/testsuite/g++.dg/cpp0x/using-enum-2.C +++ b/gcc/testsuite/g++.dg/cpp0x/using-enum-2.C @@ -1,20 +1,23 @@ // PR c++/60265 // { dg-do compile { target c++11 } } +// [namespace.udecl]/7 shall not name a scoped enumerator. +// (this changes in C++2a) + namespace A { enum class E { V }; - using E::V; // { dg-error "name enumerator" } + using E::V; // { dg-error "enum" "" { target { ! c++2a } } } } void foo() { - using A::E::V; // { dg-error "name enumerator" } + using A::E::V; // { dg-error "enum" "" { target { ! c++2a } } } } -using A::E::V; // { dg-error "name enumerator" } +using A::E::V; // { dg-error "enum" "" { target { ! c++2a } } } enum class F { U }; -using F::U; // { dg-error "name enumerator" } +using F::U; // { dg-error "enum" "" { target { ! c++2a } } } diff --git a/gcc/testsuite/g++.dg/cpp0x/using-enum-3.C b/gcc/testsuite/g++.dg/cpp0x/using-enum-3.C index ecc4ddcccd8..34f8bf4fa0b 100644 --- a/gcc/testsuite/g++.dg/cpp0x/using-enum-3.C +++ b/gcc/testsuite/g++.dg/cpp0x/using-enum-3.C @@ -1,15 +1,24 @@ // PR c++/89511 // { dg-do compile { target c++11 } } +// [namespace.udecl] In a using-declaration used as a +// member-declaration, the nested-name-specifier shall name a base +// class of the class being defined +// (this changes in C++2a) + void f () { enum e { a }; - using e::a; // { dg-error "name enumerator" } + using e::a; // { dg-error "redeclaration" } + // { dg-error "enum" "" { target { ! c++2a } } .-1 } } +enum E { A }; + struct S { enum E { A }; - using E::A; // { dg-error "type .S. is not a base type for type .S." } + using E::A; // { dg-error "not a base" "" { target { ! c++2a } } } + // { dg-error "conflicts" "" { target c++2a } .-1 } }; namespace N { @@ -17,5 +26,5 @@ namespace N { } struct T { - using N::E::B; // { dg-error "using-declaration for non-member at class scope" } + using N::E::B; // { dg-error "enum" "" { target { ! c++2a } } } }; diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction69.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction69.C index 8291f4a649f..d3363668cc5 100644 --- a/gcc/testsuite/g++.dg/cpp1z/class-deduction69.C +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction69.C @@ -6,7 +6,7 @@ namespace a { template using c = b; } template struct e : a::c { // { dg-error "incomplete" } - using a::c<>::c; // { dg-prune-output "not a base" } + using a::c<>::c; // { dg-prune-output "not a direct base" } }; template