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

Reply via email to