Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk and perhaps 13?
-- >8 -- 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) and auto{x}. PR c++/110025 PR c++/114138 gcc/cp/ChangeLog: * cp-tree.h (make_cast_auto): Declare. * parser.cc (cp_parser_functional_cast): Replace a parsed auto 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) <case TEMPLATE_TYPE_PARM>: Generalized CTAD placeholder handling to all level-less autos. (make_cast_auto): Define. (do_auto_deduction): Handle deduction of a level-less non-CTAD 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. --- gcc/cp/cp-tree.h | 1 + gcc/cp/parser.cc | 11 ++++ gcc/cp/pt.cc | 31 +++++++++- gcc/testsuite/g++.dg/cpp23/auto-fncast16.C | 12 ++++ gcc/testsuite/g++.dg/cpp23/auto-fncast17.C | 15 +++++ gcc/testsuite/g++.dg/cpp23/auto-fncast18.C | 71 ++++++++++++++++++++++ 6 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast16.C create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast17.C create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast18.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 04c3aa6cd91..6f1da1c7bad 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7476,6 +7476,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 3ee9d49fb8e..1e518e6ef51 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} are represented using 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 2803824d11e..620fe5cdbfa 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,14 @@ 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) { + /* Level-less auto must be replaced via do_auto_deduction. */ + gcc_checking_assert (is_auto (t)); tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (t); + if (!tmpl) + return t; + tmpl = tsubst_expr (tmpl, args, complain, in_decl); if (TREE_CODE (tmpl) == TEMPLATE_TEMPLATE_PARM) tmpl = TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (tmpl); @@ -29311,6 +29317,17 @@ template_placeholder_p (tree t) return is_auto (t) && CLASS_PLACEHOLDER_TEMPLATE (t); } +/* Return an auto for an explicit cast, e.g. auto(x) or 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,7 +31230,15 @@ do_auto_deduction (tree type, tree init, tree auto_node, } } - if (TEMPLATE_TYPE_LEVEL (auto_node) == 1) + if (TEMPLATE_TYPE_LEVEL (auto_node) == 0) + { + /* Level-less auto can't be replaced via tsubst, so do it directly. */ + gcc_checking_assert (type == auto_node); + gcc_checking_assert (!TYPE_QUALS (type)); + gcc_checking_assert (TREE_VEC_LENGTH (targs) == 1); + return TREE_VEC_ELT (targs, 0); + } + else 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). */; else if (context == adc_unify) 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 00000000000..e2c13f6b050 --- /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<auto V, class = decltype(auto(V)), class = decltype(auto{V})> +struct A { }; + +template<auto V> +A<V> 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 00000000000..25186dfdbf2 --- /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<class...> struct tuple; + +template<auto V> +using constant_t = int; + +template<auto... V> +using constants_t = tuple<constant_t<auto(V)>...>; + +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 00000000000..bded5caebf4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast18.C @@ -0,0 +1,71 @@ +// PR c++/114138 +// { dg-do compile { target c++23 } } + +namespace std { + template <class T> + T&& declval() noexcept requires true; + + template <class> + void declval() noexcept; + + namespace detail { + struct none_such; + template <class> + using none_such_t = none_such; + + template <class T> + extern const none_such_t<T> _getter_for; + + template <class T> + using _decay_t = decltype(auto(declval<T>())); + + static_assert(__is_same_as(_decay_t<void>, void)); + } + + template <const auto& Fn, class... Args> + using _result_of_t = decltype(Fn(declval<Args>()...)); + + template <unsigned I, class Tuple> + using tuple_element_t = _result_of_t<detail::_getter_for<detail::_decay_t<Tuple>>, char(*)[I+1], Tuple>; + + template <class First, class Second> + struct pair { + First first; + Second second; + }; + + template <class> + inline constexpr bool _is_pair = false; + + template <class First, class Second> + inline constexpr bool _is_pair<pair<First, Second>> = true; + + template <class T> + concept Pair = _is_pair<decltype(auto(std::declval<T>()))>; + + template <unsigned I, Pair P> + requires (I <= 1) + decltype(auto) get(P&& p) noexcept { + if constexpr (I == 0) { + return (static_cast<P&&>(p).first); + } else { + return (static_cast<P&&>(p).second); + } + } + + namespace detail { + inline constexpr auto _pair_getter = + []<unsigned J, class Pair>(char(*)[J], Pair&& p) noexcept -> decltype(auto) { + return std::get<J-1>(static_cast<Pair&&>(p)); + }; + + template <class First, class Second> + inline constexpr auto _getter_for<pair<First, Second>> = _pair_getter; + } + +} + +int main() { + static_assert(__is_same_as(int&, std::tuple_element_t<0, std::pair<int, float>&>)); + static_assert(__is_same_as(float&&, std::tuple_element_t<1, std::pair<int, float>&&>)); +} -- 2.44.0.rc1.15.g4fc51f00ef