Skip to content
Open
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
33 changes: 33 additions & 0 deletions lib/CppInterOp/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5206,6 +5206,35 @@ std::string ObjToString(const char* TyRef, void* obj) {
return INTEROP_RETURN(getInterp().toString(TyRef, obj));
}

namespace {
/// Traps diagnostics emitted by Sema entry points invoked outside any
/// incremental parse and, when errors were introduced, restores a clean
/// diagnostic state on scope exit — the same cleanup IncrementalParser
/// performs on a real parse failure. Without it, DiagnosticsEngine's sticky
/// ErrorOccurred flag makes the next IncrementalParser::Parse report the
/// stale flag as its own failure (and then soft-reset it), spuriously
/// failing exactly one parse.
class StickyDiagnosticResetRAII {
clang::DiagnosticsEngine* m_Diags;
clang::DiagnosticErrorTrap m_Trap;

public:
StickyDiagnosticResetRAII(clang::DiagnosticsEngine& Diags)
: m_Diags(&Diags), m_Trap(Diags) {}
StickyDiagnosticResetRAII(const StickyDiagnosticResetRAII&) = delete;
StickyDiagnosticResetRAII&
operator=(const StickyDiagnosticResetRAII&) = delete;
StickyDiagnosticResetRAII(StickyDiagnosticResetRAII&&) = delete;
StickyDiagnosticResetRAII& operator=(StickyDiagnosticResetRAII&&) = delete;
~StickyDiagnosticResetRAII() {
if (m_Trap.hasErrorOccurred()) {
m_Diags->Reset(/*soft=*/true);
m_Diags->getClient()->clear();
}
}
};
} // namespace

static Decl* InstantiateTemplate(TemplateDecl* TemplateD,
TemplateArgumentListInfo& TLI, Sema& S,
bool instantiate_body) {
Expand Down Expand Up @@ -5303,6 +5332,10 @@ DeclRef InstantiateTemplate(compat::Interpreter& I, DeclRef tmpl,
auto* TmplD = unwrap<TemplateDecl>(tmpl);
// We will create a new decl, push a transaction.
compat::SynthesizingCodeRAII RAII(&getInterp());
// A rejected instantiation emits its error diagnostic outside any
// incremental parse; contain it so the sticky error state cannot fail the
// parse that follows.
StickyDiagnosticResetRAII DiagGuard(S.getDiagnostics());
return InstantiateTemplate(TmplD, TemplateArgs, S, instantiate_body);
}

Expand Down
24 changes: 24 additions & 0 deletions unittests/CppInterOp/ScopeReflectionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,30 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_InstantiateTemplate) {
EXPECT_TRUE(TA4_1.getAsIntegral() == 3);
}

TYPED_TEST(CPPINTEROP_TEST_MODE,
ScopeReflection_RejectedInstantiationDoesNotPoisonNextParse) {
std::vector<Decl*> Decls;
std::string code = R"(
struct PoisonTag {};
template <unsigned N> struct PoisonBuf { int t; };
)";

GetAllTopLevelDecls(code, Decls);

// A type argument for the non-type parameter N: Sema rejects the
// instantiation and emits an error diagnostic outside any incremental
// parse.
Cpp::TypeRef TagTy = Cpp::GetTypeFromScope(Decls[0]);
std::vector<Cpp::TemplateArgInfo> args = {TagTy.data};
EXPECT_FALSE(Cpp::InstantiateTemplate(Decls[1], args));

// The sticky DiagnosticsEngine error state must not fail the parses that
// follow (the poison was one-shot: only the first parse after the
// rejection reported failure).
EXPECT_EQ(Cpp::Declare("int rejected_instantiation_probe_1 = 1;"), 0);
EXPECT_EQ(Cpp::Declare("int rejected_instantiation_probe_2 = 2;"), 0);
}

TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_GetClassTemplateArgs) {
std::vector<Decl*> Decls;
std::string code = R"(
Expand Down
Loading