Update LLVM.
Implement support for C++ Substitution Failure Is Not An Error
(SFINAE), which says that errors that occur during template argument
deduction do *not* produce diagnostics and do not necessarily make a
program ill-formed. Instead, template argument deduction silently
fails. This is currently implemented for template argument deduction
during matching of class template partial specializations, although
the mechanism will also apply to template argument deduction for
function templates. The scheme is simple:
- If we are in a template argument deduction context, any diagnostic
that is considered a SFINAE error (or warning) will be
suppressed. The error will be propagated up the call stack via the
normal means.
- By default, all warnings and errors are SFINAE errors. Add the
NoSFINAE class to a diagnostic in the .td file to make it a hard
error (e.g., for access-control violations).
Note that, to make this fully work, every place in Sema that emits an
error *and then immediately recovers* will need to check
Sema::isSFINAEContext() to determine whether it must immediately
return an error rather than recovering.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@73332 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp
index 3b3d61b..323f7a7 100644
--- a/lib/Basic/Diagnostic.cpp
+++ b/lib/Basic/Diagnostic.cpp
@@ -46,6 +46,7 @@
unsigned short DiagID;
unsigned Mapping : 3;
unsigned Class : 3;
+ bool SFINAE : 1;
const char *Description;
const char *OptionGroup;
@@ -58,8 +59,8 @@
};
static const StaticDiagInfoRec StaticDiagInfo[] = {
-#define DIAG(ENUM,CLASS,DEFAULT_MAPPING,DESC,GROUP) \
- { diag::ENUM, DEFAULT_MAPPING, CLASS, DESC, GROUP },
+#define DIAG(ENUM,CLASS,DEFAULT_MAPPING,DESC,GROUP,SFINAE) \
+ { diag::ENUM, DEFAULT_MAPPING, CLASS, SFINAE, DESC, GROUP },
#include "clang/Basic/DiagnosticCommonKinds.inc"
#include "clang/Basic/DiagnosticDriverKinds.inc"
#include "clang/Basic/DiagnosticFrontendKinds.inc"
@@ -68,7 +69,7 @@
#include "clang/Basic/DiagnosticASTKinds.inc"
#include "clang/Basic/DiagnosticSemaKinds.inc"
#include "clang/Basic/DiagnosticAnalysisKinds.inc"
-{ 0, 0, 0, 0, 0 }
+ { 0, 0, 0, 0, 0, 0}
};
#undef DIAG
@@ -89,7 +90,7 @@
#endif
// Search the diagnostic table with a binary search.
- StaticDiagInfoRec Find = { DiagID, 0, 0, 0, 0 };
+ StaticDiagInfoRec Find = { DiagID, 0, 0, 0, 0, 0 };
const StaticDiagInfoRec *Found =
std::lower_bound(StaticDiagInfo, StaticDiagInfo + NumDiagEntries, Find);
@@ -115,6 +116,12 @@
return 0;
}
+bool Diagnostic::isBuiltinSFINAEDiag(unsigned DiagID) {
+ if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
+ return Info->SFINAE && Info->Class != CLASS_NOTE;
+ return false;
+}
+
/// getDiagClass - Return the class field of the diagnostic.
///
static unsigned getBuiltinDiagClass(unsigned DiagID) {
@@ -399,7 +406,7 @@
/// ProcessDiag - This is the method used to report a diagnostic that is
/// finally fully formed.
-void Diagnostic::ProcessDiag() {
+bool Diagnostic::ProcessDiag() {
DiagnosticInfo Info(this);
// Figure out the diagnostic level of this message.
@@ -449,13 +456,13 @@
// If a fatal error has already been emitted, silence all subsequent
// diagnostics.
if (FatalErrorOccurred)
- return;
+ return false;
// If the client doesn't care about this message, don't issue it. If this is
// a note and the last real diagnostic was ignored, ignore it too.
if (DiagLevel == Diagnostic::Ignored ||
(DiagLevel == Diagnostic::Note && LastDiagLevel == Diagnostic::Ignored))
- return;
+ return false;
// If this diagnostic is in a system header and is not a clang error, suppress
// it.
@@ -464,7 +471,7 @@
Info.getLocation().getSpellingLoc().isInSystemHeader() &&
(DiagLevel != Diagnostic::Note || LastDiagLevel == Diagnostic::Ignored)) {
LastDiagLevel = Diagnostic::Ignored;
- return;
+ return false;
}
if (DiagLevel >= Diagnostic::Error) {
@@ -477,6 +484,8 @@
if (Client->IncludeInDiagnosticCounts()) ++NumDiagnostics;
CurDiagID = ~0U;
+
+ return true;
}
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index e3cea5b..4dc5222 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -316,7 +316,8 @@
}
Sema::SemaDiagnosticBuilder::~SemaDiagnosticBuilder() {
- this->Emit();
+ if (!this->Emit())
+ return;
// If this is not a note, and we're in a template instantiation
// that is different from the last template instantiation where
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 169ce82..b310568 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -297,11 +297,21 @@
SemaDiagnosticBuilder(DiagnosticBuilder &DB, Sema &SemaRef, unsigned DiagID)
: DiagnosticBuilder(DB), SemaRef(SemaRef), DiagID(DiagID) { }
+ explicit SemaDiagnosticBuilder(Sema &SemaRef)
+ : DiagnosticBuilder(DiagnosticBuilder::Suppress), SemaRef(SemaRef) { }
+
~SemaDiagnosticBuilder();
};
/// \brief Emit a diagnostic.
SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
+ if (isSFINAEContext() && Diagnostic::isBuiltinSFINAEDiag(DiagID)) {
+ // If we encountered an error during template argument
+ // deduction, and that error is one of the SFINAE errors,
+ // supress the diagnostic.
+ return SemaDiagnosticBuilder(*this);
+ }
+
DiagnosticBuilder DB = Diags.Report(FullSourceLoc(Loc, SourceMgr), DiagID);
return SemaDiagnosticBuilder(DB, *this, DiagID);
}
@@ -2322,6 +2332,14 @@
void PrintInstantiationStack();
+ /// \brief Determines whether we are currently in a context where
+ /// template argument substitution failures are not considered
+ /// errors.
+ ///
+ /// When this routine returns true, the emission of most diagnostics
+ /// will be suppressed and there will be no local error recovery.
+ bool isSFINAEContext() const;
+
/// \brief A stack-allocated class that identifies which local
/// variable declaration instantiations are present in this scope.
///
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 4d03e79..18b2d75 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -199,6 +199,34 @@
}
}
+bool Sema::isSFINAEContext() const {
+ using llvm::SmallVector;
+ for (SmallVector<ActiveTemplateInstantiation, 16>::const_reverse_iterator
+ Active = ActiveTemplateInstantiations.rbegin(),
+ ActiveEnd = ActiveTemplateInstantiations.rend();
+ Active != ActiveEnd;
+ ++Active) {
+
+ switch(Active->Kind) {
+ case ActiveTemplateInstantiation::PartialSpecDeductionInstantiation:
+ // We're in a template argument deduction context, so SFINAE
+ // applies.
+ return true;
+
+ case ActiveTemplateInstantiation::DefaultTemplateArgumentInstantiation:
+ // A default template argument instantiation may or may not be a
+ // SFINAE context; look further up the stack.
+ break;
+
+ case ActiveTemplateInstantiation::TemplateInstantiation:
+ // This is a template instantiation, so there is no SFINAE.
+ return false;
+ }
+ }
+
+ return false;
+}
+
//===----------------------------------------------------------------------===/
// Template Instantiation for Types
//===----------------------------------------------------------------------===/