Skip to content

Commit b6fee6d

Browse files
author
Emery Conrad
committed
Reset sticky diagnostic state after a rejected InstantiateTemplate
The rejection (e.g. a type argument for a non-type parameter) emits its error diagnostic outside any incremental parse, and DiagnosticsEngine's ErrorOccurred flag is sticky: the next IncrementalParser::Parse reported the stale flag as its own failure and then soft-reset it, poisoning exactly one parse. Contain the rejection with a DiagnosticErrorTrap-based RAII that restores a clean diagnostic state when the instantiation introduced errors - the same cleanup IncrementalParser performs on a real parse failure and InstantiateFunctionDefinition performs inline. Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
1 parent 3103be9 commit b6fee6d

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

lib/CppInterOp/CppInterOp.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5206,6 +5206,35 @@ std::string ObjToString(const char* TyRef, void* obj) {
52065206
return INTEROP_RETURN(getInterp().toString(TyRef, obj));
52075207
}
52085208

5209+
namespace {
5210+
/// Traps diagnostics emitted by Sema entry points invoked outside any
5211+
/// incremental parse and, when errors were introduced, restores a clean
5212+
/// diagnostic state on scope exit — the same cleanup IncrementalParser
5213+
/// performs on a real parse failure. Without it, DiagnosticsEngine's sticky
5214+
/// ErrorOccurred flag makes the next IncrementalParser::Parse report the
5215+
/// stale flag as its own failure (and then soft-reset it), spuriously
5216+
/// failing exactly one parse.
5217+
class StickyDiagnosticResetRAII {
5218+
clang::DiagnosticsEngine* m_Diags;
5219+
clang::DiagnosticErrorTrap m_Trap;
5220+
5221+
public:
5222+
StickyDiagnosticResetRAII(clang::DiagnosticsEngine& Diags)
5223+
: m_Diags(&Diags), m_Trap(Diags) {}
5224+
StickyDiagnosticResetRAII(const StickyDiagnosticResetRAII&) = delete;
5225+
StickyDiagnosticResetRAII& operator=(const StickyDiagnosticResetRAII&) =
5226+
delete;
5227+
StickyDiagnosticResetRAII(StickyDiagnosticResetRAII&&) = delete;
5228+
StickyDiagnosticResetRAII& operator=(StickyDiagnosticResetRAII&&) = delete;
5229+
~StickyDiagnosticResetRAII() {
5230+
if (m_Trap.hasErrorOccurred()) {
5231+
m_Diags->Reset(/*soft=*/true);
5232+
m_Diags->getClient()->clear();
5233+
}
5234+
}
5235+
};
5236+
} // namespace
5237+
52095238
static Decl* InstantiateTemplate(TemplateDecl* TemplateD,
52105239
TemplateArgumentListInfo& TLI, Sema& S,
52115240
bool instantiate_body) {
@@ -5303,6 +5332,10 @@ DeclRef InstantiateTemplate(compat::Interpreter& I, DeclRef tmpl,
53035332
auto* TmplD = unwrap<TemplateDecl>(tmpl);
53045333
// We will create a new decl, push a transaction.
53055334
compat::SynthesizingCodeRAII RAII(&getInterp());
5335+
// A rejected instantiation emits its error diagnostic outside any
5336+
// incremental parse; contain it so the sticky error state cannot fail the
5337+
// parse that follows.
5338+
StickyDiagnosticResetRAII DiagGuard(S.getDiagnostics());
53065339
return InstantiateTemplate(TmplD, TemplateArgs, S, instantiate_body);
53075340
}
53085341

unittests/CppInterOp/ScopeReflectionTest.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,6 +1245,30 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_InstantiateTemplate) {
12451245
EXPECT_TRUE(TA4_1.getAsIntegral() == 3);
12461246
}
12471247

1248+
TYPED_TEST(CPPINTEROP_TEST_MODE,
1249+
ScopeReflection_RejectedInstantiationDoesNotPoisonNextParse) {
1250+
std::vector<Decl*> Decls;
1251+
std::string code = R"(
1252+
struct PoisonTag {};
1253+
template <unsigned N> struct PoisonBuf { int t; };
1254+
)";
1255+
1256+
GetAllTopLevelDecls(code, Decls);
1257+
1258+
// A type argument for the non-type parameter N: Sema rejects the
1259+
// instantiation and emits an error diagnostic outside any incremental
1260+
// parse.
1261+
Cpp::TypeRef TagTy = Cpp::GetTypeFromScope(Decls[0]);
1262+
std::vector<Cpp::TemplateArgInfo> args = {TagTy.data};
1263+
EXPECT_FALSE(Cpp::InstantiateTemplate(Decls[1], args));
1264+
1265+
// The sticky DiagnosticsEngine error state must not fail the parses that
1266+
// follow (the poison was one-shot: only the first parse after the
1267+
// rejection reported failure).
1268+
EXPECT_EQ(Cpp::Declare("int rejected_instantiation_probe_1 = 1;"), 0);
1269+
EXPECT_EQ(Cpp::Declare("int rejected_instantiation_probe_2 = 2;"), 0);
1270+
}
1271+
12481272
TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_GetClassTemplateArgs) {
12491273
std::vector<Decl*> Decls;
12501274
std::string code = R"(

0 commit comments

Comments
 (0)