Message ID | 20221214174825.2340493-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: local alias in typename in lambda [PR105518] | expand |
On 12/14/22 12:48, Patrick Palka wrote: > We substitute the qualifying scope of a TYPENAME_TYPE directly using > tsubst_aggr_type (so that we can pass entering_scope=true) instead of > going through tsubst, which means we don't properly reuse typedefs > during this substitution. This ends up causing us to reject the below > testcase because we substitute the TYPENAME_TYPE impl::type as if it > were written without the typedef impl for A<t>, and thus we expect the > non-capturing lambda to capture t. > > This patch fixes this by making tsubst_aggr_type delegate typedefs > to tsubst so that get properly reused, and then adjusting the result > appropriately if entering_scope is true. In passing, this refactors > tsubst_aggr_type into two functions, one that's intended to be called > directly and a more minimal one that's intended to be called only from > the RECORD/UNION/ENUMERAL_TYPE cases of tsubst (and contains only the > necessary bits for that call site). > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > trunk? Patch generated with -w to suppress noisy whitespace changes. OK. > PR c++/105518 > > gcc/cp/ChangeLog: > > * pt.cc (tsubst_aggr_type): Handle typedefs by delegating to > tsubst and adjusting the result if entering_scope. Split out > the main part of the function into ... > (tsubst_aggr_type_1) ... here. > (tsubst): Use tsubst_aggr_type_1 instead of tsubst_aggr_type. > Handle TYPE_PTRMEMFUNC_P RECORD_TYPEs here instead of in > tsubst_aggr_type_1. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp0x/lambda/lambda-alias1.C: New test. > --- > gcc/cp/pt.cc | 58 ++++++++++++++----- > .../g++.dg/cpp0x/lambda/lambda-alias1.C | 23 ++++++++ > 2 files changed, 65 insertions(+), 16 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-alias1.C > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > index 81b7787fd3d..86862e56410 100644 > --- a/gcc/cp/pt.cc > +++ b/gcc/cp/pt.cc > @@ -185,6 +185,7 @@ static tree tsubst_template_parms (tree, tree, tsubst_flags_t); > static void tsubst_each_template_parm_constraints (tree, tree, tsubst_flags_t); > tree most_specialized_partial_spec (tree, tsubst_flags_t); > static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int); > +static tree tsubst_aggr_type_1 (tree, tree, tsubst_flags_t, tree, int); > static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree); > static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree); > static bool check_specialization_scope (void); > @@ -13845,23 +13846,49 @@ tsubst_aggr_type (tree t, > if (t == NULL_TREE) > return NULL_TREE; > > - /* If T is an alias template specialization, we want to substitute that > - rather than strip it, especially if it's dependent_alias_template_spec_p. > - It should be OK not to handle entering_scope in this case, since > - DECL_CONTEXT will never be an alias template specialization. We only get > - here with an alias when tsubst calls us for TYPENAME_TYPE. */ > - if (alias_template_specialization_p (t, nt_transparent)) > - return tsubst (t, args, complain, in_decl); > + /* Handle typedefs via tsubst so that they get reused. */ > + if (typedef_variant_p (t)) > + { > + t = tsubst (t, args, complain, in_decl); > + if (t == error_mark_node) > + return error_mark_node; > + > + /* The effect of entering_scope is that when substitution yields a > + dependent specialization A<T>, lookup_template_class prefers to > + return A's primary template type instead of the implicit instantiation. > + So when entering_scope, we mirror this behavior by inspecting > + TYPE_CANONICAL appropriately, taking advantage of the fact that > + lookup_template_class links the two types by setting TYPE_CANONICAL of > + the latter to the former. */ > + if (entering_scope > + && CLASS_TYPE_P (t) > + && dependent_type_p (t) > + && TYPE_CANONICAL (t) == TREE_TYPE (TYPE_TI_TEMPLATE (t))) > + t = TYPE_CANONICAL (t); > + return t; > + } > > switch (TREE_CODE (t)) > { > case RECORD_TYPE: > - if (TYPE_PTRMEMFUNC_P (t)) > - return tsubst (TYPE_PTRMEMFUNC_FN_TYPE (t), args, complain, in_decl); > - > - /* Fall through. */ > case ENUMERAL_TYPE: > case UNION_TYPE: > + return tsubst_aggr_type_1 (t, args, complain, in_decl, entering_scope); > + > + default: > + return tsubst (t, args, complain, in_decl); > + } > +} > + > +/* The part of tsubst_aggr_type that's shared with tsubst. */ > + > +static tree > +tsubst_aggr_type_1 (tree t, > + tree args, > + tsubst_flags_t complain, > + tree in_decl, > + int entering_scope) > +{ > if (TYPE_TEMPLATE_INFO (t) && uses_template_parms (t)) > { > tree argvec; > @@ -13892,10 +13919,6 @@ tsubst_aggr_type (tree t, > else > /* This is not a template type, so there's nothing to do. */ > return t; > - > - default: > - return tsubst (t, args, complain, in_decl); > - } > } > > /* Map from a FUNCTION_DECL to a vec of default argument instantiations, > @@ -15812,9 +15835,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) > switch (code) > { > case RECORD_TYPE: > + if (TYPE_PTRMEMFUNC_P (t)) > + return tsubst (TYPE_PTRMEMFUNC_FN_TYPE (t), args, complain, in_decl); > + /* Fall through. */ > case UNION_TYPE: > case ENUMERAL_TYPE: > - return tsubst_aggr_type (t, args, complain, in_decl, > + return tsubst_aggr_type_1 (t, args, complain, in_decl, > /*entering_scope=*/0); > > case ERROR_MARK: > diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-alias1.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-alias1.C > new file mode 100644 > index 00000000000..4ac69935054 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-alias1.C > @@ -0,0 +1,23 @@ > +// PR c++/105518 > +// { dg-do compile { target c++11 } } > + > +struct integral_constant { > + constexpr operator int() const { return 42; } > +}; > + > +template<int N> > +struct A { > + using type = A; > + static constexpr int value = N; > +}; > + > +template<class T> > +void f(T t) { > + using impl = A<t>; > + [] (int) { > + typename impl::type a; // { dg-bogus "'t' is not captured" } > + return a.value; > + }(0); > +} > + > +template void f(integral_constant);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 81b7787fd3d..86862e56410 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -185,6 +185,7 @@ static tree tsubst_template_parms (tree, tree, tsubst_flags_t); static void tsubst_each_template_parm_constraints (tree, tree, tsubst_flags_t); tree most_specialized_partial_spec (tree, tsubst_flags_t); static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int); +static tree tsubst_aggr_type_1 (tree, tree, tsubst_flags_t, tree, int); static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree); static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree); static bool check_specialization_scope (void); @@ -13845,23 +13846,49 @@ tsubst_aggr_type (tree t, if (t == NULL_TREE) return NULL_TREE; - /* If T is an alias template specialization, we want to substitute that - rather than strip it, especially if it's dependent_alias_template_spec_p. - It should be OK not to handle entering_scope in this case, since - DECL_CONTEXT will never be an alias template specialization. We only get - here with an alias when tsubst calls us for TYPENAME_TYPE. */ - if (alias_template_specialization_p (t, nt_transparent)) - return tsubst (t, args, complain, in_decl); + /* Handle typedefs via tsubst so that they get reused. */ + if (typedef_variant_p (t)) + { + t = tsubst (t, args, complain, in_decl); + if (t == error_mark_node) + return error_mark_node; + + /* The effect of entering_scope is that when substitution yields a + dependent specialization A<T>, lookup_template_class prefers to + return A's primary template type instead of the implicit instantiation. + So when entering_scope, we mirror this behavior by inspecting + TYPE_CANONICAL appropriately, taking advantage of the fact that + lookup_template_class links the two types by setting TYPE_CANONICAL of + the latter to the former. */ + if (entering_scope + && CLASS_TYPE_P (t) + && dependent_type_p (t) + && TYPE_CANONICAL (t) == TREE_TYPE (TYPE_TI_TEMPLATE (t))) + t = TYPE_CANONICAL (t); + return t; + } switch (TREE_CODE (t)) { case RECORD_TYPE: - if (TYPE_PTRMEMFUNC_P (t)) - return tsubst (TYPE_PTRMEMFUNC_FN_TYPE (t), args, complain, in_decl); - - /* Fall through. */ case ENUMERAL_TYPE: case UNION_TYPE: + return tsubst_aggr_type_1 (t, args, complain, in_decl, entering_scope); + + default: + return tsubst (t, args, complain, in_decl); + } +} + +/* The part of tsubst_aggr_type that's shared with tsubst. */ + +static tree +tsubst_aggr_type_1 (tree t, + tree args, + tsubst_flags_t complain, + tree in_decl, + int entering_scope) +{ if (TYPE_TEMPLATE_INFO (t) && uses_template_parms (t)) { tree argvec; @@ -13892,10 +13919,6 @@ tsubst_aggr_type (tree t, else /* This is not a template type, so there's nothing to do. */ return t; - - default: - return tsubst (t, args, complain, in_decl); - } } /* Map from a FUNCTION_DECL to a vec of default argument instantiations, @@ -15812,9 +15835,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) switch (code) { case RECORD_TYPE: + if (TYPE_PTRMEMFUNC_P (t)) + return tsubst (TYPE_PTRMEMFUNC_FN_TYPE (t), args, complain, in_decl); + /* Fall through. */ case UNION_TYPE: case ENUMERAL_TYPE: - return tsubst_aggr_type (t, args, complain, in_decl, + return tsubst_aggr_type_1 (t, args, complain, in_decl, /*entering_scope=*/0); case ERROR_MARK: diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-alias1.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-alias1.C new file mode 100644 index 00000000000..4ac69935054 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-alias1.C @@ -0,0 +1,23 @@ +// PR c++/105518 +// { dg-do compile { target c++11 } } + +struct integral_constant { + constexpr operator int() const { return 42; } +}; + +template<int N> +struct A { + using type = A; + static constexpr int value = N; +}; + +template<class T> +void f(T t) { + using impl = A<t>; + [] (int) { + typename impl::type a; // { dg-bogus "'t' is not captured" } + return a.value; + }(0); +} + +template void f(integral_constant);