Skip to content

Commit d503104

Browse files
committed
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 <[email protected]> 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.
1 parent e3b3b59 commit d503104

20 files changed

+648
-135
lines changed

gcc/cp/class.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,6 +1331,23 @@ handle_using_decl (tree using_decl, tree t)
13311331
add_method (t, *iter, true);
13321332
alter_access (t, *iter, access);
13331333
}
1334+
else if (USING_DECL_UNRELATED_P (using_decl))
1335+
{
1336+
/* C++20 using enum can import non-inherited enumerators into class
1337+
scope. We implement that by making a copy of the CONST_DECL for which
1338+
CONST_DECL_USING_P is true. */
1339+
gcc_assert (TREE_CODE (decl) == CONST_DECL);
1340+
1341+
tree copy = copy_decl (decl);
1342+
DECL_CONTEXT (copy) = t;
1343+
DECL_ARTIFICIAL (copy) = true;
1344+
/* We emitted debug info for the USING_DECL above; make sure we don't
1345+
also emit anything for this clone. */
1346+
DECL_IGNORED_P (copy) = true;
1347+
DECL_SOURCE_LOCATION (copy) = DECL_SOURCE_LOCATION (using_decl);
1348+
finish_member_declaration (copy);
1349+
DECL_ABSTRACT_ORIGIN (copy) = decl;
1350+
}
13341351
else
13351352
alter_access (t, decl, access);
13361353
}

gcc/cp/cp-tree.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
529529
TEMPLATE_DECL_COMPLEX_ALIAS_P (in TEMPLATE_DECL)
530530
DECL_INSTANTIATING_NSDMI_P (in a FIELD_DECL)
531531
LABEL_DECL_CDTOR (in LABEL_DECL)
532+
USING_DECL_UNRELATED_P (in USING_DECL)
532533
3: DECL_IN_AGGR_P.
533534
4: DECL_C_BIT_FIELD (in a FIELD_DECL)
534535
DECL_ANON_UNION_VAR_P (in a VAR_DECL)
@@ -3409,6 +3410,16 @@ struct GTY(()) lang_decl {
34093410
/* Non zero if the using decl refers to a dependent type. */
34103411
#define USING_DECL_TYPENAME_P(NODE) DECL_LANG_FLAG_1 (USING_DECL_CHECK (NODE))
34113412

3413+
/* True if member using decl NODE refers to a non-inherited NODE. */
3414+
#define USING_DECL_UNRELATED_P(NODE) DECL_LANG_FLAG_2 (USING_DECL_CHECK (NODE))
3415+
3416+
/* True iff the CONST_DECL is a class-scope clone from C++20 using enum,
3417+
created by handle_using_decl. */
3418+
#define CONST_DECL_USING_P(NODE) \
3419+
(TREE_CODE (NODE) == CONST_DECL \
3420+
&& TREE_CODE (TREE_TYPE (NODE)) == ENUMERAL_TYPE \
3421+
&& DECL_CONTEXT (NODE) != TREE_TYPE (NODE))
3422+
34123423
/* In a FUNCTION_DECL, this is nonzero if this function was defined in
34133424
the class definition. We have saved away the text of the function,
34143425
but have not yet processed it. */

gcc/cp/name-lookup.c

Lines changed: 164 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -2125,6 +2125,10 @@ supplement_binding_1 (cxx_binding *binding, tree decl)
21252125
region to refer only to the namespace to which it already
21262126
refers. */
21272127
ok = false;
2128+
else if (TREE_CODE (bval) == USING_DECL
2129+
&& CONST_DECL_USING_P (decl))
2130+
/* Let the clone hide the using-decl that introduced it. */
2131+
binding->value = decl;
21282132
else
21292133
{
21302134
if (!error_operand_p (bval))
@@ -4540,135 +4544,217 @@ push_class_level_binding (tree name, tree x)
45404544
/* Process and lookup a using decl SCOPE::lookup.name, filling in
45414545
lookup.values & lookup.type. Return true if ok. */
45424546

4543-
static bool
4547+
static tree
45444548
lookup_using_decl (tree scope, name_lookup &lookup)
45454549
{
45464550
tree current = current_scope ();
45474551
bool dependent_p = false;
4552+
tree binfo = NULL_TREE;
4553+
base_kind b_kind = bk_not_base;
4554+
4555+
/* Because C++20 breaks the invariant that only member using-decls
4556+
refer to members and only non-member using-decls refer to
4557+
non-members, we first do the lookups, and then do validation that
4558+
what we found is ok. */
4559+
4560+
if (TREE_CODE (scope) == ENUMERAL_TYPE
4561+
&& cxx_dialect < cxx20
4562+
&& UNSCOPED_ENUM_P (scope)
4563+
&& !TYPE_FUNCTION_SCOPE_P (scope))
4564+
{
4565+
/* PR c++/60265 argued that since C++11 added explicit enum scope, we
4566+
should allow it as meaning the enclosing scope. I don't see any
4567+
justification for this in C++11, but let's keep allowing it. */
4568+
tree ctx = CP_TYPE_CONTEXT (scope);
4569+
if (CLASS_TYPE_P (ctx) == CLASS_TYPE_P (current))
4570+
scope = ctx;
4571+
}
45484572

45494573
if (TREE_CODE (scope) == NAMESPACE_DECL)
45504574
{
45514575
/* Naming a namespace member. */
4552-
if (TYPE_P (current))
4576+
qualified_namespace_lookup (scope, &lookup);
4577+
4578+
if (TYPE_P (current)
4579+
&& (!lookup.value
4580+
|| lookup.type
4581+
|| cxx_dialect < cxx20
4582+
|| TREE_CODE (lookup.value) != CONST_DECL))
45534583
{
45544584
error ("using-declaration for non-member at class scope");
4555-
return false;
4585+
return NULL_TREE;
45564586
}
4557-
4558-
qualified_namespace_lookup (scope, &lookup);
45594587
}
45604588
else if (TREE_CODE (scope) == ENUMERAL_TYPE)
45614589
{
4562-
error ("using-declaration may not name enumerator %<%E::%D%>",
4563-
scope, lookup.name);
4564-
return false;
4590+
/* Naming an enumeration member. */
4591+
if (cxx_dialect < cxx20)
4592+
error ("%<using%> with enumeration scope %q#T "
4593+
"only available with %<-std=c++20%> or %<-std=gnu++20%>",
4594+
scope);
4595+
lookup.value = lookup_enumerator (scope, lookup.name);
45654596
}
45664597
else
45674598
{
4568-
/* Naming a class member. */
4569-
if (!TYPE_P (current))
4570-
{
4571-
error ("using-declaration for member at non-class scope");
4572-
return false;
4573-
}
4599+
/* Naming a class member. This is awkward in C++20, because we
4600+
might be naming an enumerator of an unrelated class. */
45744601

4575-
/* Make sure the name is not invalid */
4602+
/* You cannot using-decl a destructor. */
45764603
if (TREE_CODE (lookup.name) == BIT_NOT_EXPR)
45774604
{
45784605
error ("%<%T::%D%> names destructor", scope, lookup.name);
4579-
return false;
4606+
return NULL_TREE;
45804607
}
45814608

45824609
/* Using T::T declares inheriting ctors, even if T is a typedef. */
45834610
if (MAYBE_CLASS_TYPE_P (scope)
45844611
&& (lookup.name == TYPE_IDENTIFIER (scope)
45854612
|| constructor_name_p (lookup.name, scope)))
45864613
{
4614+
if (!TYPE_P (current))
4615+
{
4616+
error ("non-member using-decl names constructor of %qT", scope);
4617+
return NULL_TREE;
4618+
}
45874619
maybe_warn_cpp0x (CPP0X_INHERITING_CTORS);
45884620
lookup.name = ctor_identifier;
45894621
CLASSTYPE_NON_AGGREGATE (current) = true;
45904622
}
45914623

4592-
/* Cannot introduce a constructor name. */
4593-
if (constructor_name_p (lookup.name, current))
4624+
if (!MAYBE_CLASS_TYPE_P (scope))
4625+
;
4626+
else if (TYPE_P (current))
45944627
{
4595-
error ("%<%T::%D%> names constructor in %qT",
4596-
scope, lookup.name, current);
4597-
return false;
4598-
}
4599-
4600-
/* Member using decls finish processing when completing the
4601-
class. */
4602-
/* From [namespace.udecl]:
4603-
4604-
A using-declaration used as a member-declaration shall refer
4605-
to a member of a base class of the class being defined.
4628+
dependent_p = dependent_scope_p (scope);
4629+
if (!dependent_p)
4630+
{
4631+
binfo = lookup_base (current, scope, ba_any, &b_kind, tf_none);
4632+
gcc_checking_assert (b_kind >= bk_not_base);
46064633

4607-
In general, we cannot check this constraint in a template
4608-
because we do not know the entire set of base classes of the
4609-
current class type. Morover, if SCOPE is dependent, it might
4610-
match a non-dependent base. */
4634+
if (lookup.name == ctor_identifier)
4635+
{
4636+
/* Even if there are dependent bases, SCOPE will not
4637+
be direct base, no matter. */
4638+
if (b_kind < bk_proper_base || !binfo_direct_p (binfo))
4639+
{
4640+
error ("%qT is not a direct base of %qT", scope, current);
4641+
return NULL_TREE;
4642+
}
4643+
}
4644+
else if (b_kind < bk_proper_base)
4645+
binfo = TYPE_BINFO (scope);
4646+
else if (IDENTIFIER_CONV_OP_P (lookup.name)
4647+
&& dependent_type_p (TREE_TYPE (lookup.name)))
4648+
dependent_p = true;
4649+
}
4650+
}
4651+
else
4652+
binfo = TYPE_BINFO (scope);
46114653

4612-
dependent_p = dependent_scope_p (scope);
46134654
if (!dependent_p)
46144655
{
4615-
base_kind b_kind;
4616-
tree binfo = lookup_base (current, scope, ba_any, &b_kind,
4617-
tf_warning_or_error);
4618-
if (b_kind < bk_proper_base)
4656+
if (binfo)
4657+
lookup.value = lookup_member (binfo, lookup.name, /*protect=*/2,
4658+
/*want_type=*/false, tf_none);
4659+
4660+
tree saved_value = lookup.value;
4661+
if (lookup.value
4662+
&& b_kind < bk_proper_base)
46194663
{
4620-
/* If there are dependent bases, scope might resolve at
4621-
instantiation time, even if it isn't exactly one of
4622-
the dependent bases. */
4623-
if (b_kind == bk_same_type || !any_dependent_bases_p ())
4664+
if (cxx_dialect >= cxx20
4665+
&& TREE_CODE (lookup.value) == CONST_DECL)
46244666
{
4625-
error_not_base_type (scope, current);
4626-
return false;
4667+
/* Using an unrelated enum; check access here rather
4668+
than separately for class and non-class using. */
4669+
perform_or_defer_access_check
4670+
(binfo, lookup.value, lookup.value, tf_warning_or_error);
4671+
/* And then if this is a copy from handle_using_decl, look
4672+
through to the original enumerator. */
4673+
if (CONST_DECL_USING_P (lookup.value))
4674+
lookup.value = DECL_ABSTRACT_ORIGIN (lookup.value);
46274675
}
4628-
/* Treat as-if dependent. */
4629-
dependent_p = true;
4676+
else
4677+
lookup.value = NULL_TREE;
46304678
}
4631-
else if (lookup.name == ctor_identifier && !binfo_direct_p (binfo))
4679+
4680+
if (!lookup.value)
46324681
{
4633-
error ("cannot inherit constructors from indirect base %qT",
4634-
scope);
4635-
return false;
4682+
if (!TYPE_P (current))
4683+
{
4684+
error ("using-declaration for member at non-class scope");
4685+
return NULL_TREE;
4686+
}
4687+
4688+
if (b_kind < bk_proper_base)
4689+
{
4690+
if (b_kind == bk_not_base && any_dependent_bases_p ())
4691+
/* Treat as-if dependent. */
4692+
dependent_p = true;
4693+
else
4694+
{
4695+
auto_diagnostic_group g;
4696+
error_not_base_type (scope, current);
4697+
if (saved_value && DECL_IMPLICIT_TYPEDEF_P (saved_value)
4698+
&& (TREE_CODE (TREE_TYPE (saved_value))
4699+
== ENUMERAL_TYPE))
4700+
inform (input_location,
4701+
"did you mean %<using enum %T::%D%>?",
4702+
scope, lookup.name);
4703+
return NULL_TREE;
4704+
}
4705+
}
46364706
}
4637-
else if (IDENTIFIER_CONV_OP_P (lookup.name)
4638-
&& dependent_type_p (TREE_TYPE (lookup.name)))
4639-
dependent_p = true;
4640-
else
4641-
lookup.value = lookup_member (binfo, lookup.name, 0,
4642-
false, tf_warning_or_error);
46434707
}
46444708
}
46454709

4646-
if (!dependent_p)
4710+
/* Did we find anything sane? */
4711+
if (dependent_p)
4712+
;
4713+
else if (!lookup.value)
46474714
{
4648-
if (!lookup.value)
4649-
{
4650-
error ("%qD has not been declared in %qE", lookup.name, scope);
4651-
return false;
4652-
}
4715+
error ("%qD has not been declared in %qD", lookup.name, scope);
4716+
return NULL_TREE;
4717+
}
4718+
else if (TREE_CODE (lookup.value) == TREE_LIST
4719+
/* We can (independently) have ambiguous implicit typedefs. */
4720+
|| (lookup.type && TREE_CODE (lookup.type) == TREE_LIST))
4721+
{
4722+
error ("reference to %qD is ambiguous", lookup.name);
4723+
print_candidates (TREE_CODE (lookup.value) == TREE_LIST
4724+
? lookup.value : lookup.type);
4725+
return NULL_TREE;
4726+
}
4727+
else if (TREE_CODE (lookup.value) == NAMESPACE_DECL)
4728+
{
4729+
error ("using-declaration may not name namespace %qD", lookup.value);
4730+
return NULL_TREE;
4731+
}
46534732

4654-
if (TREE_CODE (lookup.value) == TREE_LIST
4655-
/* We can (independently) have ambiguous implicit typedefs. */
4656-
|| (lookup.type && TREE_CODE (lookup.type) == TREE_LIST))
4657-
{
4658-
error ("reference to %qD is ambiguous", lookup.name);
4659-
print_candidates (TREE_CODE (lookup.value) == TREE_LIST
4660-
? lookup.value : lookup.type);
4661-
return false;
4662-
}
4733+
if (TYPE_P (current))
4734+
{
4735+
/* In class scope. */
46634736

4664-
if (TREE_CODE (lookup.value) == NAMESPACE_DECL)
4737+
/* Cannot introduce a constructor name. */
4738+
if (constructor_name_p (lookup.name, current))
46654739
{
4666-
error ("using-declaration may not name namespace %qD", lookup.value);
4667-
return false;
4740+
error ("%<%T::%D%> names constructor in %qT",
4741+
scope, lookup.name, current);
4742+
return NULL_TREE;
46684743
}
4744+
4745+
if (lookup.value && BASELINK_P (lookup.value))
4746+
/* The binfo from which the functions came does not matter. */
4747+
lookup.value = BASELINK_FUNCTIONS (lookup.value);
46694748
}
46704749

4671-
return true;
4750+
tree using_decl = build_lang_decl (USING_DECL, lookup.name, NULL_TREE);
4751+
USING_DECL_SCOPE (using_decl) = scope;
4752+
USING_DECL_DECLS (using_decl) = lookup.value;
4753+
DECL_DEPENDENT_P (using_decl) = dependent_p;
4754+
if (TYPE_P (current) && b_kind == bk_not_base)
4755+
USING_DECL_UNRELATED_P (using_decl) = true;
4756+
4757+
return using_decl;
46724758
}
46734759

46744760
/* Process "using SCOPE::NAME" in a class scope. Return the
@@ -4682,20 +4768,7 @@ do_class_using_decl (tree scope, tree name)
46824768
return NULL_TREE;
46834769

46844770
name_lookup lookup (name);
4685-
if (!lookup_using_decl (scope, lookup))
4686-
return NULL_TREE;
4687-
4688-
tree found = lookup.value;
4689-
if (found && BASELINK_P (found))
4690-
/* The binfo from which the functions came does not matter. */
4691-
found = BASELINK_FUNCTIONS (found);
4692-
4693-
tree using_decl = build_lang_decl (USING_DECL, lookup.name, NULL_TREE);
4694-
USING_DECL_SCOPE (using_decl) = scope;
4695-
USING_DECL_DECLS (using_decl) = found;
4696-
DECL_DEPENDENT_P (using_decl) = !found;
4697-
4698-
return using_decl;
4771+
return lookup_using_decl (scope, lookup);
46994772
}
47004773

47014774

@@ -5076,7 +5149,8 @@ finish_nonmember_using_decl (tree scope, tree name)
50765149

50775150
name_lookup lookup (name);
50785151

5079-
if (!lookup_using_decl (scope, lookup))
5152+
tree using_decl = lookup_using_decl (scope, lookup);
5153+
if (!using_decl)
50805154
return;
50815155

50825156
/* Emit debug info. */
@@ -5105,8 +5179,6 @@ finish_nonmember_using_decl (tree scope, tree name)
51055179
}
51065180
else
51075181
{
5108-
tree using_decl = build_lang_decl (USING_DECL, lookup.name, NULL_TREE);
5109-
USING_DECL_SCOPE (using_decl) = scope;
51105182
add_decl_expr (using_decl);
51115183

51125184
cxx_binding *binding = find_local_binding (current_binding_level, name);

0 commit comments

Comments
 (0)