[clang] [clang] preserve return type sourceloc when instantiating templated blocks (PR #226431)
Henrik G. Olsson via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 25 03:50:54 PDT 2026
https://github.com/hnrklssn created https://github.com/llvm/llvm-project/pull/226431
When transforming a block expression we would transform the return type, but ignore the return type location. When using the `typename` keyword this would result in an elaborated type with invalid source location,
which would trigger the `(Keyword != ElaboratedTypeKeyword::None) == KeywordLoc.isValid()` assertion in `CheckTypenameType`.
rdar://184773464
>From 35b514c318bb85e6f9d41e5bafaf27820ddfdd99 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Thu, 24 Sep 2026 16:14:32 +0200
Subject: [PATCH 1/2] clean up trailing whitespace
---
clang/test/SemaObjCXX/blocks.mm | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/SemaObjCXX/blocks.mm b/clang/test/SemaObjCXX/blocks.mm
index f1b5b301087eb5..0d63f9c9b10176 100644
--- a/clang/test/SemaObjCXX/blocks.mm
+++ b/clang/test/SemaObjCXX/blocks.mm
@@ -33,7 +33,7 @@ - (void)bar;
@end
namespace N {
- class X { };
+ class X { };
void foo(X);
}
@@ -45,7 +45,7 @@ - (void)bar {
@end
typedef signed char BOOL;
-void foo6(void *block) {
+void foo6(void *block) {
void (^vb)(id obj, int idx, BOOL *stop) = (void (^)(id, int, BOOL *))block;
BOOL (^bb)(id obj, int idx, BOOL *stop) = (BOOL (^)(id, int, BOOL *))block;
}
>From bae3926324a08dff3a577406141e5ce99029cd77 Mon Sep 17 00:00:00 2001
From: "Henrik G. Olsson" <h_olsson at apple.com>
Date: Fri, 25 Sep 2026 12:39:44 +0200
Subject: [PATCH 2/2] [clang] preserve return type sourceloc when instantiating
templated blocks
When transforming a block expression we would transform the return type,
but ignore the return type location. When using the `typename` keyword,
this would result in an elaborated type with invalid source location,
which would trigger the
`(Keyword != ElaboratedTypeKeyword::None) == KeywordLoc.isValid()`
assertion in `CheckTypenameType`.
rdar://184773464
---
clang/lib/Sema/TreeTransform.h | 27 +++++++++++++++++++++++--
clang/test/SemaObjCXX/blocks.mm | 35 +++++++++++++++++++++++++++++++++
2 files changed, 60 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e482d15f31cc5f..b9115da43bed21 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -17910,8 +17910,31 @@ TreeTransform<Derived>::TransformBlockExpr(BlockExpr *E) {
return ExprError();
}
- QualType exprResultType =
- getDerived().TransformType(exprFunctionType->getReturnType());
+ TypeLoc resultTL;
+ if (TypeSourceInfo *SigTSI = oldBlock->getSignatureAsWritten()) {
+ resultTL = SigTSI->getTypeLoc();
+ // If there's no FunctionProtoTypeLoc, the typeloc is just the return type.
+ if (auto FTL = resultTL.getAsAdjusted<FunctionProtoTypeLoc>())
+ resultTL = FTL.getReturnLoc();
+ }
+ QualType exprResultType;
+ if (resultTL && resultTL.getType() == exprFunctionType->getReturnType()) {
+ // Preserve the source location for any elaborated-type keywords
+ // (e.g. 'typename')
+ TypeSourceInfo *ResultTSI = SemaRef.Context.CreateTypeSourceInfo(
+ resultTL.getType(), resultTL.getFullDataSize());
+ ResultTSI->getTypeLoc().initializeFullCopy(resultTL);
+ if (TypeSourceInfo *NewResultTSI = getDerived().TransformType(ResultTSI))
+ exprResultType = NewResultTSI->getType();
+ } else {
+ // Fallback for deduced return types, since they lack a source location.
+ exprResultType =
+ getDerived().TransformType(exprFunctionType->getReturnType());
+ }
+ if (exprResultType.isNull()) {
+ getSema().ActOnBlockError(E->getCaretLocation(), /*Scope=*/nullptr);
+ return ExprError();
+ }
auto epi = exprFunctionType->getExtProtoInfo();
epi.ExtParameterInfos = extParamInfos.getPointerOrNull(paramTypes.size());
diff --git a/clang/test/SemaObjCXX/blocks.mm b/clang/test/SemaObjCXX/blocks.mm
index 0d63f9c9b10176..c74a3e0ff8f375 100644
--- a/clang/test/SemaObjCXX/blocks.mm
+++ b/clang/test/SemaObjCXX/blocks.mm
@@ -147,6 +147,41 @@ void f(T t) {
template void f<X>(X);
}
+namespace DependentTypenameReturnType {
+ // Success case
+ template <class T> struct S { typedef int type; };
+ template <class T> void f() {
+ auto b = ^ typename S<T>::type () { return 0; };
+ (void)b;
+ }
+ template void f<int>();
+
+ // Same as 'f' but with no parameter list. The written block signature is
+ // then stored as just the return-type loc (there is no FunctionProtoTypeLoc
+ // to look through).
+ template <class T> void g() {
+ auto b = ^ typename S<T>::type { return 0; };
+ (void)b;
+ }
+ template void g<int>();
+
+ // A block with no explicit return type has no return-type source location, so
+ // it takes the deduced-return fallback in TransformBlockExpr.
+ template <class T> T h() {
+ auto b = ^ { T x{}; return x; };
+ return b();
+ }
+ template int h<int>();
+
+ // Failure case
+ template <class T> struct NoType {};
+ template <class T> void i() {
+ auto b = ^ typename NoType<T>::type () { return 0; }; // expected-error{{no type named 'type' in 'DependentTypenameReturnType::NoType<int>'}}
+ (void)b;
+ }
+ template void i<int>(); // expected-note{{in instantiation of function template specialization 'DependentTypenameReturnType::i<int>' requested here}}
+}
+
namespace GenericLambdaCapture {
int test(int outerp) {
auto lambda =[&](auto p) {
More information about the cfe-commits
mailing list