diff options
author | Jason Merrill <jason@redhat.com> | 2020-08-06 02:40:10 -0400 |
---|---|---|
committer | Jason Merrill <jason@redhat.com> | 2020-08-18 16:21:54 -0400 |
commit | 7fc49a5777943aab11e227136d00a836f28f12b2 (patch) | |
tree | 54b4f832d97535d644493ec5ab7a4f7a475c377b | |
parent | 6342cee8801f191466b71116d004e8ccb812caaa (diff) | |
download | gcc-7fc49a5777943aab11e227136d00a836f28f12b2.tar.gz |
c++: Handle enumerator in C++20 alias CTAD. [PR96199]
To form a deduction guide for an alias template, we substitute the template
arguments from the pattern into the deduction guide for the underlying
class. In the case of B(A1<X>), that produces B(A1<B<T,1>::X>) -> B<T,1>.
But since an enumerator doesn't have its own template info, and B<T,1> is a
dependent scope, trying to look up B<T,1>::X fails and we crash. So we need
to produce a SCOPE_REF instead.
And trying to use the members of the template class is wrong for other
members, as well, as it gives a nonsensical result if the class is
specialized.
gcc/cp/ChangeLog:
PR c++/96199
* pt.c (maybe_dependent_member_ref): New.
(tsubst_copy) [CONST_DECL]: Use it.
[VAR_DECL]: Likewise.
(tsubst_aggr_type): Handle nested type.
gcc/testsuite/ChangeLog:
PR c++/96199
* g++.dg/cpp2a/class-deduction-alias4.C: New test.
-rw-r--r-- | gcc/cp/pt.c | 43 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/class-deduction-alias4.C | 44 |
2 files changed, 87 insertions, 0 deletions
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 45efe3839d0..3678823d603 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -13396,6 +13396,17 @@ tsubst_aggr_type (tree t, complain, in_decl); if (argvec == error_mark_node) r = error_mark_node; + else if (cxx_dialect >= cxx2a && dependent_scope_p (context)) + { + /* See maybe_dependent_member_ref. */ + tree name = TYPE_IDENTIFIER (t); + tree fullname = name; + if (instantiates_primary_template_p (t)) + fullname = build_nt (TEMPLATE_ID_EXPR, name, + INNERMOST_TEMPLATE_ARGS (argvec)); + return build_typename_type (context, name, fullname, + typename_type); + } else { r = lookup_template_class (t, argvec, in_decl, context, @@ -16314,6 +16325,32 @@ tsubst_init (tree init, tree decl, tree args, return init; } +/* If T is a reference to a dependent member of the current instantiation C and + we are trying to refer to that member in a partial instantiation of C, + return a SCOPE_REF; otherwise, return NULL_TREE. + + This can happen when forming a C++20 alias template deduction guide, as in + PR96199. */ + +static tree +maybe_dependent_member_ref (tree t, tree args, tsubst_flags_t complain, + tree in_decl) +{ + if (cxx_dialect < cxx2a) + return NULL_TREE; + + tree ctx = context_for_name_lookup (t); + if (!CLASS_TYPE_P (ctx)) + return NULL_TREE; + + ctx = tsubst (ctx, args, complain, in_decl); + if (dependent_scope_p (ctx)) + return build_qualified_name (NULL_TREE, ctx, DECL_NAME (t), + /*template_p=*/false); + + return NULL_TREE; +} + /* Like tsubst, but deals with expressions. This function just replaces template parms; to finish processing the resultant expression, use tsubst_copy_and_build or tsubst_expr. */ @@ -16372,6 +16409,9 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (args == NULL_TREE) return scalar_constant_value (t); + if (tree ref = maybe_dependent_member_ref (t, args, complain, in_decl)) + return ref; + /* Unfortunately, we cannot just call lookup_name here. Consider: @@ -16422,6 +16462,9 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) return t; case VAR_DECL: + if (tree ref = maybe_dependent_member_ref (t, args, complain, in_decl)) + return ref; + gcc_fallthrough(); case FUNCTION_DECL: if (DECL_LANG_SPECIFIC (t) && DECL_TEMPLATE_INFO (t)) r = tsubst (t, args, complain, in_decl); diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias4.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias4.C new file mode 100644 index 00000000000..f2c3ffda85a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias4.C @@ -0,0 +1,44 @@ +// PR c++/96199 +// { dg-do compile { target c++2a } } + +template<int> struct A1 { }; +template<int&> struct A2 { }; +template<class> struct A3 { }; + +int i; +template<typename V, int> struct B { + enum E { X }; + B(A1<X>, V) { } + + constexpr static V& ir = i; + B(A2<ir>, V) { } + + B(A3<E>, V); +}; + +// template<class T, int I> B(A1<B<T,I>::X>,T) -> B<T,I>; +// template<class T, int I> B(A2<B<T,I>::ir>,T) -> B<T,I>; +// template<class T, int I> B(A3<typename B<T,I>::E>,T) -> B<T,I>; + +template <typename T> using U = B<T, 1>; + +// template<class T> B(A1<B<T,1>::X>,T) -> B<T,1>; +// template<class T> B(A2<B<T,1>::ir>,T) -> B<T,1>; +// template<class T> B(A3<typename B<T,1>::E>,T) -> B<T,1>; + +int j; +template <> struct B<int, 1> { + using V = int; + + enum E { X = 1 }; + B(A1<X>, V) { } + + constexpr static V& ir = j; + B(A2<ir>, V) { } + + B(A3<E>, V); +}; + +U u1 { A1<1>(), 42 }; +U u2 { A2<j>(), 42 }; +U u3 { A3<U<int>::E>(), 42 }; |