From patchwork Wed Nov 9 20:00:23 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 692930 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3tDcVy5JpKz9t1b for ; Thu, 10 Nov 2016 07:00:57 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="HB08UeO0"; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:content-type; q= dns; s=default; b=pQopTRHa39fu2JazXLGDf4hpdIgfXL561G+hUZjaWYYKvj jKbyl6go42k6Pw8feJSwa0l87YNnCKvsLcU8w/HGS8YIhpe9dlvU2bCh8hox2lc5 WoBfFONmux7fLEao9IvxOLo+aiFWArVGVRqoPRGnSTtKds8SXasFuq1DhGK8Q= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:from:date:message-id:subject:to:content-type; s= default; bh=Qp55wJ9+7XTJwUORdTfv8N+esrI=; b=HB08UeO0B189TWZMzIgM 39XFiE+WxyED41tWjZOCP6iQUqKL9O2LRtz9EE7mZoi7Gkbm4Ute4g0PXq/P9NVp EFLD/7CrbaEOFiXKdRh07tnCdTsBrsB/In3YRsHqfTGQftyx3N0Y7n0NewYCsdXf h1IbKpe4I6bJFV4DuqG3K1Q= Received: (qmail 11152 invoked by alias); 9 Nov 2016 20:00:49 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 11137 invoked by uid 89); 9 Nov 2016 20:00:48 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.8 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.2 spammy=merrill, Merrill, fn, Template X-HELO: mail-yw0-f181.google.com Received: from mail-yw0-f181.google.com (HELO mail-yw0-f181.google.com) (209.85.161.181) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 09 Nov 2016 20:00:46 +0000 Received: by mail-yw0-f181.google.com with SMTP id l124so217291316ywb.3 for ; Wed, 09 Nov 2016 12:00:45 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=qA6G3sr0Pa0vwwuqfNXdkRE4annjz+uxNrGPJjSihZs=; b=ZVXItlqOWLHfTMralKeSIsf+bAOncqO/zj7awf5ZD4gvzixvQIR+1fUs+T4uDstzIo ofOcFyfQy70dLUHfFIFygrSdJ8F/ZylrNhH0kW7lLQXNXPXF3V321tAAJHIf24mOV9rU cv82FC93XtDtCUiFlTXsHSKdU16QEA+8fm+sS3ryMtfbMKeBbgWhJrnwy7qwo0eFnwyP 4UIaCIBtJFbHVOTQXXjeA0a6KeI+nz+NoH3zfWfXC+Q+7DVrWnSdFsTvuvUjSjv7cxSL QV+EKKAIfNcKCpwl0T36mCJJHFwOOfqWSy0ooSsug+FN++7mOCn1LnvRm20N27mMtAIj lb5A== X-Gm-Message-State: ABUngvcKb7ctTjTipqFbZTwL1Mx5ArWv3bOfbEip37M9C7OyXmBBGqzzi21jhQvxGKq37rn+RSY2o+a6dMUd5ysM X-Received: by 10.157.56.226 with SMTP id k31mr829481ote.36.1478721644023; Wed, 09 Nov 2016 12:00:44 -0800 (PST) MIME-Version: 1.0 Received: by 10.183.13.228 with HTTP; Wed, 9 Nov 2016 12:00:23 -0800 (PST) From: Jason Merrill Date: Wed, 9 Nov 2016 12:00:23 -0800 Message-ID: Subject: C++ PATCH for C++17 auto non-type template parameters To: gcc-patches List X-IsSubscribed: yes This was pretty straightforward; auto template parameters look more or less the same as any other parameter with dependent type, so substitution works without any changes, we just need to do auto deduction in a couple of places. The most involved bit was handling deduction of a type parameter from the type of an array bound, which only happens if deduction otherwise doesn't find a binding. At first I tried to work this into unify et al, but eventually decided to do it in a (much simpler) separate function. Tested x86_64-pc-linux-gnu, applying to trunk. commit 04ade0b5013698fd1e458ef1425f7afd023feaf0 Author: Jason Merrill Date: Mon Nov 7 16:45:06 2016 -0800 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. 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/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 ("% 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 *, bool); + hash_set *, 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 *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 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 struct G {}; // { dg-error "auto" } +template struct G {}; // { dg-error "auto" "" { target { ! c++1z } } } template struct H { H (); ~H (); }; H 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 struct A { }; + +template struct C; +template struct C> +{ + using Q = T; +}; + +typedef long R; +typedef C>::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 struct S; +template struct S { + using Q = T; +}; + +typedef S::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 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 void f(B) { } + +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 void f(T, int (&)[n]); +template void g(int (&)[n], T); +template 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..aa5ca7f0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/nontype-auto5.C @@ -0,0 +1,15 @@ +// { dg-options -std=c++1z } + +template struct A +{ + template struct Y; + template struct Y

{ using type1 = decltype (p); }; + template struct Y { using type2 = decltype (pp); }; +}; + +int i; +int *p; + +A::Y<&i>::type1 t1; +A::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 struct A; +template int foo(A *) = 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 struct Y { }; template -struct Y { }; // { dg-error "not deducible|U" } +struct Y { }; // { dg-error "not deducible|U" "" { target { ! c++1z } } } template