Skip to content

[Clang] Clarify diagnostic notes for implicitly generated deduction guides #96084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,8 @@ Improvements to Clang's diagnostics
that will be destroyed at the end of the full expression.
Fixes #GH54492.

- Clang now shows implicit deduction guides when diagnosing overload resolution failure. #GH92393.

Improvements to Clang's time-trace
----------------------------------

Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2419,6 +2419,7 @@ def err_selected_explicit_constructor : Error<
"chosen constructor is explicit in copy-initialization">;
def note_explicit_ctor_deduction_guide_here : Note<
"explicit %select{constructor|deduction guide}0 declared here">;
def note_implicit_deduction_guide : Note<"implicit deduction guide declared as '%0'">;

// C++11 auto
def warn_cxx98_compat_auto_type_specifier : Warning<
Expand Down
52 changes: 52 additions & 0 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
Expand Down Expand Up @@ -11884,6 +11885,46 @@ static void DiagnoseFailedExplicitSpec(Sema &S, OverloadCandidate *Cand) {
<< (ES.getExpr() ? ES.getExpr()->getSourceRange() : SourceRange());
}

static void NoteImplicitDeductionGuide(Sema &S, FunctionDecl *Fn) {
auto *DG = dyn_cast<CXXDeductionGuideDecl>(Fn);
if (!DG)
return;
TemplateDecl *OriginTemplate =
DG->getDeclName().getCXXDeductionGuideTemplate();
// We want to always print synthesized deduction guides for type aliases.
// They would retain the explicit bit of the corresponding constructor.
if (!(DG->isImplicit() || (OriginTemplate && OriginTemplate->isTypeAlias())))
return;
std::string FunctionProto;
llvm::raw_string_ostream OS(FunctionProto);
FunctionTemplateDecl *Template = DG->getDescribedFunctionTemplate();
if (!Template) {
// This also could be an instantiation. Find out the primary template.
FunctionDecl *Pattern =
DG->getTemplateInstantiationPattern(/*ForDefinition=*/false);
if (!Pattern) {
// The implicit deduction guide is built on an explicit non-template
// deduction guide. Currently, this might be the case only for type
// aliases.
// FIXME: Add a test once https://github.com/llvm/llvm-project/pull/96686
// gets merged.
assert(OriginTemplate->isTypeAlias() &&
"Non-template implicit deduction guides are only possible for "
"type aliases");
DG->print(OS);
S.Diag(DG->getLocation(), diag::note_implicit_deduction_guide)
<< FunctionProto;
return;
}
Template = Pattern->getDescribedFunctionTemplate();
assert(Template && "Cannot find the associated function template of "
"CXXDeductionGuideDecl?");
}
Template->print(OS);
S.Diag(DG->getLocation(), diag::note_implicit_deduction_guide)
<< FunctionProto;
}

/// Generates a 'note' diagnostic for an overload candidate. We've
/// already generated a primary error at the call site.
///
Expand Down Expand Up @@ -11941,6 +11982,17 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
return;
}

// If this is a synthesized deduction guide we're deducing against, add a note
// for it. These deduction guides are not explicitly spelled in the source
// code, so simply printing a deduction failure note mentioning synthesized
// template parameters or pointing to the header of the surrounding RecordDecl
// would be confusing.
//
// We prefer adding such notes at the end of the deduction failure because
// duplicate code snippets appearing in the diagnostic would likely become
// noisy.
auto _ = llvm::make_scope_exit([&] { NoteImplicitDeductionGuide(S, Fn); });

switch (Cand->FailureKind) {
case ovl_fail_too_many_arguments:
case ovl_fail_too_few_arguments:
Expand Down
3 changes: 3 additions & 0 deletions clang/test/CXX/drs/cwg26xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,11 @@ static_assert(__is_same(decltype(i), I<char, 4>));
J j = { "ghi" };
// since-cxx20-error@-1 {{no viable constructor or deduction guide}}
// since-cxx20-note@#cwg2681-J {{candidate template ignored: could not match 'J<N>' against 'const char *'}}
// since-cxx20-note@#cwg2681-J {{implicit deduction guide declared as 'template <size_t N> J(J<N>) -> J<N>'}}
// since-cxx20-note@#cwg2681-J {{candidate template ignored: could not match 'const unsigned char' against 'const char'}}
// since-cxx20-note@#cwg2681-J {{implicit deduction guide declared as 'template <size_t N> J(const unsigned char (&)[N]) -> J<N>'}}
// since-cxx20-note@#cwg2681-J {{candidate function template not viable: requires 0 arguments, but 1 was provided}}
// since-cxx20-note@#cwg2681-J {{implicit deduction guide declared as 'template <size_t N> J() -> J<N>'}}
#endif
}

Expand Down
2 changes: 1 addition & 1 deletion clang/test/CXX/expr/expr.post/expr.type.conv/p1.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -std=c++1z -verify %s

template<typename T> struct A { // expected-note 2{{candidate}}
template<typename T> struct A { // expected-note 2{{candidate}} expected-note 2{{implicit deduction guide}}
T t, u;
};
template<typename T> A(T, T) -> A<T>; // expected-note {{deduced conflicting types for parameter 'T'}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ namespace std {
}

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

X xi = {0};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
#if __cplusplus > 201402L
namespace ClassTemplateParamNotForwardingRef {
// This is not a forwarding reference.
template<typename T> struct A { // expected-note {{candidate}}
A(T&&); // expected-note {{expects an rvalue}}
template<typename T> struct A { // expected-note {{candidate}} expected-note {{implicit deduction guide}}
A(T&&); // expected-note {{expects an rvalue}} expected-note {{implicit deduction guide}}
};
int n;
A a = n; // expected-error {{no viable constructor or deduction guide}}
Expand Down Expand Up @@ -75,10 +75,12 @@ namespace std_example {
int n3 = g(i); // expected-error{{no matching function for call to 'g'}}

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

Expand Down
2 changes: 2 additions & 0 deletions clang/test/Modules/template_name_lookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ import foo;
void use() {
X x; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'X'}}
// expected-note@Inputs/template_name_lookup/foo.cppm:3 {{candidate template ignored: couldn't infer template argument 'T'}}
// expected-note@Inputs/template_name_lookup/foo.cppm:3 {{implicit deduction guide declared as 'template <typename T> X(X<T>) -> X<T>'}}
// expected-note@Inputs/template_name_lookup/foo.cppm:3 {{candidate function template not viable: requires 1 argument, but 0 were provided}}
// expected-note@Inputs/template_name_lookup/foo.cppm:3 {{implicit deduction guide declared as 'template <typename T> X() -> X<T>'}}
}
4 changes: 2 additions & 2 deletions clang/test/PCH/cxx-explicit-specifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ struct A {
B<true> b_true;
B<false> b_false;
#else
//expected-note@-8 {{candidate template ignored}}
//expected-note@-8 {{explicit constructor declared here}}
//expected-note@-8 {{candidate template ignored}} expected-note@-8 {{implicit deduction guide declared as 'template <bool b> A(A<b>) -> A<b>'}}
//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>'}}
//expected-note@-15+ {{candidate constructor}}
//expected-note@-8+ {{explicit conversion function is not a candidate (explicit specifier}}
//expected-note@-11 {{explicit constructor is not a candidate (explicit specifier}}
Expand Down
4 changes: 3 additions & 1 deletion clang/test/Sema/tls_alignment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ struct struct_with_aligned_field {
template <typename>
struct templated_struct {};
// expected-note@-1{{candidate template ignored: couldn't infer template argument ''}}
// expected-note@-2{{candidate function template not viable: requires 1 argument, but 0 were provided}}
// expected-note@-2{{implicit deduction guide declared as 'template <typename> templated_struct() -> templated_struct<type-parameter-0-0>'}}
// expected-note@-3{{candidate function template not viable: requires 1 argument, but 0 were provided}}
// expected-note@-4{{implicit deduction guide declared as 'template <typename> templated_struct(templated_struct<type-parameter-0-0>) -> templated_struct<type-parameter-0-0>'}}

// A typedef of the aligned struct.
typedef aligned_struct another_aligned_struct;
Expand Down
12 changes: 8 additions & 4 deletions clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,13 @@ namespace look_into_current_instantiation {
// templates, and members of the current instantiation
A<float> &r = a;

template<typename T> struct B { // expected-note {{could not match 'B<T>' against 'int'}}
template<typename T> struct B { // expected-note {{could not match 'B<T>' against 'int'}} \
// expected-note {{implicit deduction guide declared as 'template <typename T> B(B<T>) -> B<T>'}}
struct X {
typedef T type;
};
B(typename X::type); // expected-note {{couldn't infer template argument 'T'}}
B(typename X::type); // expected-note {{couldn't infer template argument 'T'}} \
// expected-note {{implicit deduction guide declared as 'template <typename T> B(typename X::type) -> B<T>'}}
};
B b = 0; // expected-error {{no viable}}

Expand Down Expand Up @@ -564,8 +566,10 @@ namespace PR47175 {

// Ensure we don't crash when CTAD fails.
template <typename T1, typename T2>
struct Foo { // expected-note{{candidate function template not viable}}
Foo(T1, T2); // expected-note{{candidate function template not viable}}
struct Foo { // expected-note {{candidate function template not viable}} \
// expected-note {{implicit deduction guide declared as 'template <typename T1, typename T2> Foo(Foo<T1, T2>) -> Foo<T1, T2>'}}
Foo(T1, T2); // expected-note {{candidate function template not viable}} \
// expected-note {{implicit deduction guide declared as 'template <typename T1, typename T2> Foo(T1, T2) -> Foo<T1, T2>'}}
};

template <typename... Args>
Expand Down
21 changes: 17 additions & 4 deletions clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ struct Foo {

template <typename X, int Y>
using Bar = Foo<X, sizeof(X)>; // expected-note {{candidate template ignored: couldn't infer template argument 'X'}} \
// expected-note {{implicit deduction guide declared as 'template <typename X> requires __is_deducible(test9::Bar, Foo<type-parameter-0-0, sizeof(type-parameter-0-0)>) Bar(Foo<type-parameter-0-0, sizeof(type-parameter-0-0)>) -> Foo<type-parameter-0-0, sizeof(type-parameter-0-0)>'}} \
// expected-note {{implicit deduction guide declared as 'template <typename X> requires __is_deducible(test9::Bar, Foo<type-parameter-0-0, sizeof(type-parameter-0-0)>) Bar(const type-parameter-0-0 (&)[sizeof(type-parameter-0-0)]) -> Foo<type-parameter-0-0, sizeof(type-parameter-0-0)>'}} \
// expected-note {{candidate template ignored: constraints not satisfied [with X = int]}} \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hokein I think the constraints not satisfied is still a bit confusing after #92389? There is no constraint explicitly written in the code, although the note actually refers to the implicit constraint __is_deducible.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, it is not great for constraints not written in the source code.

If we print constraints (see my another comment), it will improve the situation.

// expected-note {{cannot deduce template arguments for 'Bar' from 'Foo<int, 4UL>'}}

Expand Down Expand Up @@ -137,9 +139,12 @@ struct A {};
template<class T> struct Foo { T c; };
template<class X, class Y=A>
using AFoo = Foo<Y>; // expected-note {{candidate template ignored: could not match 'Foo<type-parameter-0-0>' against 'int'}} \
// expected-note {{implicit deduction guide declared as 'template <class Y = A> requires __is_deducible(test11::AFoo, Foo<type-parameter-0-0>) AFoo(Foo<type-parameter-0-0>) -> Foo<type-parameter-0-0>'}} \
// expected-note {{candidate template ignored: constraints not satisfied [with Y = int]}} \
// expected-note {{cannot deduce template arguments for 'AFoo' from 'Foo<int>'}} \
// expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}}
// expected-note {{implicit deduction guide declared as 'template <class Y = A> requires __is_deducible(test11::AFoo, Foo<type-parameter-0-0>) AFoo(type-parameter-0-0) -> Foo<type-parameter-0-0>'}} \
// expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \
// expected-note {{implicit deduction guide declared as 'template <class Y = A> requires __is_deducible(test11::AFoo, Foo<type-parameter-0-0>) AFoo() -> Foo<type-parameter-0-0>'}}

AFoo s = {1}; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'AFoo'}}
} // namespace test11
Expand Down Expand Up @@ -192,6 +197,8 @@ struct Foo {
template <int K>
using Bar = Foo<double, K>; // expected-note {{constraints not satisfied for class template 'Foo'}}
// expected-note@-1 {{candidate template ignored: could not match}}
// expected-note@-2 {{implicit deduction guide declared as 'template <int K> requires __is_deducible(test14::Bar, Foo<double, K>) Bar(Foo<double, K>) -> Foo<double, K>'}}
// expected-note@-3 {{implicit deduction guide declared as 'template <int K> requires __is_deducible(test14::Bar, Foo<double, K>) Bar(const double (&)[K]) -> Foo<double, K>'}}
double abc[3];
Bar s2 = {abc}; // expected-error {{no viable constructor or deduction guide for deduction }}
} // namespace test14
Expand All @@ -204,7 +211,9 @@ template<typename> concept False = false;
template<False W>
using BFoo = AFoo<W>; // expected-note {{candidate template ignored: constraints not satisfied [with V = int]}} \
// expected-note {{cannot deduce template arguments for 'BFoo' from 'Foo<int *>'}} \
// expected-note {{candidate template ignored: could not match 'Foo<type-parameter-0-0 *>' against 'int *'}}
// expected-note {{implicit deduction guide declared as 'template <class V> requires __is_deducible(AFoo, Foo<type-parameter-0-0 *>) && __is_deducible(test15::BFoo, Foo<type-parameter-0-0 *>) BFoo(type-parameter-0-0 *) -> Foo<type-parameter-0-0 *>}} \
// expected-note {{candidate template ignored: could not match 'Foo<type-parameter-0-0 *>' against 'int *'}} \
// expected-note {{template <class V> requires __is_deducible(AFoo, Foo<type-parameter-0-0 *>) && __is_deducible(test15::BFoo, Foo<type-parameter-0-0 *>) BFoo(Foo<type-parameter-0-0 *>) -> Foo<type-parameter-0-0 *>}}
int i = 0;
AFoo a1(&i); // OK, deduce Foo<int *>

Expand Down Expand Up @@ -255,8 +264,11 @@ Foo(T) -> Foo<int>;

template <typename U>
using Bar = Foo<U>; // expected-note {{could not match 'Foo<type-parameter-0-0>' against 'int'}} \
// expected-note {{implicit deduction guide declared as 'template <typename U> requires __is_deducible(test18::Bar, Foo<type-parameter-0-0>) Bar(Foo<type-parameter-0-0>) -> Foo<type-parameter-0-0>'}} \
// expected-note {{candidate template ignored: constraints not satisfied}} \
// expected-note {{candidate function template not viable}}
// expected-note {{implicit deduction guide declared as 'template <typename T> requires False<T> && __is_deducible(test18::Bar, Foo<int>) Bar(type-parameter-0-0) -> Foo<int>'}} \
// expected-note {{candidate function template not viable}} \
// expected-note {{implicit deduction guide declared as 'template <typename U> requires __is_deducible(test18::Bar, Foo<type-parameter-0-0>) Bar() -> Foo<type-parameter-0-0>'}}

Bar s = {1}; // expected-error {{no viable constructor or deduction guide for deduction of template arguments}}
} // namespace test18
Expand Down Expand Up @@ -284,7 +296,8 @@ class Foo {};
// Verify that template template type parameter TTP is referenced/used in the
// template arguments of the RHS.
template <template<typename> typename TTP>
using Bar = Foo<K<TTP>>; // expected-note {{candidate template ignored: could not match 'Foo<K<template-parameter-0-0>>' against 'int'}}
using Bar = Foo<K<TTP>>; // expected-note {{candidate template ignored: could not match 'Foo<K<template-parameter-0-0>>' against 'int'}} \
// expected-note {{implicit deduction guide declared as 'template <template <typename> typename TTP> requires __is_deducible(test20::Bar, Foo<K<template-parameter-0-0>>) Bar(Foo<K<template-parameter-0-0>>) -> Foo<K<template-parameter-0-0>>'}}

template <class T>
class Container {};
Expand Down
4 changes: 4 additions & 0 deletions clang/test/SemaCXX/cxx2a-explicit-bool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,11 @@ using type = T;
template<typename T1, typename T2, bool b>
struct A {
// expected-note@-1+ {{candidate function}}
// expected-note@-2+ {{implicit deduction guide}}
explicit(false)
A(typename nondeduced<T1>::type, typename nondeduced<T2>::type, typename nondeduced<B<b>>::type) {}
// expected-note@-1+ {{candidate template ignored}}
// expected-note@-2+ {{implicit deduction guide}}
};

template<typename T1, typename T2, bool b>
Expand Down Expand Up @@ -678,10 +680,12 @@ namespace deduction_guide2 {
template<typename T1 = int, typename T2 = int>
struct A {
// expected-note@-1+ {{candidate template ignored}}
// expected-note@-2+ {{implicit deduction guide}}
explicit(!is_same<T1, T2>::value)
A(T1 = 0, T2 = 0) {}
// expected-note@-1 {{explicit constructor declared here}}
// expected-note@-2 2{{explicit constructor is not a candidate}}
// expected-note@-3 2{{implicit deduction guide declared}}
};

A a0 = 0;
Expand Down
6 changes: 4 additions & 2 deletions clang/test/SemaCXX/gh65522.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
class X {};

template<typename T>
class B3 { // expected-note {{candidate template ignored: could not match 'B3<T>' against 'int'}}
class B3 { // expected-note {{candidate template ignored: could not match 'B3<T>' against 'int'}} \
// expected-note {{implicit deduction guide declared as 'template <typename T> B3(B3<T>) -> B3<T>'}}
template<X x> B3(T); // expected-warning 2{{non-type template parameter of type 'X' is incompatible with C++ standards before C++20}} \
// expected-note {{candidate template ignored: couldn't infer template argument 'x'}}
// expected-note {{candidate template ignored: couldn't infer template argument 'x'}} \
// expected-note {{implicit deduction guide declared as 'template <typename T, X x> B3(T) -> B3<T>'}}
};
B3 b3 = 0; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'B3'}} \
// expected-note {{while building implicit deduction guide first needed here}}
Loading