Skip to content

Commit 3b639d7

Browse files
zyn0217Sirraide
andauthored
[Clang] Clarify diagnostic notes for implicitly generated deduction guides (#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 <[email protected]>
1 parent 7d0656d commit 3b639d7

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;

clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,13 @@ namespace look_into_current_instantiation {
139139
// templates, and members of the current instantiation
140140
A<float> &r = a;
141141

142-
template<typename T> struct B { // expected-note {{could not match 'B<T>' against 'int'}}
142+
template<typename T> struct B { // expected-note {{could not match 'B<T>' against 'int'}} \
143+
// expected-note {{implicit deduction guide declared as 'template <typename T> B(B<T>) -> B<T>'}}
143144
struct X {
144145
typedef T type;
145146
};
146-
B(typename X::type); // expected-note {{couldn't infer template argument 'T'}}
147+
B(typename X::type); // expected-note {{couldn't infer template argument 'T'}} \
148+
// expected-note {{implicit deduction guide declared as 'template <typename T> B(typename X::type) -> B<T>'}}
147149
};
148150
B b = 0; // expected-error {{no viable}}
149151

@@ -564,8 +566,10 @@ namespace PR47175 {
564566

565567
// Ensure we don't crash when CTAD fails.
566568
template <typename T1, typename T2>
567-
struct Foo { // expected-note{{candidate function template not viable}}
568-
Foo(T1, T2); // expected-note{{candidate function template not viable}}
569+
struct Foo { // expected-note {{candidate function template not viable}} \
570+
// expected-note {{implicit deduction guide declared as 'template <typename T1, typename T2> Foo(Foo<T1, T2>) -> Foo<T1, T2>'}}
571+
Foo(T1, T2); // expected-note {{candidate function template not viable}} \
572+
// expected-note {{implicit deduction guide declared as 'template <typename T1, typename T2> Foo(T1, T2) -> Foo<T1, T2>'}}
569573
};
570574

571575
template <typename... Args>

clang/test/SemaCXX/cxx20-ctad-type-alias.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ struct Foo {
110110

111111
template <typename X, int Y>
112112
using Bar = Foo<X, sizeof(X)>; // expected-note {{candidate template ignored: couldn't infer template argument 'X'}} \
113+
// 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)>'}} \
114+
// 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)>'}} \
113115
// expected-note {{candidate template ignored: constraints not satisfied [with X = int]}} \
114116
// expected-note {{cannot deduce template arguments for 'Bar' from 'Foo<int, 4UL>'}}
115117

@@ -137,9 +139,12 @@ struct A {};
137139
template<class T> struct Foo { T c; };
138140
template<class X, class Y=A>
139141
using AFoo = Foo<Y>; // expected-note {{candidate template ignored: could not match 'Foo<type-parameter-0-0>' against 'int'}} \
142+
// 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>'}} \
140143
// expected-note {{candidate template ignored: constraints not satisfied [with Y = int]}} \
141144
// expected-note {{cannot deduce template arguments for 'AFoo' from 'Foo<int>'}} \
142-
// expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}}
145+
// 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>'}} \
146+
// expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \
147+
// 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>'}}
143148

144149
AFoo s = {1}; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'AFoo'}}
145150
} // namespace test11
@@ -192,6 +197,8 @@ struct Foo {
192197
template <int K>
193198
using Bar = Foo<double, K>; // expected-note {{constraints not satisfied for class template 'Foo'}}
194199
// expected-note@-1 {{candidate template ignored: could not match}}
200+
// 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>'}}
201+
// 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>'}}
195202
double abc[3];
196203
Bar s2 = {abc}; // expected-error {{no viable constructor or deduction guide for deduction }}
197204
} // namespace test14
@@ -204,7 +211,9 @@ template<typename> concept False = false;
204211
template<False W>
205212
using BFoo = AFoo<W>; // expected-note {{candidate template ignored: constraints not satisfied [with V = int]}} \
206213
// expected-note {{cannot deduce template arguments for 'BFoo' from 'Foo<int *>'}} \
207-
// expected-note {{candidate template ignored: could not match 'Foo<type-parameter-0-0 *>' against 'int *'}}
214+
// 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 *>}} \
215+
// expected-note {{candidate template ignored: could not match 'Foo<type-parameter-0-0 *>' against 'int *'}} \
216+
// 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 *>}}
208217
int i = 0;
209218
AFoo a1(&i); // OK, deduce Foo<int *>
210219

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

256265
template <typename U>
257266
using Bar = Foo<U>; // expected-note {{could not match 'Foo<type-parameter-0-0>' against 'int'}} \
267+
// 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>'}} \
258268
// expected-note {{candidate template ignored: constraints not satisfied}} \
259-
// expected-note {{candidate function template not viable}}
269+
// 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>'}} \
270+
// expected-note {{candidate function template not viable}} \
271+
// 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>'}}
260272

261273
Bar s = {1}; // expected-error {{no viable constructor or deduction guide for deduction of template arguments}}
262274
} // namespace test18
@@ -284,7 +296,8 @@ class Foo {};
284296
// Verify that template template type parameter TTP is referenced/used in the
285297
// template arguments of the RHS.
286298
template <template<typename> typename TTP>
287-
using Bar = Foo<K<TTP>>; // expected-note {{candidate template ignored: could not match 'Foo<K<template-parameter-0-0>>' against 'int'}}
299+
using Bar = Foo<K<TTP>>; // expected-note {{candidate template ignored: could not match 'Foo<K<template-parameter-0-0>>' against 'int'}} \
300+
// 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>>'}}
288301

289302
template <class T>
290303
class Container {};

clang/test/SemaCXX/cxx2a-explicit-bool.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,9 +394,11 @@ using type = T;
394394
template<typename T1, typename T2, bool b>
395395
struct A {
396396
// expected-note@-1+ {{candidate function}}
397+
// expected-note@-2+ {{implicit deduction guide}}
397398
explicit(false)
398399
A(typename nondeduced<T1>::type, typename nondeduced<T2>::type, typename nondeduced<B<b>>::type) {}
399400
// expected-note@-1+ {{candidate template ignored}}
401+
// expected-note@-2+ {{implicit deduction guide}}
400402
};
401403

402404
template<typename T1, typename T2, bool b>
@@ -678,10 +680,12 @@ namespace deduction_guide2 {
678680
template<typename T1 = int, typename T2 = int>
679681
struct A {
680682
// expected-note@-1+ {{candidate template ignored}}
683+
// expected-note@-2+ {{implicit deduction guide}}
681684
explicit(!is_same<T1, T2>::value)
682685
A(T1 = 0, T2 = 0) {}
683686
// expected-note@-1 {{explicit constructor declared here}}
684687
// expected-note@-2 2{{explicit constructor is not a candidate}}
688+
// expected-note@-3 2{{implicit deduction guide declared}}
685689
};
686690

687691
A a0 = 0;

clang/test/SemaCXX/gh65522.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
class X {};
44

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

0 commit comments

Comments
 (0)