Author: Erich Keane Date: 2026-07-16T16:28:26Z New Revision: c7280675cd04a97c958bcf46db8042e33ab35fc9
URL: https://github.com/llvm/llvm-project/commit/c7280675cd04a97c958bcf46db8042e33ab35fc9 DIFF: https://github.com/llvm/llvm-project/commit/c7280675cd04a97c958bcf46db8042e33ab35fc9.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 (cherry picked from commit 0d93f2aec0b4847761bd8458b7847ce37b85cf03) 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 7d9dba8cd33fc..120af89a93b09 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -3212,8 +3212,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@+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@+2{{no matching member function for call to 'foo'}} + // expected-note@#FOO{{candidate disabled: templ_foo_disabled}} + s_whatever.foo(s_true); + // expected-error@+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@+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}} _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
