[clang] 0d93f2a - Fix references to complete types in attribute references (#209537)

via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 15 06:57:36 PDT 2026


Author: Erich Keane
Date: 2026-07-15T06:57:31-07:00
New Revision: 0d93f2aec0b4847761bd8458b7847ce37b85cf03

URL: https://github.com/llvm/llvm-project/commit/0d93f2aec0b4847761bd8458b7847ce37b85cf03
DIFF: https://github.com/llvm/llvm-project/commit/0d93f2aec0b4847761bd8458b7847ce37b85cf03.diff

LOG: Fix references to complete types in attribute references (#209537)

This is a regression from #197215.

Attributes are not REALLY in the body of a function (though the name of
said function is... awkwardly inaccurate at best), but still need to pay
attention to the completeness of their references. As a result, we
weren't marking the expression as invalid, but were also trying to
evaluate it.

This patch fixes this in 2 ways. First, we re-add the
CXXThisTypeOverride check, but except constant substitution, since that
has some additional 'this' behavior from #197215. x

Secondly, we also make the constant evaluator give up on incomplete
types when handling an L value member. This stops us from trying to
evaluate the value if it is incomplete during template instantiation,
when the type is incomplete. We don't diagnose, since it is still
potentially a constant expression, but isn't currently one.

Fixes: #199527

Added: 
    

Modified: 
    clang/lib/AST/ExprConstant.cpp
    clang/lib/Sema/SemaExprCXX.cpp
    clang/test/SemaCXX/enable_if.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index ff44138d66816..574dd8b04e779 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -3264,8 +3264,17 @@ static bool HandleLValueMember(EvalInfo &Info, const Expr *E, LValue &LVal,
                                const FieldDecl *FD,
                                const ASTRecordLayout *RL = nullptr) {
   if (!RL) {
-    if (FD->getParent()->isInvalidDecl()) return false;
-    RL = &Info.Ctx.getASTRecordLayout(FD->getParent());
+    const RecordDecl *RD = FD->getParent();
+    if (RD->isInvalidDecl())
+      return false;
+    // There are some cases where the base is not yet complete but we haven't
+    // disagnosed (such as in a template instantation of an attribute that
+    // references the expression, ala enable_if).  These aren't necessarily
+    // constant expressions so we return 'false', but they might be, so we don't
+    // diagnose.
+    if (!RD->isCompleteDefinition())
+      return false;
+    RL = &Info.Ctx.getASTRecordLayout(RD);
   }
 
   unsigned I = FD->getFieldIndex();

diff  --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 5f22cd409dc01..538604aa2e64b 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1487,6 +1487,14 @@ void Sema::MarkThisReferenced(CXXThisExpr *This) {
 }
 
 bool Sema::isThisOutsideMemberFunctionBody(QualType BaseType) {
+  // If we're outside the body of a member function, then we'll have a specified
+  // type for 'this'. Constraint substitution is the exception: a concept is
+  // evaluated in its own declaration context (see GH#197215), so it loses the
+  // enclosing '*this' even though it may legitimately name a member of the
+  // class currently being instantiated.
+  if (CXXThisTypeOverride.isNull() && !inConstraintSubstitution())
+    return false;
+
   // Determine whether we're looking into a class that's currently being
   // defined.
   CXXRecordDecl *Class = BaseType->getAsCXXRecordDecl();

diff  --git a/clang/test/SemaCXX/enable_if.cpp b/clang/test/SemaCXX/enable_if.cpp
index a34b87064b49d..4af0922fee5ac 100644
--- a/clang/test/SemaCXX/enable_if.cpp
+++ b/clang/test/SemaCXX/enable_if.cpp
@@ -646,6 +646,45 @@ void Substitute(Arg) __attribute__((enable_if(PlaceholderBitmask, ""))) {
 
 }
 
+namespace GH199527 {
+struct S { // expected-note {{definition of 'GH199527::S' is not complete until the closing '}'}}
+  ~S() {}
+  bool b;
+  // expected-error at +1{{member access into incomplete type 'S'}}
+  void foo(S b) __attribute__((enable_if(b.b, "")));
+};
+
+template<typename T>
+struct S2 {
+  bool b;
+  void foo(S2 b) const __attribute__((enable_if(b.b, "templ_foo_disabled"))); // #FOO
+};
+
+void use() {
+  S2<int> s_whatever;
+  S2<int> s_true{true};
+  S2<int> s_false{false};
+
+
+  // Both fail because this isn't a constexpr.
+  // expected-error at +2{{no matching member function for call to 'foo'}}
+  // expected-note@#FOO{{candidate disabled: templ_foo_disabled}}
+  s_whatever.foo(s_true);
+  // expected-error at +2{{no matching member function for call to 'foo'}}
+  // expected-note@#FOO{{candidate disabled: templ_foo_disabled}}
+  s_whatever.foo(s_false);
+
+  constexpr S2<int> ce_s_whatever{};
+  constexpr S2<int> ce_s_true{true};
+  constexpr S2<int> ce_s_false{false};
+
+  ce_s_whatever.foo(ce_s_true);
+  // expected-error at +2{{no matching member function for call to 'foo'}}
+  // expected-note@#FOO{{candidate disabled: templ_foo_disabled}}
+  ce_s_whatever.foo(ce_s_false);
+}
+}
+
 namespace DefaultArgs {
   void f(int n = __builtin_LINE()) __attribute__((enable_if(n == 12345, "only callable on line 12345"))); // expected-note {{only callable on line 12345}}
   void g() { f(); } // expected-error {{no matching function}}


        


More information about the cfe-commits mailing list