[clang] [clang][Sema] Fix rejection of non-dependent void{} during template instantiation (PR #228340)
Ivy Xu via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 4 07:00:34 PDT 2026
https://github.com/IvyXu420 updated https://github.com/llvm/llvm-project/pull/228340
>From e18bacfeed81a1cf3cd085a88e34ee359079c54d Mon Sep 17 00:00:00 2001
From: Ivy Xu <fakeshadow1337 at gmail.com>
Date: Fri, 2 Oct 2026 03:38:40 +0800
Subject: [PATCH 1/6] Fix clang rejecting valid `void{}` when template is used
---
clang/lib/Sema/SemaExprCXX.cpp | 2 +-
clang/test/CXX/drs/cwg23xx.cpp | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 1bf6430665ddf..1ed58fcf20af0 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1647,7 +1647,7 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
return CXXFunctionalCastExpr::Create(
Context, Ty.getUnqualifiedType(), VK_PRValue, TInfo, CK_ToVoid,
Exprs[0], /*Path=*/nullptr, CurFPFeatureOverrides(),
- Exprs[0]->getBeginLoc(), Exprs[0]->getEndLoc());
+ SourceLocation(), Exprs[0]->getEndLoc());
}
} else if (RequireCompleteType(TyBeginLoc, ElemTy,
diag::err_invalid_incomplete_type_use,
diff --git a/clang/test/CXX/drs/cwg23xx.cpp b/clang/test/CXX/drs/cwg23xx.cpp
index cfd78465c0dbe..c81fe223a596c 100644
--- a/clang/test/CXX/drs/cwg23xx.cpp
+++ b/clang/test/CXX/drs/cwg23xx.cpp
@@ -240,6 +240,10 @@ namespace cwg2351 { // cwg2351: 20
template void i<void>();
template const void i<const void>();
+ template <class T>
+ void f(T) { void{}; }
+ void g() { f(1); }
+
static_assert((void({}), true), "");
// since-cxx11-error at -1 {{cannot initialize non-class type 'void' with a parenthesized initializer list}}
#else
>From 71dd64f2aa5951756198f7ad7a99bd1ab851be3a Mon Sep 17 00:00:00 2001
From: Ivy Xu <fakeshadow1337 at gmail.com>
Date: Fri, 2 Oct 2026 14:25:05 +0800
Subject: [PATCH 2/6] Add release note
---
clang/docs/ReleaseNotes.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index d22874d4130ee..04ded7c5b6a2e 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -801,6 +801,9 @@ features cannot lower the translation-unit ABI level;
take effect, causing them to be dropped from llvm.used and omitted from
the object file. (#GH226572)
+- Fixed rejection of non-dependent `void{}` during template instantiation,
+ which previously rebuilt the expression as `void({})`. (#GH226485, #GH181448)
+
#### Bug Fixes to AST Handling
- Fixed a non-deterministic ordering of unused local typedefs that made
>From ed5657d9a4fcb2dadbbc190d6ca7e4277fcfc5ff Mon Sep 17 00:00:00 2001
From: Ivy Xu <fakeshadow1337 at gmail.com>
Date: Fri, 2 Oct 2026 15:20:06 +0800
Subject: [PATCH 3/6] fix the format
---
clang/lib/Sema/SemaExprCXX.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 1ed58fcf20af0..bb08c3bf1a0e4 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1646,8 +1646,8 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
cast<InitListExpr>(Exprs[0])->getNumInits() == 0) {
return CXXFunctionalCastExpr::Create(
Context, Ty.getUnqualifiedType(), VK_PRValue, TInfo, CK_ToVoid,
- Exprs[0], /*Path=*/nullptr, CurFPFeatureOverrides(),
- SourceLocation(), Exprs[0]->getEndLoc());
+ Exprs[0], /*Path=*/nullptr, CurFPFeatureOverrides(), SourceLocation(),
+ Exprs[0]->getEndLoc());
}
} else if (RequireCompleteType(TyBeginLoc, ElemTy,
diag::err_invalid_incomplete_type_use,
>From 102f689f149cdb8ed4dcec2a3404879ba5fa50bf Mon Sep 17 00:00:00 2001
From: Ivy Xu <fakeshadow1337 at gmail.com>
Date: Fri, 2 Oct 2026 21:08:37 +0800
Subject: [PATCH 4/6] Add a CXXFunctionalCastExpr::CreateListInitializer(...)
function
---
clang/include/clang/AST/ExprCXX.h | 5 +++++
clang/lib/AST/ExprCXX.cpp | 8 ++++++++
clang/lib/Sema/SemaExprCXX.cpp | 21 +++++++++++----------
3 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 1393da4da2721..e42b4cd2d78db 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -1869,6 +1869,11 @@ class CXXFunctionalCastExpr final
static CXXFunctionalCastExpr *
CreateEmpty(const ASTContext &Context, unsigned PathSize, bool HasFPFeatures);
+ static CXXFunctionalCastExpr *
+ CreateListInitializer(const ASTContext &Context, QualType T, ExprValueKind VK,
+ TypeSourceInfo *Written, CastKind Kind, Expr *Op,
+ const CXXCastPath *Path, FPOptionsOverride FPO);
+
SourceLocation getLParenLoc() const { return LParenLoc; }
void setLParenLoc(SourceLocation L) { LParenLoc = L; }
SourceLocation getRParenLoc() const { return RParenLoc; }
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 9d42d47f24840..135269696e044 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -974,6 +974,14 @@ CXXFunctionalCastExpr *CXXFunctionalCastExpr::CreateEmpty(const ASTContext &C,
CXXFunctionalCastExpr(EmptyShell(), PathSize, HasFPFeatures);
}
+CXXFunctionalCastExpr *CXXFunctionalCastExpr::CreateListInitializer(
+ const ASTContext &C, QualType T, ExprValueKind VK, TypeSourceInfo *Written,
+ CastKind K, Expr *Op, const CXXCastPath *BasePath, FPOptionsOverride FPO) {
+ // List-initialization is represented by null paren locations
+ return Create(C, T, VK, Written, K, Op, BasePath, FPO, SourceLocation(),
+ SourceLocation());
+}
+
SourceLocation CXXFunctionalCastExpr::getBeginLoc() const {
return getTypeInfoAsWritten()->getTypeLoc().getBeginLoc();
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index bb08c3bf1a0e4..c32f24808a4df 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1644,10 +1644,9 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
Ty.getUnqualifiedType(), TInfo, Kind.getRange().getEnd());
if (ListInitialization &&
cast<InitListExpr>(Exprs[0])->getNumInits() == 0) {
- return CXXFunctionalCastExpr::Create(
+ return CXXFunctionalCastExpr::CreateListInitializer(
Context, Ty.getUnqualifiedType(), VK_PRValue, TInfo, CK_ToVoid,
- Exprs[0], /*Path=*/nullptr, CurFPFeatureOverrides(), SourceLocation(),
- Exprs[0]->getEndLoc());
+ Exprs[0], /*Path=*/nullptr, CurFPFeatureOverrides());
}
} else if (RequireCompleteType(TyBeginLoc, ElemTy,
diag::err_invalid_incomplete_type_use,
@@ -1687,13 +1686,15 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
// to false.
bool IsListInit = ListInitialization ||
(getLangOpts().HLSL && isa<InitListExpr>(Result.get()));
- SourceRange Locs = IsListInit
- ? SourceRange()
- : SourceRange(LParenOrBraceLoc, RParenOrBraceLoc);
- Result = CXXFunctionalCastExpr::Create(
- Context, ResultType, Expr::getValueKindForType(Ty), TInfo, CK_NoOp,
- Result.get(), /*Path=*/nullptr, CurFPFeatureOverrides(),
- Locs.getBegin(), Locs.getEnd());
+ if (IsListInit)
+ Result = CXXFunctionalCastExpr::CreateListInitializer(
+ Context, ResultType, Expr::getValueKindForType(Ty), TInfo, CK_NoOp,
+ Result.get(), /*Path=*/nullptr, CurFPFeatureOverrides());
+ else
+ Result = CXXFunctionalCastExpr::Create(
+ Context, ResultType, Expr::getValueKindForType(Ty), TInfo, CK_NoOp,
+ Result.get(), /*Path=*/nullptr, CurFPFeatureOverrides(),
+ LParenOrBraceLoc, RParenOrBraceLoc);
}
return Result;
>From c82f63b5480b5f1a779667454be58d99f6cc91a2 Mon Sep 17 00:00:00 2001
From: Ivy Xu <fakeshadow1337 at gmail.com>
Date: Sun, 4 Oct 2026 21:53:43 +0800
Subject: [PATCH 5/6] Address review
---
clang/include/clang/AST/ExprCXX.h | 3 ++-
clang/lib/AST/ExprCXX.cpp | 9 +++++----
clang/lib/Sema/SemaExprCXX.cpp | 6 ++++--
3 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index c1e5cae48116b..0bf35edc47c46 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -1873,7 +1873,8 @@ class CXXFunctionalCastExpr final
static CXXFunctionalCastExpr *
CreateListInitializer(const ASTContext &Context, QualType T, ExprValueKind VK,
TypeSourceInfo *Written, CastKind Kind, Expr *Op,
- const CXXCastPath *Path, FPOptionsOverride FPO);
+ const CXXCastPath *Path, FPOptionsOverride FPO,
+ SourceLocation R);
SourceLocation getLParenLoc() const { return LParenLoc; }
void setLParenLoc(SourceLocation L) { LParenLoc = L; }
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 4897cc16d79cf..ebf3500d9ca6d 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -975,10 +975,11 @@ CXXFunctionalCastExpr *CXXFunctionalCastExpr::CreateEmpty(const ASTContext &C,
CXXFunctionalCastExpr *CXXFunctionalCastExpr::CreateListInitializer(
const ASTContext &C, QualType T, ExprValueKind VK, TypeSourceInfo *Written,
- CastKind K, Expr *Op, const CXXCastPath *BasePath, FPOptionsOverride FPO) {
- // List-initialization is represented by null paren locations
- return Create(C, T, VK, Written, K, Op, BasePath, FPO, SourceLocation(),
- SourceLocation());
+ CastKind K, Expr *Op, const CXXCastPath *BasePath, FPOptionsOverride FPO,
+ SourceLocation R) {
+ // List-initialization is represented by the null LParen location.
+ // FIXME: We should use a bit to represent it.
+ return Create(C, T, VK, Written, K, Op, BasePath, FPO, SourceLocation(), R);
}
SourceLocation CXXFunctionalCastExpr::getBeginLoc() const {
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index e00a8a39ce7a9..fd89fbf95b554 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1646,7 +1646,8 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
cast<InitListExpr>(Exprs[0])->getNumInits() == 0) {
return CXXFunctionalCastExpr::CreateListInitializer(
Context, Ty.getUnqualifiedType(), VK_PRValue, TInfo, CK_ToVoid,
- Exprs[0], /*Path=*/nullptr, CurFPFeatureOverrides());
+ Exprs[0], /*Path=*/nullptr, CurFPFeatureOverrides(),
+ Exprs[0]->getEndLoc());
}
} else if (RequireCompleteType(TyBeginLoc, ElemTy,
diag::err_invalid_incomplete_type_use,
@@ -1689,7 +1690,8 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
if (IsListInit)
Result = CXXFunctionalCastExpr::CreateListInitializer(
Context, ResultType, Expr::getValueKindForType(Ty), TInfo, CK_NoOp,
- Result.get(), /*Path=*/nullptr, CurFPFeatureOverrides());
+ Result.get(), /*Path=*/nullptr, CurFPFeatureOverrides(),
+ RParenOrBraceLoc);
else
Result = CXXFunctionalCastExpr::Create(
Context, ResultType, Expr::getValueKindForType(Ty), TInfo, CK_NoOp,
>From 66ef358821c085886473c9d97f9eb6d44875d794 Mon Sep 17 00:00:00 2001
From: Ivy Xu <fakeshadow1337 at gmail.com>
Date: Sun, 4 Oct 2026 22:00:11 +0800
Subject: [PATCH 6/6] Add the "FIXME" comment to
`CXXFunctionalCastExpr::isListInitialization`
---
clang/include/clang/AST/ExprCXX.h | 1 +
clang/lib/AST/ExprCXX.cpp | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 0bf35edc47c46..cef23154b9230 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -1882,6 +1882,7 @@ class CXXFunctionalCastExpr final
void setRParenLoc(SourceLocation L) { RParenLoc = L; }
/// Determine whether this expression models list-initialization.
+ /// FIXME: We should use a bit to represent it.
bool isListInitialization() const { return LParenLoc.isInvalid(); }
SourceLocation getBeginLoc() const LLVM_READONLY;
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index ebf3500d9ca6d..4c64359154e4d 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -978,7 +978,6 @@ CXXFunctionalCastExpr *CXXFunctionalCastExpr::CreateListInitializer(
CastKind K, Expr *Op, const CXXCastPath *BasePath, FPOptionsOverride FPO,
SourceLocation R) {
// List-initialization is represented by the null LParen location.
- // FIXME: We should use a bit to represent it.
return Create(C, T, VK, Written, K, Op, BasePath, FPO, SourceLocation(), R);
}
More information about the cfe-commits
mailing list