Skip to content

Commit 907cefe

Browse files
committed
Always deduce the lengths of contained parameter packs when deducing a
pack expansion. Previously, if all parameter / argument pairs for a pack expansion deduction were non-deduced contexts, we would not deduce the arity of the pack, and could end up deducing a different arity (leading to failures during substitution) or defaulting to an arity of 0 (leading to bad diagnostics about passing the wrong number of arguments to a variadic function). Instead, we now always deduce the arity for all involved packs any time we deduce a pack expansion. This will result in less substitution happening in some cases, which could avoid non-SFINAEable errors, and should generally improve the quality of diagnostics when passing initializer lists to variadic functions.
1 parent e93b1ff commit 907cefe

File tree

8 files changed

+78
-34
lines changed

8 files changed

+78
-34
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3875,12 +3875,14 @@ def note_ovl_candidate_bad_deduction : Note<
38753875
"candidate template ignored: failed template argument deduction">;
38763876
def note_ovl_candidate_incomplete_deduction : Note<"candidate template ignored: "
38773877
"couldn't infer template argument %0">;
3878-
def note_ovl_candidate_incomplete_deduction_pack : Note<"candidate template ignored: "
3878+
def note_ovl_candidate_incomplete_deduction_pack : Note<
3879+
"candidate template ignored: "
38793880
"deduced too few arguments for expanded pack %0; no argument for %ordinal1 "
38803881
"expanded parameter in deduced argument pack %2">;
38813882
def note_ovl_candidate_inconsistent_deduction : Note<
3882-
"candidate template ignored: deduced conflicting %select{types|values|"
3883-
"templates}0 for parameter %1%diff{ ($ vs. $)|}2,3">;
3883+
"candidate template ignored: deduced %select{conflicting types|"
3884+
"conflicting values|conflicting templates|packs of different lengths}0 "
3885+
"for parameter %1%diff{ ($ vs. $)|}2,3">;
38843886
def note_ovl_candidate_inconsistent_deduction_types : Note<
38853887
"candidate template ignored: deduced values %diff{"
38863888
"of conflicting types for parameter %0 (%1 of type $ vs. %3 of type $)|"

clang/lib/Sema/SemaOverload.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10348,6 +10348,16 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
1034810348
which = 2;
1034910349
}
1035010350

10351+
// Tweak the diagnostic if the problem is that we deduced packs of
10352+
// different arities. We'll print the actual packs anyway in case that
10353+
// includes additional useful information.
10354+
if (DeductionFailure.getFirstArg()->getKind() == TemplateArgument::Pack &&
10355+
DeductionFailure.getSecondArg()->getKind() == TemplateArgument::Pack &&
10356+
DeductionFailure.getFirstArg()->pack_size() !=
10357+
DeductionFailure.getSecondArg()->pack_size()) {
10358+
which = 3;
10359+
}
10360+
1035110361
S.Diag(Templated->getLocation(),
1035210362
diag::note_ovl_candidate_inconsistent_deduction)
1035310363
<< which << ParamD->getDeclName() << *DeductionFailure.getFirstArg()
@@ -10385,6 +10395,8 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
1038510395
TemplateArgString = " ";
1038610396
TemplateArgString += S.getTemplateArgumentBindingsText(
1038710397
getDescribedTemplate(Templated)->getTemplateParameters(), *Args);
10398+
if (TemplateArgString.size() == 1)
10399+
TemplateArgString.clear();
1038810400
S.Diag(Templated->getLocation(),
1038910401
diag::note_ovl_candidate_unsatisfied_constraints)
1039010402
<< TemplateArgString;
@@ -10412,6 +10424,8 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
1041210424
TemplateArgString = " ";
1041310425
TemplateArgString += S.getTemplateArgumentBindingsText(
1041410426
getDescribedTemplate(Templated)->getTemplateParameters(), *Args);
10427+
if (TemplateArgString.size() == 1)
10428+
TemplateArgString.clear();
1041510429
}
1041610430

1041710431
// If this candidate was disabled by enable_if, say so.
@@ -10461,6 +10475,8 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
1046110475
TemplateArgString = " ";
1046210476
TemplateArgString += S.getTemplateArgumentBindingsText(
1046310477
getDescribedTemplate(Templated)->getTemplateParameters(), *Args);
10478+
if (TemplateArgString.size() == 1)
10479+
TemplateArgString.clear();
1046410480
}
1046510481

1046610482
S.Diag(Templated->getLocation(), diag::note_ovl_candidate_deduced_mismatch)

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -860,34 +860,31 @@ class PackDeductionScope {
860860
/// Finish template argument deduction for a set of argument packs,
861861
/// producing the argument packs and checking for consistency with prior
862862
/// deductions.
863-
Sema::TemplateDeductionResult
864-
finish(bool TreatNoDeductionsAsNonDeduced = true) {
863+
Sema::TemplateDeductionResult finish() {
865864
// Build argument packs for each of the parameter packs expanded by this
866865
// pack expansion.
867866
for (auto &Pack : Packs) {
868867
// Put back the old value for this pack.
869868
Deduced[Pack.Index] = Pack.Saved;
870869

871-
// If we are deducing the size of this pack even if we didn't deduce any
872-
// values for it, then make sure we build a pack of the right size.
873-
// FIXME: Should we always deduce the size, even if the pack appears in
874-
// a non-deduced context?
875-
if (!TreatNoDeductionsAsNonDeduced)
876-
Pack.New.resize(PackElements);
870+
// Always make sure the size of this pack is correct, even if we didn't
871+
// deduce any values for it.
872+
//
873+
// FIXME: This isn't required by the normative wording, but substitution
874+
// and post-substitution checking will always fail if the arity of any
875+
// pack is not equal to the number of elements we processed. (Either that
876+
// or something else has gone *very* wrong.) We're permitted to skip any
877+
// hard errors from those follow-on steps by the intent (but not the
878+
// wording) of C++ [temp.inst]p8:
879+
//
880+
// If the function selected by overload resolution can be determined
881+
// without instantiating a class template definition, it is unspecified
882+
// whether that instantiation actually takes place
883+
Pack.New.resize(PackElements);
877884

878885
// Build or find a new value for this pack.
879886
DeducedTemplateArgument NewPack;
880-
if (PackElements && Pack.New.empty()) {
881-
if (Pack.DeferredDeduction.isNull()) {
882-
// We were not able to deduce anything for this parameter pack
883-
// (because it only appeared in non-deduced contexts), so just
884-
// restore the saved argument pack.
885-
continue;
886-
}
887-
888-
NewPack = Pack.DeferredDeduction;
889-
Pack.DeferredDeduction = TemplateArgument();
890-
} else if (Pack.New.empty()) {
887+
if (Pack.New.empty()) {
891888
// If we deduced an empty argument pack, create it now.
892889
NewPack = DeducedTemplateArgument(TemplateArgument::getEmptyPack());
893890
} else {
@@ -2636,8 +2633,8 @@ static Sema::TemplateDeductionResult ConvertDeducedTemplateArguments(
26362633
// be deduced to an empty sequence of template arguments.
26372634
// FIXME: Where did the word "trailing" come from?
26382635
if (Deduced[I].isNull() && Param->isTemplateParameterPack()) {
2639-
if (auto Result = PackDeductionScope(S, TemplateParams, Deduced, Info, I)
2640-
.finish(/*TreatNoDeductionsAsNonDeduced*/false))
2636+
if (auto Result =
2637+
PackDeductionScope(S, TemplateParams, Deduced, Info, I).finish())
26412638
return Result;
26422639
}
26432640

clang/test/CXX/drs/dr13xx.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,9 @@ namespace dr1388 { // dr1388: 4
348348
// we know exactly how many arguments correspond to it.
349349
template<typename T, typename U> struct pair {};
350350
template<typename ...T> struct tuple { typedef char type; }; // expected-error 0-2{{C++11}}
351-
template<typename ...T, typename ...U> void f_pair_1(pair<T, U>..., int); // expected-error 0-2{{C++11}} expected-note {{different lengths (2 vs. 0)}}
351+
template<typename ...T, typename ...U> void f_pair_1(pair<T, U>..., int); // expected-error 0-2{{C++11}} expected-note {{[with T = <int, long>]: deduced incomplete pack <(no value), (no value)> for template parameter 'U'}}
352352
template<typename ...T, typename U> void f_pair_2(pair<T, char>..., U); // expected-error 0-2{{C++11}}
353-
template<typename ...T, typename ...U> void f_pair_3(pair<T, U>..., tuple<U...>); // expected-error 0-2{{C++11}} expected-note {{different lengths (2 vs. 1)}}
353+
template<typename ...T, typename ...U> void f_pair_3(pair<T, U>..., tuple<U...>); // expected-error 0-2{{C++11}} expected-note {{deduced packs of different lengths for parameter 'U' (<(no value), (no value)> vs. <char>)}}
354354
template<typename ...T> void f_pair_4(pair<T, char>..., T...); // expected-error 0-2{{C++11}} expected-note {{<int, long> vs. <int, long, const char *>}}
355355
void g(pair<int, char> a, pair<long, char> b, tuple<char, char> c) {
356356
f_pair_1<int, long>(a, b, 0); // expected-error {{no match}}

clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,34 @@ void (*ptr_has_non_trailing_pack)(char, int) = has_non_trailing_pack<char>;
3535
template<typename ...T, typename U> void has_non_trailing_pack_and_more(T ..., U); // expected-note {{failed}}
3636
void (*ptr_has_non_trailing_pack_and_more_1)(float, double, int) = &has_non_trailing_pack_and_more<float, double>;
3737
void (*ptr_has_non_trailing_pack_and_more_2)(float, double, int) = &has_non_trailing_pack_and_more<float>; // expected-error {{does not match}}
38+
39+
// - A function parameter for which the associated argument is an initializer
40+
// list but the parameter does not have a type for which deduction from an
41+
// initializer list is specified.
42+
43+
// We interpret these "non-deduced context"s as actually deducing the arity --
44+
// but not the contents -- of a function parameter pack appropriately for the
45+
// number of arguments.
46+
namespace VariadicVsInitList {
47+
template<typename T, typename ...> struct X { using type = typename T::error; };
48+
template<typename ...T, typename X<int, T...>::type = 0> void f(T ...) = delete;
49+
void f(long);
50+
void f(long, long);
51+
void f(long, long, long);
52+
53+
// FIXME: We shouldn't say "substitution failure: " here.
54+
template<typename ...T> void g(T ...) = delete; // expected-note {{substitution failure: deduced incomplete pack <(no value)> for template parameter 'T'}}
55+
56+
void h() {
57+
// These all call the non-template overloads of 'f', because of a deduction
58+
// failure due to incomplete deduction of the pack 'T'. If deduction
59+
// succeeds and deduces an empty pack instead, we would get a hard error
60+
// instantiating 'X'.
61+
f({0}); // expected-warning {{braces around scalar}}
62+
f({0}, {0}); // expected-warning 2{{braces around scalar}}
63+
f(1, {0}); // expected-warning {{braces around scalar}}
64+
f(1, {0}, 2); // expected-warning {{braces around scalar}}
65+
66+
g({0}); // expected-error {{no matching function}}
67+
}
68+
}

clang/test/SemaTemplate/alias-templates.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,12 @@ namespace PR11848 {
7777
return i + f1<Ts...>(is...);
7878
}
7979

80-
// FIXME: This note is technically correct, but could be better. We
81-
// should really say that we couldn't infer template argument 'Ts'.
8280
template<typename ...Ts>
83-
void f2(U<Ts> ...is) { } // expected-note {{requires 0 arguments, but 1 was provided}}
81+
void f2(U<Ts> ...is) { } // expected-note {{deduced incomplete pack <(no value)> for template parameter 'Ts'}}
8482

8583
template<typename...> struct type_tuple {};
8684
template<typename ...Ts>
87-
void f3(type_tuple<Ts...>, U<Ts> ...is) {} // expected-note {{requires 4 arguments, but 3 were provided}}
85+
void f3(type_tuple<Ts...>, U<Ts> ...is) {} // expected-note {{deduced packs of different lengths for parameter 'Ts' (<void, void, void> vs. <(no value), (no value)>)}}
8886

8987
void g() {
9088
f1(U<void>()); // expected-error {{no match}}

clang/test/SemaTemplate/deduction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ namespace deduction_after_explicit_pack {
365365
int test(ExtraArgs..., unsigned vla_size, const char *input);
366366
int n = test(0, "");
367367

368-
template <typename... T> void i(T..., int, T..., ...); // expected-note 5{{deduced conflicting}}
368+
template <typename... T> void i(T..., int, T..., ...); // expected-note 5{{deduced packs of different lengths}}
369369
void j() {
370370
i(0);
371371
i(0, 1); // expected-error {{no match}}
@@ -381,7 +381,7 @@ namespace deduction_after_explicit_pack {
381381
// parameter against the first argument, then passing the first argument
382382
// through the first parameter.
383383
template<typename... T> struct X { X(int); operator int(); };
384-
template<typename... T> void p(T..., X<T...>, ...); // expected-note {{deduced conflicting}}
384+
template<typename... T> void p(T..., X<T...>, ...); // expected-note {{deduced packs of different lengths for parameter 'T' (<> vs. <int>)}}
385385
void q() { p(X<int>(0), 0); } // expected-error {{no match}}
386386

387387
struct A {

clang/test/SemaTemplate/pack-deduction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ template<typename ...T> struct X {};
55
template<typename T, typename U> struct P {};
66

77
namespace Nested {
8-
template<typename ...T> int f1(X<T, T...>... a); // expected-note +{{conflicting types for parameter 'T'}}
9-
template<typename ...T> int f2(P<X<T...>, T> ...a); // expected-note +{{conflicting types for parameter 'T'}}
8+
template<typename ...T> int f1(X<T, T...>... a); // expected-note +{{packs of different lengths for parameter 'T'}}
9+
template<typename ...T> int f2(P<X<T...>, T> ...a); // expected-note +{{packs of different lengths for parameter 'T'}}
1010

1111
int a1 = f1(X<int, int, double>(), X<double, int, double>());
1212
int a2 = f1(X<int, int>());

0 commit comments

Comments
 (0)