From: Patrick Palka Date: Fri, 1 Mar 2024 17:50:18 +0000 (-0500) Subject: c++: auto(x) partial substitution [PR110025, PR114138] X-Git-Tag: basepoints/gcc-15~879 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a6a1920b592b58c38137c5c891b3bbb02b084f38;p=thirdparty%2Fgcc.git c++: auto(x) partial substitution [PR110025, PR114138] In r12-6773-g09845ad7569bac we gave CTAD placeholders a level of 0 and ensured we never replaced them via tsubst. It turns out that autos representing an explicit cast need the same treatment and for the same reason: such autos appear in an expression context and so their level gets easily messed up after partial substitution, leading to premature replacement via an incidental tsubst instead of via do_auto_deduction. This patch fixes this by extending the r12-6773 approach to auto(x). PR c++/110025 PR c++/114138 gcc/cp/ChangeLog: * cp-tree.h (make_cast_auto): Declare. * parser.cc (cp_parser_functional_cast): If the type is an auto, replace it with a level-less one via make_cast_auto. * pt.cc (find_parameter_packs_r): Don't treat level-less auto as a type parameter pack. (tsubst) : Generalize CTAD placeholder auto handling to all level-less autos. (make_cast_auto): Define. (do_auto_deduction): Handle replacement of a level-less auto. gcc/testsuite/ChangeLog: * g++.dg/cpp23/auto-fncast16.C: New test. * g++.dg/cpp23/auto-fncast17.C: New test. * g++.dg/cpp23/auto-fncast18.C: New test. Reviewed-by: Jason Merrill --- diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 06c2637d87a1..4469d965ef08 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7477,6 +7477,7 @@ extern tree make_decltype_auto (void); extern tree make_constrained_auto (tree, tree); extern tree make_constrained_decltype_auto (tree, tree); extern tree make_template_placeholder (tree); +extern tree make_cast_auto (void); extern bool template_placeholder_p (tree); extern bool ctad_template_p (tree); extern bool unparenthesized_id_or_class_member_access_p (tree); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 3ee9d49fb8ea..a310b9e8c07c 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -33314,6 +33314,17 @@ cp_parser_functional_cast (cp_parser* parser, tree type) if (!type) type = error_mark_node; + if (TREE_CODE (type) == TYPE_DECL + && is_auto (TREE_TYPE (type))) + type = TREE_TYPE (type); + + if (is_auto (type) + && !AUTO_IS_DECLTYPE (type) + && !PLACEHOLDER_TYPE_CONSTRAINTS (type) + && !CLASS_PLACEHOLDER_TEMPLATE (type)) + /* auto(x) and auto{x} need to use a level-less auto. */ + type = make_cast_auto (); + if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) { cp_lexer_set_source_position (parser->lexer); diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 2803824d11e1..c4bc54a8fdb3 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -3921,7 +3921,8 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data) parameter pack (14.6.3), or the type-specifier-seq of a type-id that is a pack expansion, the invented template parameter is a template parameter pack. */ - if (ppd->type_pack_expansion_p && is_auto (t)) + if (ppd->type_pack_expansion_p && is_auto (t) + && TEMPLATE_TYPE_LEVEL (t) != 0) TEMPLATE_TYPE_PARAMETER_PACK (t) = true; if (TEMPLATE_TYPE_PARAMETER_PACK (t)) parameter_pack_p = true; @@ -16297,9 +16298,19 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) } case TEMPLATE_TYPE_PARM: - if (template_placeholder_p (t)) + if (TEMPLATE_TYPE_LEVEL (t) == 0) { + /* This is either an ordinary level-less auto or a CTAD placeholder + auto. These get replaced only via do_auto_deduction which, in the + ordinary case, temporarily overrides its level to 1 before calling + tsubst. CTAD placeholders are replaced via do_class_deduction. */ + gcc_checking_assert (is_auto (t)); tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (t); + if (!tmpl) + /* Ordinary level-less auto has nothing to substitute. */ + return t; + + /* Substitute the template of this CTAD placeholder. */ tmpl = tsubst_expr (tmpl, args, complain, in_decl); if (TREE_CODE (tmpl) == TEMPLATE_TEMPLATE_PARM) tmpl = TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (tmpl); @@ -29311,6 +29322,17 @@ template_placeholder_p (tree t) return is_auto (t) && CLASS_PLACEHOLDER_TEMPLATE (t); } +/* Return an auto for an explicit cast expression auto(x). + Like CTAD placeholders, these have level 0 so that they're + not accidentally replaced via tsubst and are always directly + resolved via do_auto_deduction. */ + +tree +make_cast_auto () +{ + return make_auto_1 (auto_identifier, true, /*level=*/0); +} + /* Make a "constrained auto" type-specifier. This is an auto or decltype(auto) type with constraints that must be associated after deduction. The constraint is formed from the given concept CON @@ -31213,6 +31235,16 @@ do_auto_deduction (tree type, tree init, tree auto_node, } } + if (TEMPLATE_TYPE_LEVEL (auto_node) == 0) + { + /* Substitute this level-less auto via tsubst by temporarily + overriding its level to 1. */ + TEMPLATE_TYPE_LEVEL (auto_node) = 1; + type = tsubst (type, targs, complain, NULL_TREE); + TEMPLATE_TYPE_LEVEL (auto_node) = 0; + return type; + } + if (TEMPLATE_TYPE_LEVEL (auto_node) == 1) /* The outer template arguments are already substituted into type (but we still may have used them for constraint checking above). */; diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast16.C b/gcc/testsuite/g++.dg/cpp23/auto-fncast16.C new file mode 100644 index 000000000000..e2c13f6b050b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast16.C @@ -0,0 +1,12 @@ +// PR c++/110025 +// { dg-do compile { target c++23 } } + +template +struct A { }; + +template +A f(); + +int main() { + f<0>(); +} diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast17.C b/gcc/testsuite/g++.dg/cpp23/auto-fncast17.C new file mode 100644 index 000000000000..25186dfdbf23 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast17.C @@ -0,0 +1,15 @@ +// PR c++/110025 +// { dg-do compile { target c++23 } } + +template struct tuple; + +template +using constant_t = int; + +template +using constants_t = tuple...>; + +using ty0 = constants_t<>; +using ty1 = constants_t<1>; +using ty2 = constants_t<1, 2>; +using ty3 = constants_t<1, 2, 3>; diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast18.C b/gcc/testsuite/g++.dg/cpp23/auto-fncast18.C new file mode 100644 index 000000000000..4656723684f9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast18.C @@ -0,0 +1,69 @@ +// PR c++/114138 +// { dg-do compile { target c++23 } } + +namespace std { + template + T&& declval() noexcept requires true; + + template + void declval() noexcept; + + namespace detail { + struct none_such; + template + using none_such_t = none_such; + + template + extern const none_such_t _getter_for; + + template + using _decay_t = decltype(auto(declval())); + + static_assert(__is_same_as(_decay_t, void)); + } + + template + using _result_of_t = decltype(Fn(declval()...)); + + template + using tuple_element_t = _result_of_t>, char(*)[I+1], Tuple>; + + template + struct pair { + First first; + Second second; + }; + + template + inline constexpr bool _is_pair = false; + + template + inline constexpr bool _is_pair> = true; + + template + concept Pair = _is_pair()))>; + + template + requires (I <= 1) + decltype(auto) get(P&& p) noexcept { + if constexpr (I == 0) { + return (static_cast(p).first); + } else { + return (static_cast(p).second); + } + } + + namespace detail { + inline constexpr auto _pair_getter = + [](char(*)[J], Pair&& p) noexcept -> decltype(auto) { + return std::get(static_cast(p)); + }; + + template + inline constexpr auto _getter_for> = _pair_getter; + } + +} + +static_assert(__is_same_as(int&, std::tuple_element_t<0, std::pair&>)); +static_assert(__is_same_as(float&&, std::tuple_element_t<1, std::pair&&>));