https://github.com/swjng created 
https://github.com/llvm/llvm-project/pull/197647

## Summary
- Bail out of `Parser::ParseLexedAttribute` when 
`DiagnosticsEngine::hasFatalErrorOccurred()` is true, draining the cached 
attribute tokens without invoking the late parser.
- Fixes a clang frontend assertion crash (`Expression evaluator can't be called 
on a dependent expression`) when a translation unit exceeds `-ferror-limit` 
before a class with late-parsed attributes (e.g. `diagnose_if`, `enable_if`) 
closes.

## Root cause
After fatal-error state is set, name lookup performed by the late attribute 
parser can silently fail. The parser then wraps successful sub-expressions in a 
`RecoveryExpr`, which makes the whole attribute condition `<dependent type>` / 
value-dependent. The condition is attached to the (non-template) declaration 
as-is, so on a later call to that declaration 
`Sema::diagnoseArgDependentDiagnoseIfAttrs` calls 
`Expr::EvaluateWithSubstitution` on a value-dependent condition and the 
assertion in `clang/lib/AST/ExprConstant.cpp` fires.

`Sema::CheckEnableIf` already guards the same call with `isValueDependent()`; 
the `diagnose_if` counterpart never did, but the cleaner fix is to keep the 
malformed condition out of the AST entirely.

## Test plan
- New regression test `clang/test/SemaCXX/diagnose-if-fatal-error.cpp` 
reproduces #197625 with `-ferror-limit 19` and verifies the compiler no longer 
aborts.
- Existing `clang/test/SemaCXX/diagnose_if.cpp` continues to pass.

## Tool-use disclosure
This patch was developed with the assistance of an AI coding agent (Claude 
Code, Anthropic). The agent performed the regression bisection on the 
reporter's input, identified the RecoveryExpr-from-fatal-error mechanism, 
drafted the fix and the regression test, and ran the relevant lit subsets 
locally. All code, the commit message, and this PR description were reviewed 
and approved by the human author before submission.

Fixes #197625

>From c0349bf351c2715ef1cc496353f4693aa706a4a8 Mon Sep 17 00:00:00 2001
From: Soowon Jeong <[email protected]>
Date: Thu, 14 May 2026 19:14:01 +0900
Subject: [PATCH] [clang][Parse] Skip late-parsed attributes after a fatal
 error

When the diagnostic engine has entered a fatal-error state (e.g. after
-ferror-limit is exceeded), name lookup performed by the late attribute
parser can silently fail, leaving a RecoveryExpr in the attribute's
condition. Attaching that value-dependent condition to a non-template
declaration later trips an assertion in Expr::EvaluateWithSubstitution
when the declaration is referenced (e.g. via diagnose_if).

Bail out of ParseLexedAttribute when a fatal error has occurred: drain
the cached tokens and return without attaching the half-formed
attribute.
---
 clang/lib/Parse/ParseCXXInlineMethods.cpp     | 14 ++++++
 .../test/SemaCXX/diagnose-if-fatal-error.cpp  | 46 +++++++++++++++++++
 2 files changed, 60 insertions(+)
 create mode 100644 clang/test/SemaCXX/diagnose-if-fatal-error.cpp

diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp 
b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index 6189c854e5fbf..e78e3e6ff0c46 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -757,6 +757,20 @@ void Parser::ParseLexedAttribute(LateParsedAttribute &LA,
 
   ParsedAttributes Attrs(AttrFactory);
 
+  // If a fatal error has occurred, semantic analysis is no longer reliable
+  // (name lookup may silently fail and wrap references in RecoveryExpr,
+  // producing value-dependent attribute conditions on non-template decls
+  // that later confuse the attribute consumers). Skip the parse and drain
+  // the cached tokens instead. The diagnostics emitted up to this point
+  // already justify failing the compilation.
+  if (Actions.getDiagnostics().hasFatalErrorOccurred()) {
+    while (Tok.isNot(tok::eof))
+      ConsumeAnyToken();
+    if (Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData())
+      ConsumeAnyToken();
+    return;
+  }
+
   if (LA.Decls.size() > 0) {
     Decl *D = LA.Decls[0];
     NamedDecl *ND  = dyn_cast<NamedDecl>(D);
diff --git a/clang/test/SemaCXX/diagnose-if-fatal-error.cpp 
b/clang/test/SemaCXX/diagnose-if-fatal-error.cpp
new file mode 100644
index 0000000000000..57dea815e9ac7
--- /dev/null
+++ b/clang/test/SemaCXX/diagnose-if-fatal-error.cpp
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++17 -ferror-limit 19 -verify %s
+
+// After -ferror-limit is exceeded the parser enters a fatal-error state.
+// Late-parsed attribute conditions must not be attached to the declaration
+// in that state: name lookup of the condition's sub-expressions may fail
+// silently and wrap the result in a RecoveryExpr, producing a
+// value-dependent condition on a non-template function that later confuses
+// the diagnose_if consumer (Expr::EvaluateWithSubstitution asserts on
+// value-dependent expressions).
+// We test that we do not crash in such cases (#197625).
+
+#define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
+
+using size_t = decltype(sizeof(0));
+namespace std {
+template <typename T>
+struct initializer_list {
+  const T *p;
+  size_t s;
+  constexpr size_t size() const { return s; }
+};
+} // namespace std
+
+template <typename T>
+struct E {
+  void f(int i) _diagnose_if(i, "bad i", "error"); // expected-note 19 {{from 
'diagnose_if' attribute on 'f'}}
+};
+void blast() {
+  E<int> e;
+  e.f(1); e.f(1); e.f(1); e.f(1); e.f(1); // expected-error 5 {{bad i}}
+  e.f(1); e.f(1); e.f(1); e.f(1); e.f(1); // expected-error 5 {{bad i}}
+  e.f(1); e.f(1); e.f(1); e.f(1); e.f(1); // expected-error 5 {{bad i}}
+  e.f(1); e.f(1); e.f(1); e.f(1); e.f(1); // expected-error 4 {{bad i}} 
expected-error@* {{too many errors emitted}}
+}
+
+struct Foo {
+  Foo(std::initializer_list<int> l)
+    _diagnose_if(l.size() == 1, "first", "warning")
+    _diagnose_if(l.size() == 2, "second", "error") {}
+};
+
+void run() {
+  Foo{std::initializer_list<int>{}};
+  Foo{std::initializer_list<int>{1}};
+  Foo{std::initializer_list<int>{1, 2}};
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to