Here we ICE on template<typename T, T = T{}> // #1 struct A {};
template<typename T> void foo(A<T>) {} void bar() { foo(A<char>()); } when deducing T in the non-type template parameter, because unify didn't expect a CONSTRUCTOR: default: /* An unresolved overload is a nondeduced context. */ if (is_overloaded_fn (parm) || type_unknown_p (parm)) return unify_success (explain_p); gcc_assert (EXPR_P (parm) || TREE_CODE (parm) == TRAIT_EXPR); This works if T{} is replaced with T() in #1 -- then unify gets a CAST_EXPR, which is EXPR_P. Since here we're in a non-deduced context, I think we should simply accept the CONSTRUCTOR and return unify_success. I've also updated a comment that has gotten obsolete now. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2019-06-18 Marek Polacek <pola...@redhat.com> PR c++/60223 - ICE with T{} in non-deduced context. * pt.c (unify): Allow COMPOUND_LITERAL_P in a non-deduced context. Update a comment. * g++.dg/cpp0x/nondeduced1.C: New test. * g++.dg/cpp0x/nondeduced2.C: New test. * g++.dg/cpp0x/nondeduced3.C: New test. * g++.dg/cpp0x/nondeduced4.C: New test. diff --git gcc/cp/pt.c gcc/cp/pt.c index 2a626526c6f..69de55369dd 100644 --- gcc/cp/pt.c +++ gcc/cp/pt.c @@ -22786,7 +22786,9 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, /* An unresolved overload is a nondeduced context. */ if (is_overloaded_fn (parm) || type_unknown_p (parm)) return unify_success (explain_p); - gcc_assert (EXPR_P (parm) || TREE_CODE (parm) == TRAIT_EXPR); + gcc_assert (EXPR_P (parm) + || COMPOUND_LITERAL_P (parm) + || TREE_CODE (parm) == TRAIT_EXPR); expr: /* We must be looking at an expression. This can happen with something like: @@ -22794,15 +22796,19 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, template <int I> void foo(S<I>, S<I + 2>); - This is a "nondeduced context": + or + + template<typename T> + void foo(A<T, T{}>); + + This is a "non-deduced context": [deduct.type] - The nondeduced contexts are: + The non-deduced contexts are: - --A type that is a template-id in which one or more of - the template-arguments is an expression that references - a template-parameter. + --A non-type template argument or an array bound in which + a subexpression references a template parameter. In these cases, we assume deduction succeeded, but don't actually infer any unifications. */ diff --git gcc/testsuite/g++.dg/cpp0x/nondeduced1.C gcc/testsuite/g++.dg/cpp0x/nondeduced1.C new file mode 100644 index 00000000000..067079e50df --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/nondeduced1.C @@ -0,0 +1,16 @@ +// PR c++/60223 +// { dg-do compile { target c++11 } } + +template<typename T, T = T{}> +struct A { }; + +template<typename T> +void foo(A<T> a); + +void bar() +{ + foo(A<char, char{}>()); + foo(A<char>()); + foo<>(A<char>()); + foo<>(A<char, char{}>()); +} diff --git gcc/testsuite/g++.dg/cpp0x/nondeduced2.C gcc/testsuite/g++.dg/cpp0x/nondeduced2.C new file mode 100644 index 00000000000..3f96fe4e858 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/nondeduced2.C @@ -0,0 +1,14 @@ +// PR c++/60223 +// { dg-do compile { target c++11 } } + +template<typename T, T> +struct A { }; + +template<typename T> +void foo(A<T, T{}>); + +void bar() +{ + foo(A<char, char{}>()); + foo<>(A<char, char{}>()); +} diff --git gcc/testsuite/g++.dg/cpp0x/nondeduced3.C gcc/testsuite/g++.dg/cpp0x/nondeduced3.C new file mode 100644 index 00000000000..d943dceea4b --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/nondeduced3.C @@ -0,0 +1,16 @@ +// PR c++/60223 +// { dg-do compile { target c++11 } } + +template<typename T, T = T{1}> +struct A { }; + +template<typename T> +void foo(A<T> a); + +void bar() +{ + foo(A<char>()); + foo(A<char, char{1}>()); + foo<>(A<char>()); + foo<>(A<char, char{1}>()); +} diff --git gcc/testsuite/g++.dg/cpp0x/nondeduced4.C gcc/testsuite/g++.dg/cpp0x/nondeduced4.C new file mode 100644 index 00000000000..818034c857c --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/nondeduced4.C @@ -0,0 +1,13 @@ +// PR c++/60223 +// { dg-do compile { target c++11 } } + +template<typename T> +struct A { }; + +template<typename T> +void foo(A<T>, T = T{}); + +void bar() +{ + foo(A<int>()); +}