summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Palka <ppalka@redhat.com>2024-03-01 12:50:18 -0500
committerPatrick Palka <ppalka@redhat.com>2024-03-01 12:50:18 -0500
commita6a1920b592b58c38137c5c891b3bbb02b084f38 (patch)
tree615cf08dc9ce23225989d29031de2eb40988da9b
parent4894c82b0c3cf0d6ec4bc1e96709b6140ec11f6e (diff)
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.h1
-rw-r--r--gcc/cp/parser.cc11
-rw-r--r--gcc/cp/pt.cc36
-rw-r--r--gcc/testsuite/g++.dg/cpp23/auto-fncast16.C12
-rw-r--r--gcc/testsuite/g++.dg/cpp23/auto-fncast17.C15
-rw-r--r--gcc/testsuite/g++.dg/cpp23/auto-fncast18.C69
6 files changed, 142 insertions, 2 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 06c2637d87a..4469d965ef0 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 3ee9d49fb8e..a310b9e8c07 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 2803824d11e..c4bc54a8fdb 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 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..4656723684f
--- /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>&&>));