aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2016-11-09 15:02:50 -0500
committerJason Merrill <jason@gcc.gnu.org>2016-11-09 15:02:50 -0500
commit4a826ca6feb3c7ec3ef6287214a3f2820222c97e (patch)
tree760d23d9253039365e07de038db17dfe9aada879 /gcc
parentff1f317b3c1fdcfb061e20f3474f77c183189830 (diff)
downloadgcc-4a826ca6feb3c7ec3ef6287214a3f2820222c97e.zip
gcc-4a826ca6feb3c7ec3ef6287214a3f2820222c97e.tar.gz
gcc-4a826ca6feb3c7ec3ef6287214a3f2820222c97e.tar.bz2
Implement P0127R2, Declaring non-type parameters with auto.
gcc/cp/ * cp-tree.h (enum auto_deduction_context): Add adc_unify. * decl.c (grokdeclarator): Allow 'auto' in C++17 template non-type parameter types. * pt.c (do_auto_deduction): Add outer_targs parameter. (convert_template_argument): Call do_auto_deduction. If adc_unify, don't give up on dependent init. (unify): Likewise. In C++17, walk into the type of a TEMPLATE_PARM_INDEX. (for_each_template_parm): Add any_fn parameter. (struct pair_fn_data): Likewise. (for_each_template_parm_r): Call it for any tree. In C++17, walk into the type of a TEMPLATE_PARM_INDEX. (zero_r, array_deduction_r, try_array_deduction): New. (type_unification_real): Call try_array_deduction. (get_partial_spec_bindings): Likewise. gcc/c-family/ * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_template_auto. From-SVN: r242017
Diffstat (limited to 'gcc')
-rw-r--r--gcc/c-family/ChangeLog4
-rw-r--r--gcc/c-family/c-cppbuiltin.c1
-rw-r--r--gcc/cp/ChangeLog19
-rw-r--r--gcc/cp/cp-tree.h4
-rw-r--r--gcc/cp/decl.c3
-rw-r--r--gcc/cp/pt.c142
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/auto9.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C6
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/nontype-auto1.C13
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/nontype-auto2.C10
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C15
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/nontype-auto4.C14
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/nontype-auto5.C15
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/nontype-auto6.C8
-rw-r--r--gcc/testsuite/g++.dg/template/partial5.C2
15 files changed, 240 insertions, 18 deletions
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index 89b3043..55c2e60 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,7 @@
+2016-11-09 Jason Merrill <jason@redhat.com>
+
+ * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_template_auto.
+
2016-11-09 Jakub Jelinek <jakub@redhat.com>
* c-ubsan.c (ubsan_instrument_shift): Handle split
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index 55dbf44..70eade1 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -942,6 +942,7 @@ c_cpp_builtins (cpp_reader *pfile)
cpp_define (pfile, "__cpp_aggregate_bases=201603");
cpp_define (pfile, "__cpp_deduction_guides=201606");
cpp_define (pfile, "__cpp_noexcept_function_type=201510");
+ cpp_define (pfile, "__cpp_template_auto=201606");
}
if (flag_concepts)
cpp_define (pfile, "__cpp_concepts=201507");
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index f325ccc..e220c5f 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,22 @@
+2016-11-09 Jason Merrill <jason@redhat.com>
+
+ Implement P0127R2, Declaring non-type parameters with auto.
+ * cp-tree.h (enum auto_deduction_context): Add adc_unify.
+ * decl.c (grokdeclarator): Allow 'auto' in C++17 template non-type
+ parameter types.
+ * pt.c (do_auto_deduction): Add outer_targs parameter.
+ (convert_template_argument): Call do_auto_deduction. If adc_unify,
+ don't give up on dependent init.
+ (unify): Likewise. In C++17, walk into the type of a
+ TEMPLATE_PARM_INDEX.
+ (for_each_template_parm): Add any_fn parameter.
+ (struct pair_fn_data): Likewise.
+ (for_each_template_parm_r): Call it for any tree. In C++17, walk
+ into the type of a TEMPLATE_PARM_INDEX.
+ (zero_r, array_deduction_r, try_array_deduction): New.
+ (type_unification_real): Call try_array_deduction.
+ (get_partial_spec_bindings): Likewise.
+
2016-11-07 Jason Merrill <jason@redhat.com>
Implement P0012R1, Make exception specifications part of the type
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 20b52ad..9b5b5bc 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5163,6 +5163,7 @@ enum auto_deduction_context
adc_unspecified, /* Not given */
adc_variable_type, /* Variable initializer deduction */
adc_return_type, /* Return type deduction */
+ adc_unify, /* Template argument deduction */
adc_requirement /* Argument dedution constraint */
};
@@ -6088,7 +6089,8 @@ extern tree make_template_placeholder (tree);
extern tree do_auto_deduction (tree, tree, tree);
extern tree do_auto_deduction (tree, tree, tree,
tsubst_flags_t,
- auto_deduction_context);
+ auto_deduction_context,
+ tree = NULL_TREE);
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.c b/gcc/cp/decl.c
index c0321f9..bd37faa 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -11135,7 +11135,8 @@ grokdeclarator (const cp_declarator *declarator,
if (ctype || in_namespace)
error ("cannot use %<::%> in parameter declaration");
- if (type_uses_auto (type))
+ if (type_uses_auto (type)
+ && !(cxx_dialect >= cxx1z && template_parm_flag))
{
if (cxx_dialect >= cxx14)
error ("%<auto%> parameter not permitted in this context");
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 3df71dd..64e566e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -161,7 +161,7 @@ static tree convert_nontype_argument (tree, tree, tsubst_flags_t);
static tree convert_template_argument (tree, tree, tree,
tsubst_flags_t, int, tree);
static tree for_each_template_parm (tree, tree_fn_t, void*,
- hash_set<tree> *, bool);
+ hash_set<tree> *, bool, tree_fn_t = NULL);
static tree expand_template_argument_pack (tree);
static tree build_template_parm_index (int, int, int, tree, tree);
static bool inline_needs_template_parms (tree, bool);
@@ -7299,6 +7299,13 @@ convert_template_argument (tree parm,
{
tree t = tsubst (TREE_TYPE (parm), args, complain, in_decl);
+ if (tree a = type_uses_auto (t))
+ {
+ t = do_auto_deduction (t, arg, a, complain, adc_unspecified);
+ if (t == error_mark_node)
+ return error_mark_node;
+ }
+
if (invalid_nontype_parm_type_p (t, complain))
return error_mark_node;
@@ -8789,6 +8796,7 @@ lookup_and_finish_template_variable (tree templ, tree targs,
struct pair_fn_data
{
tree_fn_t fn;
+ tree_fn_t any_fn;
void *data;
/* True when we should also visit template parameters that occur in
non-deduced contexts. */
@@ -8811,11 +8819,15 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d)
do \
{ \
result = for_each_template_parm (NODE, fn, data, pfd->visited, \
- pfd->include_nondeduced_p); \
+ pfd->include_nondeduced_p, \
+ pfd->any_fn); \
if (result) goto out; \
} \
while (0)
+ if (pfd->any_fn && (*pfd->any_fn)(t, data))
+ return t;
+
if (TYPE_P (t)
&& (pfd->include_nondeduced_p || TREE_CODE (t) != TYPENAME_TYPE))
WALK_SUBTREE (TYPE_CONTEXT (t));
@@ -8880,7 +8892,8 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d)
if (pfd->include_nondeduced_p
&& for_each_template_parm (TYPE_VALUES_RAW (t), fn, data,
pfd->visited,
- pfd->include_nondeduced_p))
+ pfd->include_nondeduced_p,
+ pfd->any_fn))
return error_mark_node;
break;
@@ -8911,6 +8924,12 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d)
return t;
else if (!fn)
return t;
+
+ /* In C++17 we can deduce a type argument from the type of a non-type
+ argument. */
+ if (cxx_dialect >= cxx1z
+ && TREE_CODE (t) == TEMPLATE_PARM_INDEX)
+ WALK_SUBTREE (TREE_TYPE (t));
break;
case TEMPLATE_DECL:
@@ -8984,13 +9003,15 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d)
static tree
for_each_template_parm (tree t, tree_fn_t fn, void* data,
hash_set<tree> *visited,
- bool include_nondeduced_p)
+ bool include_nondeduced_p,
+ tree_fn_t any_fn)
{
struct pair_fn_data pfd;
tree result;
/* Set up. */
pfd.fn = fn;
+ pfd.any_fn = any_fn;
pfd.data = data;
pfd.include_nondeduced_p = include_nondeduced_p;
@@ -18559,6 +18580,53 @@ unify_one_argument (tree tparms, tree targs, tree parm, tree arg,
return unify (tparms, targs, parm, arg, arg_strict, explain_p);
}
+/* for_each_template_parm callback that always returns 0. */
+
+static int
+zero_r (tree, void *)
+{
+ return 0;
+}
+
+/* for_each_template_parm any_fn callback to handle deduction of a template
+ type argument from the type of an array bound. */
+
+static int
+array_deduction_r (tree t, void *data)
+{
+ tree_pair_p d = (tree_pair_p)data;
+ tree &tparms = d->purpose;
+ tree &targs = d->value;
+
+ if (TREE_CODE (t) == ARRAY_TYPE)
+ if (tree dom = TYPE_DOMAIN (t))
+ if (tree max = TYPE_MAX_VALUE (dom))
+ {
+ if (TREE_CODE (max) == MINUS_EXPR)
+ max = TREE_OPERAND (max, 0);
+ if (TREE_CODE (max) == TEMPLATE_PARM_INDEX)
+ unify (tparms, targs, TREE_TYPE (max), size_type_node,
+ UNIFY_ALLOW_NONE, /*explain*/false);
+ }
+
+ /* Keep walking. */
+ return 0;
+}
+
+/* Try to deduce any not-yet-deduced template type arguments from the type of
+ an array bound. This is handled separately from unify because 14.8.2.5 says
+ "The type of a type parameter is only deduced from an array bound if it is
+ not otherwise deduced." */
+
+static void
+try_array_deduction (tree tparms, tree targs, tree parm)
+{
+ tree_pair_s data = { tparms, targs };
+ hash_set<tree> visited;
+ for_each_template_parm (parm, zero_r, &data, &visited,
+ /*nondeduced*/false, array_deduction_r);
+}
+
/* Most parms like fn_type_unification.
If SUBR is 1, we're being called recursively (to unify the
@@ -18688,6 +18756,7 @@ type_unification_real (tree tparms,
tsubst_flags_t complain = (explain_p
? tf_warning_or_error
: tf_none);
+ bool tried_array_deduction = (cxx_dialect < cxx1z);
for (i = 0; i < ntparms; i++)
{
@@ -18706,6 +18775,15 @@ type_unification_real (tree tparms,
continue;
tparm = TREE_VALUE (tparm);
+ if (TREE_CODE (tparm) == TYPE_DECL
+ && !tried_array_deduction)
+ {
+ try_array_deduction (tparms, targs, xparms);
+ tried_array_deduction = true;
+ if (TREE_VEC_ELT (targs, i))
+ continue;
+ }
+
/* If this is an undeduced nontype parameter that depends on
a type parameter, try another pass; its type may have been
deduced from a later argument than the one from which
@@ -19378,8 +19456,8 @@ template_parm_level_and_index (tree parm, int* level, int* index)
/* Unifies the remaining arguments in PACKED_ARGS with the pack
expansion at the end of PACKED_PARMS. Returns 0 if the type
deduction succeeds, 1 otherwise. STRICT is the same as in
- unify. CALL_ARGS_P is true iff PACKED_ARGS is actually a function
- call argument list. We'll need to adjust the arguments to make them
+ fn_type_unification. CALL_ARGS_P is true iff PACKED_ARGS is actually a
+ function call argument list. We'll need to adjust the arguments to make them
types. SUBR tells us if this is from a recursive call to
type_unification_real, or for comparing two template argument
lists. */
@@ -19680,6 +19758,9 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
tree targ;
tree tparm;
int strict_in = strict;
+ tsubst_flags_t complain = (explain_p
+ ? tf_warning_or_error
+ : tf_none);
/* I don't think this will do the right thing with respect to types.
But the only case I've seen it in so far has been array bounds, where
@@ -19897,9 +19978,7 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
if (coerce_template_parms (parm_parms,
full_argvec,
TYPE_TI_TEMPLATE (parm),
- (explain_p
- ? tf_warning_or_error
- : tf_none),
+ complain,
/*require_all_args=*/true,
/*use_default_args=*/false)
== error_mark_node)
@@ -20046,6 +20125,18 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
return x;
}
+ if (cxx_dialect >= cxx1z
+ /* We deduce from array bounds in try_array_deduction. */
+ && !(strict & UNIFY_ALLOW_INTEGER)
+ && uses_template_parms (TREE_TYPE (parm))
+ && !type_uses_auto (TREE_TYPE (parm)))
+ {
+ tree atype = TREE_TYPE (arg);
+ RECUR_AND_CHECK_FAILURE (tparms, targs,
+ TREE_TYPE (parm), atype,
+ UNIFY_ALLOW_NONE, explain_p);
+ }
+
/* [temp.deduct.type] If, in the declaration of a function template
with a non-type template-parameter, the non-type
template-parameter is used in an expression in the function
@@ -20055,6 +20146,13 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
deduced from an array bound may be of any integral type.
The non-type parameter might use already deduced type parameters. */
tparm = tsubst (TREE_TYPE (parm), targs, 0, NULL_TREE);
+ if (tree a = type_uses_auto (tparm))
+ {
+ tparm = do_auto_deduction (tparm, arg, a, complain, adc_unify);
+ if (tparm == error_mark_node)
+ return 1;
+ }
+
if (!TREE_TYPE (arg))
/* Template-parameter dependent expression. Just accept it for now.
It will later be processed in convert_template_argument. */
@@ -21015,6 +21113,8 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
else
deduced_args = innermost_deduced_args;
+ bool tried_array_deduction = (cxx_dialect < cxx1z);
+ again:
if (unify (tparms, deduced_args,
INNERMOST_TEMPLATE_ARGS (spec_args),
INNERMOST_TEMPLATE_ARGS (args),
@@ -21023,7 +21123,17 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
for (i = 0; i < ntparms; ++i)
if (! TREE_VEC_ELT (innermost_deduced_args, i))
- return NULL_TREE;
+ {
+ if (!tried_array_deduction)
+ {
+ try_array_deduction (tparms, innermost_deduced_args,
+ INNERMOST_TEMPLATE_ARGS (spec_args));
+ tried_array_deduction = true;
+ if (TREE_VEC_ELT (innermost_deduced_args, i))
+ goto again;
+ }
+ return NULL_TREE;
+ }
tree tinst = build_tree_list (spec_tmpl, deduced_args);
if (!push_tinst_level (tinst))
@@ -24607,14 +24717,16 @@ do_auto_deduction (tree type, tree init, tree auto_node)
tree
do_auto_deduction (tree type, tree init, tree auto_node,
- tsubst_flags_t complain, auto_deduction_context context)
+ tsubst_flags_t complain, auto_deduction_context context,
+ tree outer_targs)
{
tree targs;
if (init == error_mark_node)
return error_mark_node;
- if (type_dependent_expression_p (init))
+ if (type_dependent_expression_p (init)
+ && context != adc_unify)
/* Defining a subset of type-dependent expressions that we can deduce
from ahead of time isn't worth the trouble. */
return type;
@@ -24733,6 +24845,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
switch (context)
{
case adc_unspecified:
+ case adc_unify:
error("placeholder constraints not satisfied");
break;
case adc_variable_type:
@@ -24754,8 +24867,9 @@ do_auto_deduction (tree type, tree init, tree auto_node,
}
}
- if (processing_template_decl)
- targs = add_to_template_args (current_template_args (), targs);
+ if (processing_template_decl && context != adc_unify)
+ outer_targs = current_template_args ();
+ targs = add_to_template_args (outer_targs, targs);
return tsubst (type, targs, complain, NULL_TREE);
}
diff --git a/gcc/testsuite/g++.dg/cpp0x/auto9.C b/gcc/testsuite/g++.dg/cpp0x/auto9.C
index 9001f78..771ce0e 100644
--- a/gcc/testsuite/g++.dg/cpp0x/auto9.C
+++ b/gcc/testsuite/g++.dg/cpp0x/auto9.C
@@ -111,7 +111,7 @@ badthrow2 () throw (auto &) // { dg-error "invalid use of|expected" }
{
}
-template <auto V = 4> struct G {}; // { dg-error "auto" }
+template <auto V = 4> struct G {}; // { dg-error "auto" "" { target { ! c++1z } } }
template <typename T> struct H { H (); ~H (); };
H<auto> h; // { dg-error "invalid|initializer" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
index f4658a9..adbc32c 100644
--- a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
+++ b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
@@ -368,6 +368,12 @@
# error "__cpp_aligned_new != 201606"
#endif
+#ifndef __cpp_template_auto
+# error "__cpp_template_auto"
+#elif __cpp_template_auto != 201606
+# error "__cpp_template_auto != 201606"
+#endif
+
#ifndef __cpp_inline_variables
# error "__cpp_inline_variables"
#elif __cpp_inline_variables != 201606
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto1.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto1.C
new file mode 100644
index 0000000..9d05074
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto1.C
@@ -0,0 +1,13 @@
+// Testcase from P0127R2
+// { dg-options -std=c++1z }
+
+template <long n> struct A { };
+
+template <class T> struct C;
+template <class T, T n> struct C<A<n>>
+{
+ using Q = T;
+};
+
+typedef long R;
+typedef C<A<2>>::Q R; // OK; T was deduced to long from the template argument value in the type A<2>
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto2.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto2.C
new file mode 100644
index 0000000..23dac8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto2.C
@@ -0,0 +1,10 @@
+// Testcase from P0127R2
+// { dg-options -std=c++1z }
+
+template <typename T> struct S;
+template <typename T, T n> struct S<int[n]> {
+ using Q = T;
+};
+
+typedef S<int[42]>::Q V;
+typedef decltype(sizeof 0) V; // OK; T was deduced to std::size_t from the type int[42]
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C
new file mode 100644
index 0000000..00b56b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto3.C
@@ -0,0 +1,15 @@
+// Testcase from P0127R2
+// { dg-options -std=c++1z }
+
+template<auto n> struct B { decltype(n) f = n; };
+B<5> b1; // OK: template parameter type is int
+B<'a'> b2; // OK: template parameter type is char
+B<2.5> b3; // { dg-error "" } template parameter type cannot be double
+
+template <auto n> void f(B<n>) { }
+
+int main()
+{
+ f(B<42>());
+ f(B<'a'>());
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto4.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto4.C
new file mode 100644
index 0000000..80bbbed
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto4.C
@@ -0,0 +1,14 @@
+// { dg-options -std=c++1z }
+
+template <class T, T n> void f(T, int (&)[n]);
+template <class T, T n> void g(int (&)[n], T);
+template <class T, T n> void h(int (&)[n]);
+
+int main()
+{
+ const int i = 42;
+ int ar[i];
+ h(ar);
+ f(i, ar);
+ g(ar, i);
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto5.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto5.C
new file mode 100644
index 0000000..aa5ca7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto5.C
@@ -0,0 +1,15 @@
+// { dg-options -std=c++1z }
+
+template <class T> struct A
+{
+ template <auto v> struct Y;
+ template <auto* p> struct Y<p> { using type1 = decltype (p); };
+ template <auto** pp> struct Y<pp> { using type2 = decltype (pp); };
+};
+
+int i;
+int *p;
+
+A<void>::Y<&i>::type1 t1;
+A<void>::Y<&p>::type2 t2;
+
diff --git a/gcc/testsuite/g++.dg/cpp1z/nontype-auto6.C b/gcc/testsuite/g++.dg/cpp1z/nontype-auto6.C
new file mode 100644
index 0000000..cbf1b46
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto6.C
@@ -0,0 +1,8 @@
+// { dg-do compile { target c++11 } }
+
+template <int N> struct A;
+template <typename T, T N> int foo(A<N> *) = delete;
+void foo(void *);
+void bar(A<0> *p) {
+ foo(p); // { dg-error "" "" { target c++1z } }
+}
diff --git a/gcc/testsuite/g++.dg/template/partial5.C b/gcc/testsuite/g++.dg/template/partial5.C
index 979e4c6..2f400f7 100644
--- a/gcc/testsuite/g++.dg/template/partial5.C
+++ b/gcc/testsuite/g++.dg/template/partial5.C
@@ -14,7 +14,7 @@ template<typename T, typename T::foo V>
struct Y { };
template<typename T, typename U, U v>
-struct Y<T, v> { }; // { dg-error "not deducible|U" }
+struct Y<T, v> { }; // { dg-error "not deducible|U" "" { target { ! c++1z } } }
template<typename T, T V>