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.
This commit is contained in:
Jason Merrill
2020-11-10 17:17:19 -05:00
parent e3b3b59683
commit d50310408f
20 changed files with 659 additions and 146 deletions

View File

@@ -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);
}

View File

@@ -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. */

View File

@@ -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;
}
if (!MAYBE_CLASS_TYPE_P (scope))
;
else if (TYPE_P (current))
{
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);
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);
if (!dependent_p)
{
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 (cxx_dialect >= cxx20
&& TREE_CODE (lookup.value) == CONST_DECL)
{
/* 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);
}
else
lookup.value = NULL_TREE;
}
if (!lookup.value)
{
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;
}
}
}
}
}
/* Did we find anything sane? */
if (dependent_p)
;
else if (!lookup.value)
{
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 (TYPE_P (current))
{
/* In class scope. */
/* Cannot introduce a constructor name. */
if (constructor_name_p (lookup.name, current))
{
error ("%<%T::%D%> names constructor in %qT",
scope, lookup.name, current);
return false;
return NULL_TREE;
}
/* 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.
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. */
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 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 ())
{
error_not_base_type (scope, current);
return false;
}
/* Treat as-if dependent. */
dependent_p = true;
}
else if (lookup.name == ctor_identifier && !binfo_direct_p (binfo))
{
error ("cannot inherit constructors from indirect base %qT",
scope);
return false;
}
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 (lookup.value && BASELINK_P (lookup.value))
/* The binfo from which the functions came does not matter. */
lookup.value = BASELINK_FUNCTIONS (lookup.value);
}
if (!dependent_p)
{
if (!lookup.value)
{
error ("%qD has not been declared in %qE", lookup.name, scope);
return false;
}
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;
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 (TREE_CODE (lookup.value) == NAMESPACE_DECL)
{
error ("using-declaration may not name namespace %qD", lookup.value);
return false;
}
}
return 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);

View File

@@ -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 ())
tree decl = finish_using_decl (qscope, identifier, typename_p);
if (decl == error_mark_node)
{
/* Create the USING_DECL. */
decl = do_class_using_decl (qscope, identifier);
if (decl && typename_p)
USING_DECL_TYPENAME_P (decl) = 1;
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);
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. */

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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" }

View File

@@ -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()

View File

@@ -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 };

View File

@@ -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 } } }

View File

@@ -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 } } }
};

View File

@@ -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>(); }

View File

@@ -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;

View File

@@ -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" }
};

View File

@@ -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() {}

View File

@@ -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 }
};

View File

@@ -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
}
}

View File

@@ -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" }

View File

@@ -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;

View File

@@ -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