aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2020-11-10 17:17:19 -0500
committerJason Merrill <jason@redhat.com>2020-11-13 13:36:29 -0500
commitd50310408f54e38031f34931e591c63ff36fee09 (patch)
tree8797b7178962c36a470b2e374fd5b3c8d2fb45fe
parente3b3b59683c1e7d31a9d313dd97394abebf644be (diff)
downloadgcc-d50310408f54e38031f34931e591c63ff36fee09.zip
gcc-d50310408f54e38031f34931e591c63ff36fee09.tar.gz
gcc-d50310408f54e38031f34931e591c63ff36fee09.tar.bz2
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 <nathan@acm.org> 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.
-rw-r--r--gcc/cp/class.c17
-rw-r--r--gcc/cp/cp-tree.h11
-rw-r--r--gcc/cp/name-lookup.c256
-rw-r--r--gcc/cp/parser.c145
-rw-r--r--gcc/cp/semantics.c14
-rw-r--r--gcc/dwarf2out.c16
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/inh-ctor28.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/inh-ctor33.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/using-enum-1.C3
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/using-enum-2.C11
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/using-enum-3.C15
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/class-deduction69.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/using-enum-1.C62
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/using-enum-2.C48
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/using-enum-3.C6
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/using-enum-4.C13
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/using-enum-5.C132
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/using-enum-6.C5
-rw-r--r--gcc/testsuite/g++.dg/debug/dwarf2/using-enum.C21
-rw-r--r--gcc/testsuite/g++.dg/inherit/using5.C2
20 files changed, 648 insertions, 135 deletions
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 7c34d94..ec47b06 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 63724c0..9ae6ff5 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 410ec59..bf05e7b 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 ("%<using%> 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 %<using enum %T::%D%>?",
+ 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 68f1cfa..42f7052 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 = _("%<using enum%> "
+ "only available with %<-std=c++20%> or %<-std=gnu++20%>");
+ else if (dependent_type_p (type))
+ msg = _("%<using enum%> of dependent type %qT");
+ else if (TREE_CODE (type) != ENUMERAL_TYPE)
+ msg = _("%<using enum%> of non-enumeration type %q#T");
+ else if (!COMPLETE_TYPE_P (type))
+ msg = _("%<using enum%> of incomplete type %qT");
+ else if (OPAQUE_ENUM_P (type))
+ msg = _("%<using enum%> 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 0389198..5ff70ff 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 4452b9f..0e8436e 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 90a06c6..59801a1 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 95b7812..4e61290 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 9904d59..bf251ba 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 faa3817..8ea70d7 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 ecc4ddc..34f8bf4 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 8291f4a..d336366 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 <typename...> using c = b;
}
template <typename... d> struct e : a::c<d...> { // { 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 <template <typename> typename f> void g() { f(); }
void h() { g<e>(); }
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-1.C b/gcc/testsuite/g++.dg/cpp2a/using-enum-1.C
new file mode 100644
index 0000000..fd34ca8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-1.C
@@ -0,0 +1,62 @@
+// Test of using an enumerator.
+// { dg-do compile { target c++2a } }
+
+// using ENUM::V;
+enum class E {v};
+
+using E::v;
+using E::v; // OK
+
+E a = v;
+
+class C
+{
+ using E::v; // { dg-message "declared private here" }
+
+ static inline const E m = v;
+};
+
+E b = C::v; // { dg-error "private" }
+
+struct B
+{
+ enum E {e};
+ enum class EC {f};
+ using EC::f;
+};
+
+struct D
+{
+private:
+ using B::e; // { dg-message "declared private here" }
+ using B::f; // { dg-message "declared private here" }
+};
+
+struct F : D
+{
+ static inline const auto bad1 = e; // { dg-error "private" }
+ static inline const auto bad2 = f; // { dg-error "private" }
+
+ static inline const auto ok1 = B::e;
+ static inline const auto ok2 = B::f;
+ static inline const auto also_ok1 = B::E::e;
+ static inline const auto also_ok2 = B::EC::f;
+};
+
+using B::e;
+auto bob = e;
+
+struct Q
+{
+ using B::e;
+};
+using Q::e; // OK
+
+using D::e; // { dg-error "private" }
+
+template <class T>
+struct X : T
+{
+ using T::e;
+};
+auto fob = X<Q>::e;
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-2.C b/gcc/testsuite/g++.dg/cpp2a/using-enum-2.C
new file mode 100644
index 0000000..66b37f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-2.C
@@ -0,0 +1,48 @@
+// Test of 'using enum' in different scopes.
+// { dg-do compile { target c++20 } }
+
+namespace N
+{
+ enum class E { e, f };
+}
+
+int main()
+{
+ using enum N::E;
+ static_assert (e < f);
+}
+
+struct A
+{
+ using enum N::E;
+ static_assert (e < f);
+};
+
+namespace M
+{
+ using enum N::E;
+ static_assert (e < f);
+
+ enum class X: int; // { dg-message "opaque" }
+ using enum X; // { dg-error "enum-specifier" }
+}
+
+template <class T>
+void f()
+{
+ using enum N::E;
+ static_assert (e < f);
+}
+
+template <class T>
+struct AT
+{
+ using enum N::E;
+ static_assert (e < f);
+};
+
+template <class T>
+struct BT
+{
+ using enum T::E; // { dg-error "dependent" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-3.C b/gcc/testsuite/g++.dg/cpp2a/using-enum-3.C
new file mode 100644
index 0000000..d09bd6a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-3.C
@@ -0,0 +1,6 @@
+// Test of 'using enum' syntax error recovery.
+// { dg-do compile { target c++20 } }
+
+using enum 2 + garbage3'850%^&; // { dg-error "" }
+
+void f() {}
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-4.C b/gcc/testsuite/g++.dg/cpp2a/using-enum-4.C
new file mode 100644
index 0000000..03432cf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-4.C
@@ -0,0 +1,13 @@
+// Test for suggestion to try 'using enum'.
+// { dg-do compile { target c++20 } }
+
+struct A
+{
+ enum E { e };
+};
+
+struct B
+{
+ using A::E; // { dg-error "" }
+ // { dg-message "using enum" "" { target *-*-* } .-1 }
+};
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-5.C b/gcc/testsuite/g++.dg/cpp2a/using-enum-5.C
new file mode 100644
index 0000000..e5fe820
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-5.C
@@ -0,0 +1,132 @@
+// Examples from P1099R5
+// { dg-do compile { target c++20 } }
+
+namespace my_lib {
+
+ enum class errcode
+ {
+ SUCCESS = 0,
+ ENOMEM = 1,
+ EAGAIN = 2,
+ ETOOSLOW = 3
+ };
+ using enum errcode; // import enumerators into namespace
+}
+
+namespace NS {
+ my_lib::errcode get_widget() {
+ using namespace my_lib;
+ return ETOOSLOW; // works, and conversions to int don't.
+ int i = ETOOSLOW; // { dg-error "" }
+ }
+}
+
+enum class rgba_color_channel { red, green, blue, alpha};
+
+const char * to_string(rgba_color_channel channel) {
+ switch (channel) {
+ using enum rgba_color_channel;
+ case red: return "red";
+ case green: return "green";
+ case blue: return "blue";
+ case alpha: return "alpha";
+ }
+ return nullptr;
+}
+
+namespace ns {
+ struct E_detail {
+ enum E { e1, e2 };
+ friend void swap(E&, E&); // adl-only swap in the only associated scope of the enum
+ };
+ using E = E_detail::E; // import E into ns
+ using enum E; // expose the enumerators of E in ns. Also note the direct reference to E.
+}
+
+int main() {
+ auto x = ns::e1;
+ auto y = ns::e2;
+ swap(x, y); // finds the swap in the associated struct
+}
+
+namespace N0 {
+ enum E { x };
+ struct S {
+ enum H { y };
+ enum class K { z };
+ using E::x; // OK, introduces x into S
+ using E::x; // { dg-error "" } redeclaration in class scope
+ using H::y; // { dg-error "" } redeclaration in class scope
+ using K::z; // OK, introduces z into S
+ };
+ namespace NS {
+ enum H { y };
+ enum class K { z };
+ using E::x; // OK, introduces x into NS
+ using E::x; // OK, just a redeclaration of the same entity
+ using H::y; // OK, redeclaration of the same entity
+ using K::z; // OK, introduces z into NS
+ };
+}
+namespace N1 {
+ struct S {
+ enum E { x };
+ enum class EC { y };
+ using EC::y;
+ };
+
+ void f() {
+ using S::x; // OK
+ x; // resolves to S::E::x;
+ using S::y; // OK
+ y; // resolves to S::EC::y;
+ }
+}
+
+namespace N2 {
+ enum class E { a, b, c };
+ using E::a, E::b, E::c; // OK, imports all three
+ auto x = (a,b,c);
+}
+
+namespace N3 {
+ struct B {
+ enum class E { x };
+ };
+ enum class H { y };
+ struct C : B {
+ using enum B::E; // OK, introduces E::x into C
+ using enum H; // OK, introduces y into C. Does not introduce H
+ };
+ auto i = C::y; // OK
+ C::H h; // { dg-error "" }
+}
+
+namespace N4 {
+ enum class button { up, down };
+ struct S {
+ using button::up;
+ button b = up; // OK
+ };
+}
+
+namespace N5 {
+ enum class fruit { orange, apple };
+ struct S {
+ using enum fruit; // OK, introduces orange and apple into S
+ };
+ void f() {
+ S s;
+ s.orange; // OK, names fruit::orange
+ S::orange; // OK, names fruit::orange
+ }
+}
+
+namespace N6 {
+ enum class fruit { orange, apple };
+ enum class color { red, orange };
+ void f() {
+ using enum fruit; // OK
+ using enum color; // { dg-error "" } color::orange and fruit::orange conflict
+ }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-6.C b/gcc/testsuite/g++.dg/cpp2a/using-enum-6.C
new file mode 100644
index 0000000..732cdfd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-6.C
@@ -0,0 +1,5 @@
+// { dg-do compile { target c++2a } }
+
+using enum void; // { dg-error "non-enum" }
+struct A {}; // { dg-message "declared here" }
+using enum A; // { dg-error "non-enum" }
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/using-enum.C b/gcc/testsuite/g++.dg/debug/dwarf2/using-enum.C
new file mode 100644
index 0000000..7663a13
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/using-enum.C
@@ -0,0 +1,21 @@
+// Test of 'using enum' debug info.
+// { dg-do compile { target c++20 } }
+// { dg-options "-g -dA" }
+
+struct A
+{
+ // All the counts are +1 for the abbreviation table.
+ // { dg-final { scan-assembler-times "DW_TAG_enumeration_type" 2 } }
+ // { dg-final { scan-assembler-times "DW_TAG_enumerator" 3 } }
+ enum E { e, f };
+};
+
+struct B
+{
+ // The using-enum-declaration is represented by two
+ // DW_TAG_imported_declaration, one for each enumerator.
+ // { dg-final { scan-assembler-times "DW_TAG_imported_declaration" 3 } }
+ using enum A::E;
+};
+
+B b;
diff --git a/gcc/testsuite/g++.dg/inherit/using5.C b/gcc/testsuite/g++.dg/inherit/using5.C
index b8e5107..514cd8d 100644
--- a/gcc/testsuite/g++.dg/inherit/using5.C
+++ b/gcc/testsuite/g++.dg/inherit/using5.C
@@ -6,7 +6,7 @@
template<int> struct A
{
- A::A; // { dg-error "constructor|not a base" }
+ A::A; // { dg-error "constructor|not a direct base" }
};
struct B