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. 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); -- 2.39.0.56.g57e2c6ebbe