[clang] Improve error reporting for SFINAE
paul Fultz
pfultz2 at yahoo.com
Thu Feb 26 12:13:15 PST 2015
This is to help improve error reporting with SFINAE as part of the bug report here:
http://llvm.org/bugs/show_bug.cgi?id=13309
So now when this program compiles:
template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();}
void h() {}
template<class T> auto g(T x)->decltype(h(x)){return h(x);}
template<class T> auto f(T x)->decltype(g(x)){return g(x);}
int main(){
f(3);
}
Clang will report back this error with the true diagnostics:
smurf.cpp:5:3: error: no matching function for call to 'f'
f(3);
^
smurf.cpp:1:42: note: candidate template ignored: substitution failure [with T = int]: member reference base type 'int' is not a structure or union
template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();}
^
Now this only does this when there is single overloads involved. If we change
the example to add an extra overload for `h`, like this:
template<class T> auto h(T x)->decltype(x.smurf()){return x.smurf();}
void h() {}
template<class T> auto g(T x)->decltype(h(x)){return h(x);}
template<class T> auto f(T x)->decltype(g(x)){return g(x);}
int main(){
f(3);
}
Then we get an error like this instead:
smurf.cpp:6:3: error: no matching function for call to 'f'
f(3);
^
smurf.cpp:3:41: note: candidate template ignored: substitution failure [with T = int]: no matching function for call to 'h'
template<class T> auto g(T x)->decltype(h(x)){return h(x);}
^
So clang basically stops when there is multiple overloads. If the user needs
to see more detail for this, we could look at adding an option for a full
backtrace in the future.
Either way, I think this is very useful to have, and improves the error
reporting in clang immensely. I would like to get this patch merged into
clang.
Index: include/clang/Sema/TemplateDeduction.h
===================================================================
--- include/clang/Sema/TemplateDeduction.h (revision 230275)
+++ include/clang/Sema/TemplateDeduction.h (working copy)
@@ -68,8 +68,10 @@
/// \brief Take ownership of the SFINAE diagnostic.
void takeSFINAEDiagnostic(PartialDiagnosticAt &PD) {
assert(HasSFINAEDiagnostic);
- PD.first = SuppressedDiagnostics.front().first;
- PD.second.swap(SuppressedDiagnostics.front().second);
+ unsigned NumberOfDiagnostics = SuppressedDiagnostics.size();
+ PartialDiagnosticAt &PDiag = SuppressedDiagnostics[((NumberOfDiagnostics == 2) ? 1 : 0)];
+ PD.first = PDiag.first;
+ PD.second.swap(PDiag.second);
SuppressedDiagnostics.clear();
HasSFINAEDiagnostic = false;
}
@@ -98,8 +100,6 @@
/// \brief Add a new diagnostic to the set of diagnostics
void addSuppressedDiagnostic(SourceLocation Loc,
PartialDiagnostic PD) {
- if (HasSFINAEDiagnostic)
- return;
SuppressedDiagnostics.emplace_back(Loc, std::move(PD));
}
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp (revision 230275)
+++ lib/Sema/SemaOverload.cpp (working copy)
@@ -9105,6 +9105,16 @@
return;
}
+ // If the diagnsotics is already for overload candidate substitution
+ // failure, then just emit the diagnostics as is.
+ if (PDiag && (PDiag->second.getDiagID() ==
+ diag::note_ovl_candidate_substitution_failure ||
+ PDiag->second.getDiagID() ==
+ diag::note_ovl_candidate_disabled_by_enable_if)) {
+ S.Diag(PDiag->first, PDiag->second);
+ return;
+ }
+
// Format the SFINAE diagnostic into the argument string.
// FIXME: Add a general mechanism to include a PartialDiagnostic *'s
// formatted message in another diagnostic.
@@ -9116,7 +9126,7 @@
PDiag->second.EmitToString(S.getDiagnostics(), SFINAEArgString);
}
- S.Diag(Templated->getLocation(),
+ S.Diag(PDiag->first,
diag::note_ovl_candidate_substitution_failure)
<< TemplateArgString << SFINAEArgString << R;
MaybeEmitInheritedConstructorNote(S, Templated);
More information about the cfe-commits
mailing list