Author: Balázs Benics Date: 2026-10-20T15:04:59Z New Revision: 3bffd76f0e3de3b5a01bda90c170cf336439ce0b
URL: https://github.com/llvm/llvm-project/commit/3bffd76f0e3de3b5a01bda90c170cf336439ce0b DIFF: https://github.com/llvm/llvm-project/commit/3bffd76f0e3de3b5a01bda90c170cf336439ce0b.diff LOG: [LifetimeSafety] Fix assertion failure inferring lifetimebound 'this' for deduced return types (#233558) When the TU-wide inference decides a method's implicit object parameter should be `[[clang::lifetimebound]]`, `addLifetimeBoundToImplicitThis` wraps the method's function type in an AttributedType and rebuilds its TypeLoc. It built the AttributedType from the method's (resolved) type but copied the inner TypeLoc from the method's TypeSourceInfo. For a method with a deduced 'auto' return type in a template instantiation, the TypeSourceInfo still carries the written 'auto' placeholder while the method's type has already been resolved to the deduced type. The two disagree, so the wrapping AttributedTypeLoc's inner type did not match the pushed inner TypeLoc, tripping a TypeLocBuilder assertion (clang/lib/Sema/TypeLocBuilder.cpp): ``` Assertion failed: (TLast == LastTy && "mismatch between last type and new type's inner type") ``` That consistency check is guarded by `#ifndef NDEBUG`, so this only aborts in assertions-enabled builds. In a release (NDEBUG) build the assertion is compiled out and the function instead silently produces a malformed TypeSourceInfo for the method: the semantic type (`MD->getType()`) is still correct, but the stored TypeLoc data describes the original 'auto' function type while the reported type is the resolved one. Consumers that later walk the method's written type-source info (e.g. diagnostics or tooling) would then read source-location data under the wrong TypeLoc interpretation. The bug was introduced together with `addLifetimeBoundToImplicitThis` in commit 7d887bccc9bc5: > [LifetimeSafety] Add suggestion and inference for implicit this (#176703) the function copied the method's TypeSourceInfo unconditionally from the start, never accounting for a TypeSourceInfo whose type disagrees with the (possibly deduced) method type. Only copy the existing TypeLoc when its type matches the method's type; otherwise push a trivial TypeLoc for the method's type so the inner type is consistent with the AttributedType being pushed. Co-Authored-By: claude Added: Modified: clang/lib/Sema/SemaDecl.cpp clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp Removed: ################################################################################ diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 886b2a0b4c6d75..6aae0b57df0f99 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15128,8 +15128,18 @@ void Sema::addLifetimeBoundToImplicitThis(CXXMethodDecl *MD) { QualType AttributedType = Context.getAttributedType(Attr, MethodType, MethodType); TypeLocBuilder TLB; - if (TypeSourceInfo *TSI = MD->getTypeSourceInfo()) + TypeSourceInfo *TSI = MD->getTypeSourceInfo(); + if (TSI && TSI->getTypeLoc().getType() == MethodType) { TLB.pushFullCopy(TSI->getTypeLoc()); + } else { + // The written type-source info can disagree with the method's type. For + // example, a deduced 'auto' return type in a template instantiation keeps + // the 'auto' placeholder in its TypeSourceInfo while the method's type has + // already been resolved to the deduced type. Pushing the stale TypeLoc + // would make the wrapping AttributedTypeLoc's inner type inconsistent with + // MethodType. Build a trivial TypeLoc that matches MethodType instead. + TLB.pushTrivial(Context, MethodType, MD->getLocation()); + } AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(AttributedType); TyLoc.setAttr(Attr); MD->setType(AttributedType); diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp index 34f3ad4f45de1a..49c49b18e3b9cc 100644 --- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp +++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp @@ -258,6 +258,20 @@ struct ReturnsSelf { } }; +template <class C> struct SelfCapturingIter { + C *c; // expected-note {{escapes to this field}} + explicit SelfCapturingIter(C &x) : c(&x) {} // expected-warning {{parameter in intra-TU constructor should be marked [[clang::lifetimebound]]}} +}; + +template <class T> struct AutoReturnSelf { + auto make() { return SelfCapturingIter<AutoReturnSelf>(*this); } // expected-warning {{implicit this in intra-TU function should be marked [[clang::lifetimebound]]}} expected-note {{param returned here}} +}; + +auto test_auto_return_implicit_this() { + AutoReturnSelf<char> b; + return b.make(); // expected-warning {{stack memory associated with local variable 'b' is returned}} expected-note {{returned here}} +} + struct ReturnThisAnnotated { const ReturnThisAnnotated& get() [[clang::lifetimebound]] { return *this; } }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
