This patch addresses a bug in constrained auto deduction (C++/PR115030) by modifying tsubst to eagerly substitute the constraint args of an auto node. This change avoids the complexity of finding outer_targs during do_auto_deduction.
Note that outer_targs cannot be completely removed but will be set to TREE_NULL in all calls except the one for template <typename T, C<T> auto V> struct S { }; Substituting constraint arguments earlier will slightly alter error messages, changing the errors from constraint satisfaction to template substitution / deduction errors. PR c++/115030 gcc/cp/ChangeLog: * constraint.cc (type_deducible_p): Remove outer_targs from do_auto_deduction call. * cp-tree.h (do_auto_deduction): Remove tmpl from the signature. * decl.cc (cp_finish_decl): Remove outer_targs from do_auto_deduction call and the corresponding code to set it from DECL_TI_ARGS. * pt.cc (convert_template_argument): Remove tmpl from do_auto_deduction call. (tsubst): Strengthen the check for using the cache from TEMPLATE_TYPE_DESCENDANTS. Substitute the constraint args and the auto node itself, while retaining all other parameters as is. (unify): Remove tmpl and outer_targs from do_auto_deduction call. (do_auto_deduction): Remove the tmpl argument and its related code, remove the part that sets outer_targs from current_function_decl and change the hack to use TEMPLATE_TYPE_LEVEL. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-placeholder14.C: New test. --- gcc/cp/constraint.cc | 3 +- gcc/cp/cp-tree.h | 3 +- gcc/cp/decl.cc | 11 +-- gcc/cp/pt.cc | 73 ++++++++++--------- .../g++.dg/cpp2a/concepts-placeholder14.C | 20 +++++ 5 files changed, 60 insertions(+), 50 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index ebf4255e5..6ea791812 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -2088,8 +2088,7 @@ type_deducible_p (tree expr, tree type, tree placeholder, tree args, expr = force_paren_expr_uneval (expr); tree deduced_type = do_auto_deduction (type, expr, placeholder, - info.complain, adc_requirement, - /*outer_targs=*/args); + info.complain, adc_requirement); return deduced_type != error_mark_node; } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 4bb3e9c49..961537f9f 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7521,8 +7521,7 @@ extern tree do_auto_deduction (tree, tree, tree, auto_deduction_context = adc_unspecified, tree = NULL_TREE, - int = LOOKUP_NORMAL, - tree = NULL_TREE); + int = LOOKUP_NORMAL); extern tree type_uses_auto (tree); extern tree type_uses_auto_or_concept (tree); extern void append_type_to_template_for_access_check (tree, tree, tree, diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 29616100c..773370b48 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -8567,18 +8567,9 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, enum auto_deduction_context adc = adc_variable_type; if (DECL_DECOMPOSITION_P (decl)) adc = adc_decomp_type; - tree outer_targs = NULL_TREE; - if (PLACEHOLDER_TYPE_CONSTRAINTS_INFO (auto_node) - && DECL_LANG_SPECIFIC (decl) - && DECL_TEMPLATE_INFO (decl) - && !DECL_FUNCTION_SCOPE_P (decl)) - /* The outer template arguments might be needed for satisfaction. - (For function scope variables, do_auto_deduction will obtain the - outer template arguments from current_function_decl.) */ - outer_targs = DECL_TI_ARGS (decl); type = TREE_TYPE (decl) = do_auto_deduction (type, d_init, auto_node, tf_warning_or_error, adc, - outer_targs, flags); + NULL_TREE, flags); if (type == error_mark_node) return; if (TREE_CODE (type) == FUNCTION_TYPE) diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index d1316483e..fdd641e5c 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -8781,7 +8781,7 @@ convert_template_argument (tree parm, else if (tree a = type_uses_auto (t)) { t = do_auto_deduction (t, arg, a, complain, adc_unify, args, - LOOKUP_IMPLICIT, /*tmpl=*/in_decl); + LOOKUP_IMPLICIT); if (t == error_mark_node) return error_mark_node; } @@ -16506,8 +16506,8 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (tree d = TEMPLATE_TYPE_DESCENDANTS (t)) if (TEMPLATE_PARM_LEVEL (d) == TEMPLATE_TYPE_LEVEL (t) - levels - && (code == TEMPLATE_TYPE_PARM - || TEMPLATE_TEMPLATE_PARM_SIMPLE_P (t))) + && ((code == TEMPLATE_TYPE_PARM && !PLACEHOLDER_TYPE_CONSTRAINTS_INFO(t)) + || (code == TEMPLATE_TEMPLATE_PARM && TEMPLATE_TEMPLATE_PARM_SIMPLE_P (t)))) /* Cache lowering a type parameter or a simple template template parameter. */ r = TREE_TYPE (d); @@ -16515,6 +16515,32 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (!r) { r = copy_type (t); + + if (code == TEMPLATE_TYPE_PARM) + if (tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t)) + { + tree cparms = TREE_PURPOSE (ci); + + tree auto_vec = make_tree_vec (1); + TREE_VEC_ELT (auto_vec, 0) = r; + tree extra = template_parms_to_args (cparms); + extra = add_outermost_template_args (extra, auto_vec); + extra = get_innermost_template_args(extra, level - levels); + tree full_args = add_to_template_args (args, extra); + + tree cexpr + = tsubst_constraint (TREE_VALUE (ci), full_args, complain, in_decl); + + if (cexpr == error_mark_node) + return error_mark_node; + + PLACEHOLDER_TYPE_CONSTRAINTS_INFO (r) + = build_tree_list(cparms, cexpr); + + /* reduce_template_parm_level needs proper canonical type */ + TYPE_CANONICAL (r) = canonical_type_parameter (r); + } + TEMPLATE_TYPE_PARM_INDEX (r) = reduce_template_parm_level (TEMPLATE_TYPE_PARM_INDEX (t), r, levels, args, complain); @@ -16523,12 +16549,6 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) TYPE_POINTER_TO (r) = NULL_TREE; TYPE_REFERENCE_TO (r) = NULL_TREE; - if (code == TEMPLATE_TYPE_PARM) - if (tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t)) - /* Propagate constraints on placeholders since they are - only instantiated during satisfaction. */ - PLACEHOLDER_TYPE_CONSTRAINTS_INFO (r) = ci; - if (TYPE_STRUCTURAL_EQUALITY_P (t)) SET_TYPE_STRUCTURAL_EQUALITY (r); else @@ -24915,9 +24935,8 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, if (tree a = type_uses_auto (tparm)) { tparm = do_auto_deduction (tparm, arg, a, - complain, adc_unify, targs, - LOOKUP_NORMAL, - TPARMS_PRIMARY_TEMPLATE (tparms)); + complain, adc_unify, NULL_TREE, + LOOKUP_NORMAL); if (tparm == error_mark_node) return 1; } @@ -31116,8 +31135,7 @@ do_auto_deduction (tree type, tree init, tree auto_node, tsubst_flags_t complain /* = tf_warning_or_error */, auto_deduction_context context /* = adc_unspecified */, tree outer_targs /* = NULL_TREE */, - int flags /* = LOOKUP_NORMAL */, - tree tmpl /* = NULL_TREE */) + int flags /* = LOOKUP_NORMAL */) { if (type == error_mark_node || init == error_mark_node) return error_mark_node; @@ -31287,28 +31305,7 @@ do_auto_deduction (tree type, tree init, tree auto_node, return type; } - if (context == adc_return_type - || context == adc_variable_type - || context == adc_decomp_type) - if (tree fn = current_function_decl) - if (DECL_TEMPLATE_INFO (fn) || LAMBDA_FUNCTION_P (fn)) - { - outer_targs = DECL_TEMPLATE_INFO (fn) - ? DECL_TI_ARGS (fn) : NULL_TREE; - if (LAMBDA_FUNCTION_P (fn)) - { - /* As in satisfy_declaration_constraints. */ - tree regen_args = lambda_regenerating_args (fn); - if (outer_targs) - outer_targs = add_to_template_args (regen_args, outer_targs); - else - outer_targs = regen_args; - } - } - tree full_targs = outer_targs; - if (context == adc_unify && tmpl) - full_targs = add_outermost_template_args (tmpl, full_targs); full_targs = add_to_template_args (full_targs, targs); /* HACK: Compensate for callers not always communicating all levels of @@ -31318,9 +31315,13 @@ do_auto_deduction (tree type, tree init, tree auto_node, these missing levels, but this hack otherwise allows us to handle a large subset of possible constraints (including all non-dependent constraints). */ - if (int missing_levels = (TEMPLATE_TYPE_ORIG_LEVEL (auto_node) + if (int missing_levels = (TEMPLATE_TYPE_LEVEL (auto_node) - TMPL_ARGS_DEPTH (full_targs))) { + /* For example the case that + template <typename T> is_same<bool> auto x = true; + will end up here, because it will be deduced before instantiation */ + gcc_assert (missing_levels > 0); tree dummy_levels = make_tree_vec (missing_levels); for (int i = 0; i < missing_levels; ++i) TREE_VEC_ELT (dummy_levels, i) = make_tree_vec (0); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C new file mode 100644 index 000000000..a8bd48e39 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C @@ -0,0 +1,20 @@ +// PR c++/115030 +// { dg-do compile { target c++20 } } + +template<typename T, typename U> +concept C = __is_same(T, U); + +template <typename T> +struct s { +}; + +template <typename T> +char v = 'a'; + +template<typename T> +C<T> auto v<s<T>> = 'c'; + +int main() { + v<s<char>> = 'b'; + return 0; +} -- 2.45.2