Skip to content

Commit 72bfc29

Browse files
zyn0217Sirraide
authored andcommitted
[Clang] Clarify diagnostic notes for implicitly generated deduction guides (llvm#96084)
Given the following invalid code, ```cpp template <class T> struct S { T *a; }; S s = {1}; ``` we produce such diagnostics currently: ``` <source>:2:8: note: candidate template ignored: could not match 'S<T>' against 'int' 2 | struct S { | ^ <source>:2:8: note: candidate template ignored: could not match 'T *' against 'int' ``` Which I think is confusing because there's no `S<T>` nor `T *` at the location it points to. This is because we're deducing the initializer against implicitly generated deduction guides, and their source locations just point to the corresponding `RecordDecl`. Hence the misleading notes. This patch alleviates the issue by adding extra notes demonstrating which implicit deduction guide we're deducing against. In other words, in addition to the note of `could not match 'T *' against 'int'`, we would also say the implicit deduction guide we're trying to use: `template <class T> S(T *) -> S<T>`, which looks clearer IMO. --------- Co-authored-by: Sirraide <aeternalmail@gmail.com>
1 parent b34706d commit 72bfc29

File tree

23 files changed

+156
-44
lines changed

23 files changed

+156
-44
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,8 @@ Improvements to Clang's diagnostics
651651
that will be destroyed at the end of the full expression.
652652
Fixes #GH54492.
653653

654+
- Clang now shows implicit deduction guides when diagnosing overload resolution failure. #GH92393.
655+
654656
Improvements to Clang's time-trace
655657
----------------------------------
656658

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2419,6 +2419,7 @@ def err_selected_explicit_constructor : Error<
24192419
"chosen constructor is explicit in copy-initialization">;
24202420
def note_explicit_ctor_deduction_guide_here : Note<
24212421
"explicit %select{constructor|deduction guide}0 declared here">;
2422+
def note_implicit_deduction_guide : Note<"implicit deduction guide declared as '%0'">;
24222423

24232424
// C++11 auto
24242425
def warn_cxx98_compat_auto_type_specifier : Warning<

clang/lib/Sema/SemaOverload.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "llvm/ADT/DenseSet.h"
4141
#include "llvm/ADT/STLExtras.h"
4242
#include "llvm/ADT/STLForwardCompat.h"
43+
#include "llvm/ADT/ScopeExit.h"
4344
#include "llvm/ADT/SmallPtrSet.h"
4445
#include "llvm/ADT/SmallString.h"
4546
#include "llvm/ADT/SmallVector.h"
@@ -11884,6 +11885,46 @@ static void DiagnoseFailedExplicitSpec(Sema &S, OverloadCandidate *Cand) {
1188411885
<< (ES.getExpr() ? ES.getExpr()->getSourceRange() : SourceRange());
1188511886
}
1188611887

11888+
static void NoteImplicitDeductionGuide(Sema &S, FunctionDecl *Fn) {
11889+
auto *DG = dyn_cast<CXXDeductionGuideDecl>(Fn);
11890+
if (!DG)
11891+
return;
11892+
TemplateDecl *OriginTemplate =
11893+
DG->getDeclName().getCXXDeductionGuideTemplate();
11894+
// We want to always print synthesized deduction guides for type aliases.
11895+
// They would retain the explicit bit of the corresponding constructor.
11896+
if (!(DG->isImplicit() || (OriginTemplate && OriginTemplate->isTypeAlias())))
11897+
return;
11898+
std::string FunctionProto;
11899+
llvm::raw_string_ostream OS(FunctionProto);
11900+
FunctionTemplateDecl *Template = DG->getDescribedFunctionTemplate();
11901+
if (!Template) {
11902+
// This also could be an instantiation. Find out the primary template.
11903+
FunctionDecl *Pattern =
11904+
DG->getTemplateInstantiationPattern(/*ForDefinition=*/false);
11905+
if (!Pattern) {
11906+
// The implicit deduction guide is built on an explicit non-template
11907+
// deduction guide. Currently, this might be the case only for type
11908+
// aliases.
11909+
// FIXME: Add a test once https://github.com/llvm/llvm-project/pull/96686
11910+
// gets merged.
11911+
assert(OriginTemplate->isTypeAlias() &&
11912+
"Non-template implicit deduction guides are only possible for "
11913+
"type aliases");
11914+
DG->print(OS);
11915+
S.Diag(DG->getLocation(), diag::note_implicit_deduction_guide)
11916+
<< FunctionProto;
11917+
return;
11918+
}
11919+
Template = Pattern->getDescribedFunctionTemplate();
11920+
assert(Template && "Cannot find the associated function template of "
11921+
"CXXDeductionGuideDecl?");
11922+
}
11923+
Template->print(OS);
11924+
S.Diag(DG->getLocation(), diag::note_implicit_deduction_guide)
11925+
<< FunctionProto;
11926+
}
11927+
1188711928
/// Generates a 'note' diagnostic for an overload candidate. We've
1188811929
/// already generated a primary error at the call site.
1188911930
///
@@ -11941,6 +11982,17 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
1194111982
return;
1194211983
}
1194311984

11985+
// If this is a synthesized deduction guide we're deducing against, add a note
11986+
// for it. These deduction guides are not explicitly spelled in the source
11987+
// code, so simply printing a deduction failure note mentioning synthesized
11988+
// template parameters or pointing to the header of the surrounding RecordDecl
11989+
// would be confusing.
11990+
//
11991+
// We prefer adding such notes at the end of the deduction failure because
11992+
// duplicate code snippets appearing in the diagnostic would likely become
11993+
// noisy.
11994+
auto _ = llvm::make_scope_exit([&] { NoteImplicitDeductionGuide(S, Fn); });
11995+
1194411996
switch (Cand->FailureKind) {
1194511997
case ovl_fail_too_many_arguments:
1194611998
case ovl_fail_too_few_arguments:

clang/test/CXX/drs/cwg26xx.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,11 @@ static_assert(__is_same(decltype(i), I<char, 4>));
193193
J j = { "ghi" };
194194
// since-cxx20-error@-1 {{no viable constructor or deduction guide}}
195195
// since-cxx20-note@#cwg2681-J {{candidate template ignored: could not match 'J<N>' against 'const char *'}}
196+
// since-cxx20-note@#cwg2681-J {{implicit deduction guide declared as 'template <size_t N> J(J<N>) -> J<N>'}}
196197
// since-cxx20-note@#cwg2681-J {{candidate template ignored: could not match 'const unsigned char' against 'const char'}}
198+
// since-cxx20-note@#cwg2681-J {{implicit deduction guide declared as 'template <size_t N> J(const unsigned char (&)[N]) -> J<N>'}}
197199
// since-cxx20-note@#cwg2681-J {{candidate function template not viable: requires 0 arguments, but 1 was provided}}
200+
// since-cxx20-note@#cwg2681-J {{implicit deduction guide declared as 'template <size_t N> J() -> J<N>'}}
198201
#endif
199202
}
200203

clang/test/CXX/expr/expr.post/expr.type.conv/p1.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %clang_cc1 -std=c++1z -verify %s
22

3-
template<typename T> struct A { // expected-note 2{{candidate}}
3+
template<typename T> struct A { // expected-note 2{{candidate}} expected-note 2{{implicit deduction guide}}
44
T t, u;
55
};
66
template<typename T> A(T, T) -> A<T>; // expected-note {{deduced conflicting types for parameter 'T'}}

clang/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p2.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ namespace std {
3535
}
3636

3737
namespace p0702r1 {
38-
template<typename T> struct X { // expected-note {{candidate}}
39-
X(std::initializer_list<T>); // expected-note {{candidate template ignored: could not match 'std::initializer_list<T>' against 'Z'}}
38+
template<typename T> struct X { // expected-note {{candidate}} expected-note {{implicit deduction guide}}
39+
X(std::initializer_list<T>); // expected-note {{candidate template ignored: could not match 'std::initializer_list<T>' against 'Z'}} \
40+
// expected-note {{implicit deduction guide declared as 'template <typename T> X(std::initializer_list<T>) -> X<T>'}}
4041
};
4142

4243
X xi = {0};

clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3-0x.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
#if __cplusplus > 201402L
77
namespace ClassTemplateParamNotForwardingRef {
88
// This is not a forwarding reference.
9-
template<typename T> struct A { // expected-note {{candidate}}
10-
A(T&&); // expected-note {{expects an rvalue}}
9+
template<typename T> struct A { // expected-note {{candidate}} expected-note {{implicit deduction guide}}
10+
A(T&&); // expected-note {{expects an rvalue}} expected-note {{implicit deduction guide}}
1111
};
1212
int n;
1313
A a = n; // expected-error {{no viable constructor or deduction guide}}
@@ -75,10 +75,12 @@ namespace std_example {
7575
int n3 = g(i); // expected-error{{no matching function for call to 'g'}}
7676

7777
#if __cplusplus > 201402L
78-
template<class T> struct A { // expected-note {{candidate}}
78+
template<class T> struct A { // expected-note {{candidate}} expected-note {{implicit deduction guide}}
7979
template<class U>
80-
A(T &&, U &&, int *); // expected-note {{[with T = int, U = int] not viable: expects an rvalue}}
81-
A(T &&, int *); // expected-note {{requires 2}}
80+
A(T &&, U &&, int *); // expected-note {{[with T = int, U = int] not viable: expects an rvalue}} \
81+
// expected-note {{implicit deduction guide declared as 'template <class T, class U> A(T &&, type-parameter-0-1 &&, int *) -> A<T>'}}
82+
A(T &&, int *); // expected-note {{requires 2}} \
83+
// expected-note {{implicit deduction guide declared as 'template <class T> A(T &&, int *) -> A<T>'}}
8284
};
8385
template<class T> A(T &&, int *) -> A<T>; // expected-note {{requires 2}}
8486

clang/test/Modules/template_name_lookup.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@ import foo;
77
void use() {
88
X x; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'X'}}
99
// expected-note@Inputs/template_name_lookup/foo.cppm:3 {{candidate template ignored: couldn't infer template argument 'T'}}
10+
// expected-note@Inputs/template_name_lookup/foo.cppm:3 {{implicit deduction guide declared as 'template <typename T> X(X<T>) -> X<T>'}}
1011
// expected-note@Inputs/template_name_lookup/foo.cppm:3 {{candidate function template not viable: requires 1 argument, but 0 were provided}}
12+
// expected-note@Inputs/template_name_lookup/foo.cppm:3 {{implicit deduction guide declared as 'template <typename T> X() -> X<T>'}}
1113
}

clang/test/PCH/cxx-explicit-specifier.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ struct A {
7979
B<true> b_true;
8080
B<false> b_false;
8181
#else
82-
//expected-note@-8 {{candidate template ignored}}
83-
//expected-note@-8 {{explicit constructor declared here}}
82+
//expected-note@-8 {{candidate template ignored}} expected-note@-8 {{implicit deduction guide declared as 'template <bool b> A(A<b>) -> A<b>'}}
83+
//expected-note@-8 {{explicit constructor declared here}} expected-note@-8 {{implicit deduction guide declared as 'template <bool b> explicit(b) A(B<b>) -> A<b>'}}
8484
//expected-note@-15+ {{candidate constructor}}
8585
//expected-note@-8+ {{explicit conversion function is not a candidate (explicit specifier}}
8686
//expected-note@-11 {{explicit constructor is not a candidate (explicit specifier}}

clang/test/Sema/tls_alignment.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ struct struct_with_aligned_field {
2222
template <typename>
2323
struct templated_struct {};
2424
// expected-note@-1{{candidate template ignored: couldn't infer template argument ''}}
25-
// expected-note@-2{{candidate function template not viable: requires 1 argument, but 0 were provided}}
25+
// expected-note@-2{{implicit deduction guide declared as 'template <typename> templated_struct() -> templated_struct<type-parameter-0-0>'}}
26+
// expected-note@-3{{candidate function template not viable: requires 1 argument, but 0 were provided}}
27+
// expected-note@-4{{implicit deduction guide declared as 'template <typename> templated_struct(templated_struct<type-parameter-0-0>) -> templated_struct<type-parameter-0-0>'}}
2628

2729
// A typedef of the aligned struct.
2830
typedef aligned_struct another_aligned_struct;

0 commit comments

Comments
 (0)