diff options
author | Patrick Palka <ppalka@redhat.com> | 2024-03-01 12:50:18 -0500 |
---|---|---|
committer | Patrick Palka <ppalka@redhat.com> | 2024-03-01 12:50:18 -0500 |
commit | a6a1920b592b58c38137c5c891b3bbb02b084f38 (patch) | |
tree | 615cf08dc9ce23225989d29031de2eb40988da9b | |
parent | 4894c82b0c3cf0d6ec4bc1e96709b6140ec11f6e (diff) | |
download | gcc-a6a1920b592b58c38137c5c891b3bbb02b084f38.zip gcc-a6a1920b592b58c38137c5c891b3bbb02b084f38.tar.gz gcc-a6a1920b592b58c38137c5c891b3bbb02b084f38.tar.bz2 |
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) <case TEMPLATE_TYPE_PARM>: 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 <jason@redhat.com>
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/parser.cc | 11 | ||||
-rw-r--r-- | gcc/cp/pt.cc | 36 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp23/auto-fncast16.C | 12 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp23/auto-fncast17.C | 15 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp23/auto-fncast18.C | 69 |
6 files changed, 142 insertions, 2 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 06c2637..4469d96 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 3ee9d49..a310b9e 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 2803824..c4bc54a 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 0000000..e2c13f6 --- /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 0000000..25186df --- /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 0000000..4656723 --- /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 <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; + } + +} + +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>&&>)); |