[clang] [clang] Diagnose VLA types from an enclosing function in local classes and blocks (PR #229697)
Ivan Ugryumov via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 18 04:10:21 PDT 2026
https://github.com/Servall4 updated https://github.com/llvm/llvm-project/pull/229697
>From 87b9ce63ce79665d20d72a7492fb713f83c44702 Mon Sep 17 00:00:00 2001
From: Ivan Ugryumov <smille2003 at yandex.ru>
Date: Wed, 7 Oct 2026 11:17:49 +0300
Subject: [PATCH 1/6] [clang] Diagnose VLA types from an enclosing function in
local classes and blocks
A variably modified type whose array bound is evaluated in an enclosing
function cannot be used in a member function of a local class or in a
block: the size is only computed in the enclosing function, so CodeGen
crashed when emitting such a declaration.
Record the context in which each VLA bound is evaluated, and diagnose
variable and parameter declarations whose type has such a bound when they
appear across a local class or block boundary.
Fixes #229694
---
.../clang/Basic/DiagnosticSemaKinds.td | 6 ++
clang/include/clang/Sema/Sema.h | 9 ++
clang/lib/Sema/SemaDecl.cpp | 12 +++
clang/lib/Sema/SemaExpr.cpp | 100 ++++++++++++++++--
clang/lib/Sema/SemaType.cpp | 8 ++
.../test/Sema/vla-enclosing-function-blocks.c | 18 ++++
clang/test/SemaCXX/vla-enclosing-function.cpp | 56 ++++++++++
7 files changed, 198 insertions(+), 11 deletions(-)
create mode 100644 clang/test/Sema/vla-enclosing-function-blocks.c
create mode 100644 clang/test/SemaCXX/vla-enclosing-function.cpp
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9208aba1445d7e..c5d1ff7a213953 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8314,6 +8314,12 @@ def err_unexpected_interface : Error<
def err_ref_non_value : Error<"%0 does not refer to a value">;
def err_ref_vm_type : Error<
"cannot refer to declaration with a variably modified type inside block">;
+def err_vm_type_from_enclosing_function : Error<
+ "variably modified type %0 cannot be used in a "
+ "%select{local class|block}1 because its size is evaluated in an "
+ "enclosing function">;
+def note_vm_type_size_evaluated_here : Note<
+ "size expression is evaluated here">;
def err_ref_flexarray_type : Error<
"cannot refer to declaration of structure variable with flexible array member "
"inside block">;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 7fbcc4aae7d6c5..fc8d7ce6bb425c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7222,6 +7222,11 @@ class Sema final : public SemaBase {
/// variable will have in the given scope.
QualType getCapturedDeclRefType(ValueDecl *Var, SourceLocation Loc);
+ /// Diagnose a use of the variably modified type \p T in a local class or
+ /// block when one of its array bounds is evaluated in an enclosing function.
+ /// Returns false if a diagnostic was emitted.
+ bool CheckVariablyModifiedTypeUse(QualType T, SourceLocation Loc);
+
/// Mark all of the declarations referenced within a particular AST node as
/// referenced. Used when template instantiation instantiates a non-dependent
/// type -- entities referenced by the type are now referenced.
@@ -8435,6 +8440,10 @@ class Sema final : public SemaBase {
private:
static BinaryOperatorKind ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind);
+ /// Maps the size expression of each VLA type created inside a function,
+ /// block or lambda to the context in which that size is evaluated.
+ llvm::DenseMap<const Expr *, const DeclContext *> VLASizeExprOwners;
+
/// Methods for marking which expressions involve dereferencing a pointer
/// marked with the 'noderef' attribute. Expressions are checked bottom up as
/// they are parsed, meaning that a noderef pointer may not be accessed. For
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index fdbe1521165122..2db8fa88676daa 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -9002,6 +9002,14 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
if (NewVD->isInvalidDecl())
return;
+ SourceLocation VMLoc = NewVD->getLocation();
+ if (TypeSourceInfo *TSI = NewVD->getTypeSourceInfo())
+ VMLoc = TSI->getTypeLoc().getBeginLoc();
+ if (!CheckVariablyModifiedTypeUse(NewVD->getType(), VMLoc)) {
+ NewVD->setInvalidDecl();
+ return;
+ }
+
QualType T = NewVD->getType();
// Defer checking an 'auto' type until its initializer is attached.
@@ -16135,6 +16143,10 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
Context.getAdjustedParameterType(T),
TSInfo, SC, nullptr);
+ SourceLocation VMLoc = TSInfo ? TSInfo->getTypeLoc().getBeginLoc() : NameLoc;
+ if (!CheckVariablyModifiedTypeUse(T, VMLoc))
+ New->setInvalidDecl();
+
// Make a note if we created a new pack in the scope of a lambda, so that
// we know that references to that pack must also be expanded within the
// lambda scope.
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 9b3384cdbdac8c..d7650ae54452a5 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4652,10 +4652,12 @@ bool Sema::CheckVecStepExpr(Expr *E) {
return CheckUnaryExprOrTypeTraitOperand(E, UETT_VecStep);
}
-static void captureVariablyModifiedType(ASTContext &Context, QualType T,
- CapturingScopeInfo *CSI) {
+/// Walk down into the variably modified type \p T and call \p Fn for each
+/// variable length array type in it.
+static void forEachVariableArrayType(
+ ASTContext &Context, QualType T,
+ llvm::function_ref<void(const VariableArrayType *)> Fn) {
assert(T->isVariablyModifiedType());
- assert(CSI != nullptr);
// We're going to walk down into the type and look for VLA expressions.
do {
@@ -4716,14 +4718,7 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T,
case Type::VariableArray: {
// Losing element qualification here is fine.
const VariableArrayType *VAT = cast<VariableArrayType>(Ty);
-
- // Unknown size indication requires no size computation.
- // Otherwise, evaluate and record it.
- auto Size = VAT->getSizeExpr();
- if (Size && !CSI->isVLATypeCaptured(VAT) &&
- (isa<CapturedRegionScopeInfo>(CSI) || isa<LambdaScopeInfo>(CSI)))
- CSI->addVLATypeCapture(Size->getExprLoc(), VAT, Context.getSizeType());
-
+ Fn(VAT);
T = VAT->getElementType();
break;
}
@@ -4774,6 +4769,89 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T,
} while (!T.isNull() && T->isVariablyModifiedType());
}
+static void captureVariablyModifiedType(ASTContext &Context, QualType T,
+ CapturingScopeInfo *CSI) {
+ assert(CSI != nullptr);
+ forEachVariableArrayType(Context, T, [&](const VariableArrayType *VAT) {
+ // Unknown size indication requires no size computation.
+ // Otherwise, evaluate and record it.
+ auto Size = VAT->getSizeExpr();
+ if (Size && !CSI->isVLATypeCaptured(VAT) &&
+ (isa<CapturedRegionScopeInfo>(CSI) || isa<LambdaScopeInfo>(CSI)))
+ CSI->addVLATypeCapture(Size->getExprLoc(), VAT, Context.getSizeType());
+ });
+}
+
+/// Find what separates \p DC from \p Owner, the context in which a VLA bound
+/// is evaluated: 0 for a local class, 1 for a block, or std::nullopt if the
+/// bound is available in \p DC or \p Owner does not enclose it.
+static std::optional<unsigned> findVLABoundBarrier(const DeclContext *DC,
+ const DeclContext *Owner) {
+ std::optional<unsigned> Barrier;
+ for (; DC; DC = DC->getParent()) {
+ if (DC == Owner)
+ return Barrier;
+ // Lambdas and captured regions can capture a VLA bound (see
+ // captureVariablyModifiedType), so they are not barriers here.
+ if (isLambdaCallOperator(DC) || isa<CapturedDecl>(DC))
+ continue;
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(DC); RD && RD->isLambda())
+ continue;
+ if (isa<BlockDecl>(DC)) {
+ if (!Barrier)
+ Barrier = 1;
+ continue;
+ }
+ if (isa<RecordDecl>(DC) && !Barrier)
+ Barrier = 0;
+ }
+ return std::nullopt;
+}
+
+static bool isInLocalClassOrBlock(const DeclContext *DC) {
+ for (; DC; DC = DC->getParent()) {
+ if (isa<BlockDecl>(DC))
+ return true;
+ if (const auto *RD = dyn_cast<RecordDecl>(DC)) {
+ if (const auto *CRD = dyn_cast<CXXRecordDecl>(RD); CRD && CRD->isLambda())
+ continue;
+ if (RD->getParentFunctionOrMethod())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Sema::CheckVariablyModifiedTypeUse(QualType T, SourceLocation Loc) {
+ if (VLASizeExprOwners.empty() || T.isNull())
+ return true;
+ // Deduced types (auto, __auto_type) do not propagate the variably modified
+ // bit, so look at the canonical type.
+ QualType CanonT = T.getCanonicalType();
+ if (!CanonT->isVariablyModifiedType() || !isInLocalClassOrBlock(CurContext))
+ return true;
+
+ const Expr *BadSize = nullptr;
+ std::optional<unsigned> Barrier;
+ forEachVariableArrayType(Context, CanonT, [&](const VariableArrayType *VAT) {
+ const Expr *Size = VAT->getSizeExpr();
+ if (BadSize || !Size)
+ return;
+ auto It = VLASizeExprOwners.find(Size);
+ if (It == VLASizeExprOwners.end())
+ return;
+ Barrier = findVLABoundBarrier(CurContext, It->second);
+ if (Barrier)
+ BadSize = Size;
+ });
+ if (!BadSize)
+ return true;
+
+ Diag(Loc, diag::err_vm_type_from_enclosing_function) << T << *Barrier;
+ Diag(BadSize->getBeginLoc(), diag::note_vm_type_size_evaluated_here);
+ return false;
+}
+
bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
SourceLocation OpLoc,
SourceRange ExprRange,
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 202bd486321f94..3bcc09a571c765 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -2370,6 +2370,14 @@ QualType Sema::BuildArrayType(QualType T, ArraySizeModifier ASM,
}
}
+ // Remember where the bound of a VLA is evaluated. Bounds in a function
+ // prototype are evaluated in that function, which doesn't exist yet;
+ // those are not recorded and never diagnosed.
+ if (const VariableArrayType *VAT = Context.getAsVariableArrayType(T))
+ if (CurContext->isFunctionOrMethod())
+ if (const Expr *Size = VAT->getSizeExpr())
+ VLASizeExprOwners.try_emplace(Size, CurContext);
+
return T;
}
diff --git a/clang/test/Sema/vla-enclosing-function-blocks.c b/clang/test/Sema/vla-enclosing-function-blocks.c
new file mode 100644
index 00000000000000..42961dcf3bd1c8
--- /dev/null
+++ b/clang/test/Sema/vla-enclosing-function-blocks.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -verify %s
+
+// GH229694
+int size_tab(void);
+int main(void) {
+ __auto_type tab = (int(*)[size_tab()])0; // expected-note {{size expression is evaluated here}}
+ (^ int(__typeof(tab) arr) { return sizeof(*arr); })(tab); // expected-error {{cannot be used in a block}}
+}
+
+void var_in_block(void) {
+ typedef int X[size_tab()]; // expected-note {{size expression is evaluated here}}
+ ^{ X x; }(); // expected-error {{cannot be used in a block}}
+}
+
+// Valid: VLA declared inside the block itself.
+int own_vla_in_block(void) {
+ return (^ int(void) { int a[size_tab()]; return sizeof(a); })();
+}
diff --git a/clang/test/SemaCXX/vla-enclosing-function.cpp b/clang/test/SemaCXX/vla-enclosing-function.cpp
new file mode 100644
index 00000000000000..3e9ec40a766a01
--- /dev/null
+++ b/clang/test/SemaCXX/vla-enclosing-function.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -fsyntax-only -Wno-vla-cxx-extension -verify %s
+
+// GH229694
+int foo();
+
+void ctor() {
+ using X = int[foo()]; // expected-note {{size expression is evaluated here}}
+ struct S { S() { X x; } }; // expected-error {{cannot be used in a local class because its size is evaluated in an enclosing function}}
+}
+
+void sizeof_enclosing_vla_var() {
+ int a[foo()]; // expected-note {{'a' declared here}}
+ struct S { unsigned long f() { return sizeof(a); } }; // expected-error {{reference to local variable 'a' declared in enclosing function}}
+}
+
+void decltype_var() {
+ int a[foo()]; // expected-note {{size expression is evaluated here}}
+ struct S { void f() { decltype(a) b; } }; // expected-error {{cannot be used in a local class}}
+}
+
+void nested_local_class() {
+ using X = int[foo()]; // expected-note {{size expression is evaluated here}}
+ struct S {
+ void f() { struct T { T() { X x; } }; } // expected-error {{cannot be used in a local class}}
+ };
+}
+
+void pointer_to_vla() {
+ using P = int (*)[foo()]; // expected-note {{size expression is evaluated here}}
+ struct S { unsigned long f(P p) { return sizeof(*p); } }; // expected-error {{cannot be used in a local class}}
+}
+
+template <typename T> void in_template() {
+ using X = int[foo()]; // expected-note {{size expression is evaluated here}}
+ struct S { S() { X x; } }; // expected-error {{cannot be used in a local class}}
+}
+
+// Valid: no diagnostics.
+int same_function() {
+ using X = int[foo()];
+ X x;
+ return sizeof(X) + sizeof(x);
+}
+
+void own_vla_in_local_class() {
+ struct S { void f() { int a[foo()]; (void)sizeof(a); } };
+}
+
+void param_vla_in_local_class() {
+ struct S { unsigned long f(int n, int (*p)[n]) { return sizeof(*p); } };
+}
+
+int lambda_sizeof() {
+ using X = int[foo()];
+ return [&] { return (int)sizeof(X); }();
+}
>From 91c1b302f647de239c1aea348c8e4f9f050cd38a Mon Sep 17 00:00:00 2001
From: Ivan Ugryumov <smille2003 at yandex.ru>
Date: Thu, 8 Oct 2026 09:57:42 +0300
Subject: [PATCH 2/6] Also reject member typedefs of such types in local
classes
Diagnose a typedef or alias member of a class that names a variably
modified type whose bound is evaluated in an enclosing function, as GCC
does, and don't diagnose uses of such a typedef again.
Add the example from GH55686 and reference that issue in the tests.
---
clang/lib/Sema/SemaDecl.cpp | 9 +++++++++
clang/lib/Sema/SemaExpr.cpp | 5 +++++
clang/test/Sema/vla-enclosing-function-blocks.c | 2 +-
clang/test/SemaCXX/vla-enclosing-function.cpp | 17 ++++++++++++++++-
4 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 2db8fa88676daa..e43ac27c4110f4 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7017,6 +7017,15 @@ Sema::CheckTypedefForVariablyModifiedType(Scope *S, TypedefNameDecl *NewTD) {
// that redeclarations will match.
TypeSourceInfo *TInfo = NewTD->getTypeSourceInfo();
QualType T = TInfo->getType();
+
+ // A member typedef cannot name a variably modified type whose bound is
+ // evaluated in an enclosing function.
+ if (NewTD->getDeclContext()->isRecord() &&
+ !CheckVariablyModifiedTypeUse(T, TInfo->getTypeLoc().getBeginLoc())) {
+ NewTD->setInvalidDecl();
+ return;
+ }
+
if (T->isVariablyModifiedType()) {
setFunctionHasBranchProtectedScope();
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index d7650ae54452a5..40a967ec8eb63c 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4847,6 +4847,11 @@ bool Sema::CheckVariablyModifiedTypeUse(QualType T, SourceLocation Loc) {
if (!BadSize)
return true;
+ // A member typedef naming such a type has already been diagnosed.
+ if (const auto *TT = T->getAs<TypedefType>();
+ TT && TT->getDecl()->isInvalidDecl())
+ return false;
+
Diag(Loc, diag::err_vm_type_from_enclosing_function) << T << *Barrier;
Diag(BadSize->getBeginLoc(), diag::note_vm_type_size_evaluated_here);
return false;
diff --git a/clang/test/Sema/vla-enclosing-function-blocks.c b/clang/test/Sema/vla-enclosing-function-blocks.c
index 42961dcf3bd1c8..2306087f68768e 100644
--- a/clang/test/Sema/vla-enclosing-function-blocks.c
+++ b/clang/test/Sema/vla-enclosing-function-blocks.c
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -fblocks -verify %s
-// GH229694
+// GH55686
int size_tab(void);
int main(void) {
__auto_type tab = (int(*)[size_tab()])0; // expected-note {{size expression is evaluated here}}
diff --git a/clang/test/SemaCXX/vla-enclosing-function.cpp b/clang/test/SemaCXX/vla-enclosing-function.cpp
index 3e9ec40a766a01..5ee408c7544dee 100644
--- a/clang/test/SemaCXX/vla-enclosing-function.cpp
+++ b/clang/test/SemaCXX/vla-enclosing-function.cpp
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -Wno-vla-cxx-extension -verify %s
-// GH229694
+// GH55686
int foo();
void ctor() {
@@ -35,6 +35,21 @@ template <typename T> void in_template() {
struct S { S() { X x; } }; // expected-error {{cannot be used in a local class}}
}
+namespace GH55686 {
+void f(int n) {
+ using T = int[n]; // expected-note 2 {{size expression is evaluated here}}
+ struct A {
+ using U = T; // expected-error {{cannot be used in a local class}}
+ void f() { U u; }
+ void g() { T t; } // expected-error {{cannot be used in a local class}}
+ };
+ A::U u;
+ A a;
+ a.f();
+ a.g();
+}
+} // namespace GH55686
+
// Valid: no diagnostics.
int same_function() {
using X = int[foo()];
>From 4225f28176f3f882f267c5a6e02ae1941cecc656 Mon Sep 17 00:00:00 2001
From: Ivan Ugryumov <smille2003 at yandex.ru>
Date: Wed, 14 Oct 2026 10:20:25 +0300
Subject: [PATCH 3/6] Handle VLA bound captures and diagnostics in one function
---
clang/include/clang/Sema/Sema.h | 7 +-
clang/lib/Sema/SemaDecl.cpp | 5 +-
clang/lib/Sema/SemaExpr.cpp | 125 +++++++++---------
.../vla-lambda-enclosing-function.cpp | 27 ++++
.../test/Sema/vla-enclosing-function-blocks.c | 5 +
clang/test/SemaCXX/vla-enclosing-function.cpp | 5 +
6 files changed, 101 insertions(+), 73 deletions(-)
create mode 100644 clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index fc8d7ce6bb425c..858f763ceaf306 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7222,9 +7222,10 @@ class Sema final : public SemaBase {
/// variable will have in the given scope.
QualType getCapturedDeclRefType(ValueDecl *Var, SourceLocation Loc);
- /// Diagnose a use of the variably modified type \p T in a local class or
- /// block when one of its array bounds is evaluated in an enclosing function.
- /// Returns false if a diagnostic was emitted.
+ /// Handle a use of the variably modified type \p T in the current context.
+ /// Array bounds evaluated in an enclosing context are captured by the
+ /// lambdas in between; if a local class or a block is in between instead,
+ /// the use is diagnosed. Returns false if the use is invalid.
bool CheckVariablyModifiedTypeUse(QualType T, SourceLocation Loc);
/// Mark all of the declarations referenced within a particular AST node as
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e43ac27c4110f4..3c0dd3740e9ddf 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7018,10 +7018,7 @@ Sema::CheckTypedefForVariablyModifiedType(Scope *S, TypedefNameDecl *NewTD) {
TypeSourceInfo *TInfo = NewTD->getTypeSourceInfo();
QualType T = TInfo->getType();
- // A member typedef cannot name a variably modified type whose bound is
- // evaluated in an enclosing function.
- if (NewTD->getDeclContext()->isRecord() &&
- !CheckVariablyModifiedTypeUse(T, TInfo->getTypeLoc().getBeginLoc())) {
+ if (!CheckVariablyModifiedTypeUse(T, TInfo->getTypeLoc().getBeginLoc())) {
NewTD->setInvalidDecl();
return;
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 40a967ec8eb63c..bfad1c3cf32468 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4782,44 +4782,20 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T,
});
}
-/// Find what separates \p DC from \p Owner, the context in which a VLA bound
-/// is evaluated: 0 for a local class, 1 for a block, or std::nullopt if the
-/// bound is available in \p DC or \p Owner does not enclose it.
-static std::optional<unsigned> findVLABoundBarrier(const DeclContext *DC,
- const DeclContext *Owner) {
- std::optional<unsigned> Barrier;
- for (; DC; DC = DC->getParent()) {
- if (DC == Owner)
- return Barrier;
- // Lambdas and captured regions can capture a VLA bound (see
- // captureVariablyModifiedType), so they are not barriers here.
- if (isLambdaCallOperator(DC) || isa<CapturedDecl>(DC))
- continue;
- if (const auto *RD = dyn_cast<CXXRecordDecl>(DC); RD && RD->isLambda())
- continue;
- if (isa<BlockDecl>(DC)) {
- if (!Barrier)
- Barrier = 1;
- continue;
- }
- if (isa<RecordDecl>(DC) && !Barrier)
- Barrier = 0;
- }
- return std::nullopt;
-}
-
-static bool isInLocalClassOrBlock(const DeclContext *DC) {
- for (; DC; DC = DC->getParent()) {
- if (isa<BlockDecl>(DC))
- return true;
- if (const auto *RD = dyn_cast<RecordDecl>(DC)) {
- if (const auto *CRD = dyn_cast<CXXRecordDecl>(RD); CRD && CRD->isLambda())
- continue;
- if (RD->getParentFunctionOrMethod())
- return true;
- }
+/// Find the capturing scope that is being built for the lambda call operator
+/// or captured declaration \p DC, if any.
+static CapturingScopeInfo *
+findCapturingScope(ArrayRef<FunctionScopeInfo *> Scopes,
+ const DeclContext *DC) {
+ for (FunctionScopeInfo *FSI : llvm::reverse(Scopes)) {
+ if (auto *LSI = dyn_cast<LambdaScopeInfo>(FSI);
+ LSI && LSI->CallOperator == DC)
+ return LSI;
+ if (auto *CRSI = dyn_cast<CapturedRegionScopeInfo>(FSI);
+ CRSI && CRSI->TheCapturedDecl == DC)
+ return CRSI;
}
- return false;
+ return nullptr;
}
bool Sema::CheckVariablyModifiedTypeUse(QualType T, SourceLocation Loc) {
@@ -4828,11 +4804,11 @@ bool Sema::CheckVariablyModifiedTypeUse(QualType T, SourceLocation Loc) {
// Deduced types (auto, __auto_type) do not propagate the variably modified
// bit, so look at the canonical type.
QualType CanonT = T.getCanonicalType();
- if (!CanonT->isVariablyModifiedType() || !isInLocalClassOrBlock(CurContext))
+ if (!CanonT->isVariablyModifiedType())
return true;
const Expr *BadSize = nullptr;
- std::optional<unsigned> Barrier;
+ unsigned BadKind = 0;
forEachVariableArrayType(Context, CanonT, [&](const VariableArrayType *VAT) {
const Expr *Size = VAT->getSizeExpr();
if (BadSize || !Size)
@@ -4840,19 +4816,56 @@ bool Sema::CheckVariablyModifiedTypeUse(QualType T, SourceLocation Loc) {
auto It = VLASizeExprOwners.find(Size);
if (It == VLASizeExprOwners.end())
return;
- Barrier = findVLABoundBarrier(CurContext, It->second);
- if (Barrier)
+
+ // Walk from the use to the context in which the bound is evaluated.
+ // Lambdas and captured regions on the way capture the bound; a local
+ // class or a block cannot see it.
+ SmallVector<CapturingScopeInfo *, 2> Capturers;
+ std::optional<unsigned> Barrier;
+ const DeclContext *DC = CurContext;
+ for (; DC && DC != It->second; DC = DC->getParent()) {
+ if (isLambdaCallOperator(DC) || isa<CapturedDecl>(DC)) {
+ if (CapturingScopeInfo *CSI = findCapturingScope(FunctionScopes, DC))
+ Capturers.push_back(CSI);
+ } else if (isa<BlockDecl>(DC)) {
+ Barrier = Barrier.value_or(1);
+ } else if (const auto *RD = dyn_cast<RecordDecl>(DC)) {
+ if (const auto *CRD = dyn_cast<CXXRecordDecl>(RD);
+ !CRD || !CRD->isLambda())
+ Barrier = Barrier.value_or(0);
+ }
+ }
+ // Nothing to do if the bound is not evaluated in an enclosing context.
+ if (!DC)
+ return;
+ if (Barrier) {
BadSize = Size;
+ BadKind = *Barrier;
+ return;
+ }
+ // VAT is a canonical type; the bound may already have been captured
+ // through a different type node with the same size expression.
+ for (CapturingScopeInfo *CSI : Capturers)
+ if (llvm::none_of(CSI->Captures, [&](const Capture &C) {
+ return C.isVLATypeCapture() &&
+ C.getCapturedVLAType()->getSizeExpr() == Size;
+ }))
+ CSI->addVLATypeCapture(Size->getExprLoc(), VAT, Context.getSizeType());
});
if (!BadSize)
return true;
- // A member typedef naming such a type has already been diagnosed.
+ // Default arguments and default member initializers are evaluated where
+ // they are used, and unevaluated operands are not evaluated at all.
+ if (isUnevaluatedContext() || isCheckingDefaultArgumentOrInitializer())
+ return true;
+
+ // A typedef naming such a type has already been diagnosed.
if (const auto *TT = T->getAs<TypedefType>();
TT && TT->getDecl()->isInvalidDecl())
return false;
- Diag(Loc, diag::err_vm_type_from_enclosing_function) << T << *Barrier;
+ Diag(Loc, diag::err_vm_type_from_enclosing_function) << T << BadKind;
Diag(BadSize->getBeginLoc(), diag::note_vm_type_size_evaluated_here);
return false;
}
@@ -4935,29 +4948,9 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
ExprKind))
return true;
- if (ExprType->isVariablyModifiedType() && FunctionScopes.size() > 1) {
- if (auto *TT = ExprType->getAs<TypedefType>()) {
- for (auto I = FunctionScopes.rbegin(),
- E = std::prev(FunctionScopes.rend());
- I != E; ++I) {
- auto *CSI = dyn_cast<CapturingScopeInfo>(*I);
- if (CSI == nullptr)
- break;
- DeclContext *DC = nullptr;
- if (auto *LSI = dyn_cast<LambdaScopeInfo>(CSI))
- DC = LSI->CallOperator;
- else if (auto *CRSI = dyn_cast<CapturedRegionScopeInfo>(CSI))
- DC = CRSI->TheCapturedDecl;
- else if (auto *BSI = dyn_cast<BlockScopeInfo>(CSI))
- DC = BSI->TheDecl;
- if (DC) {
- if (DC->containsDecl(TT->getDecl()))
- break;
- captureVariablyModifiedType(Context, ExprType, CSI);
- }
- }
- }
- }
+ if (ExprType->getAs<TypedefType>() &&
+ !CheckVariablyModifiedTypeUse(ExprType, OpLoc))
+ return true;
return false;
}
diff --git a/clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp b/clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp
new file mode 100644
index 00000000000000..d3d245eed22fdd
--- /dev/null
+++ b/clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -Wno-vla-cxx-extension -emit-llvm -o - %s | FileCheck %s
+
+// A lambda that declares a variable of a VLA type from the enclosing function
+// captures the array bound.
+
+int foo();
+
+// CHECK-LABEL: define {{.*}} @_Z9by_copy_tv(
+// CHECK: [[SIZE:%.*]] = call noundef i32 @_Z3foov()
+// CHECK: [[EXT:%.*]] = zext i32 [[SIZE]] to i64
+// CHECK: store i64 [[EXT]], ptr {{%.*}}
+// CHECK-LABEL: define internal {{.*}} @"_ZZ9by_copy_tvENK3$_0clEv"(
+// CHECK: [[BOUND:%[0-9]+]] = load i64, ptr {{%[0-9]+}}
+// CHECK: alloca i32, i64 [[BOUND]]
+int by_copy_t() {
+ using X = int[foo()];
+ return [] { X x; return (int)sizeof(x); }();
+}
+
+// CHECK-LABEL: define {{.*}} @_Z8nested_tv(
+// CHECK-LABEL: define internal {{.*}} @"_ZZZ8nested_tvENK3$_0clEvENKUlvE_clEv"(
+// CHECK: [[BOUND:%[0-9]+]] = load i64, ptr {{%[0-9]+}}
+// CHECK: alloca i32, i64 [[BOUND]]
+int nested_t() {
+ using X = int[foo()];
+ return [&] { return [&] { X x; return (int)sizeof(x); }(); }();
+}
diff --git a/clang/test/Sema/vla-enclosing-function-blocks.c b/clang/test/Sema/vla-enclosing-function-blocks.c
index 2306087f68768e..7534089d6bf053 100644
--- a/clang/test/Sema/vla-enclosing-function-blocks.c
+++ b/clang/test/Sema/vla-enclosing-function-blocks.c
@@ -12,6 +12,11 @@ void var_in_block(void) {
^{ X x; }(); // expected-error {{cannot be used in a block}}
}
+void typedef_in_block(void) {
+ typedef int X[size_tab()]; // expected-note {{size expression is evaluated here}}
+ ^{ typedef X Y; }(); // expected-error {{cannot be used in a block}}
+}
+
// Valid: VLA declared inside the block itself.
int own_vla_in_block(void) {
return (^ int(void) { int a[size_tab()]; return sizeof(a); })();
diff --git a/clang/test/SemaCXX/vla-enclosing-function.cpp b/clang/test/SemaCXX/vla-enclosing-function.cpp
index 5ee408c7544dee..516a3ee604cd8b 100644
--- a/clang/test/SemaCXX/vla-enclosing-function.cpp
+++ b/clang/test/SemaCXX/vla-enclosing-function.cpp
@@ -30,6 +30,11 @@ void pointer_to_vla() {
struct S { unsigned long f(P p) { return sizeof(*p); } }; // expected-error {{cannot be used in a local class}}
}
+void typedef_in_member_function() {
+ using X = int[foo()]; // expected-note {{size expression is evaluated here}}
+ struct S { void f() { typedef X Y; } }; // expected-error {{cannot be used in a local class}}
+}
+
template <typename T> void in_template() {
using X = int[foo()]; // expected-note {{size expression is evaluated here}}
struct S { S() { X x; } }; // expected-error {{cannot be used in a local class}}
>From 87ecda45fb525993a3d96c8465d7e91ccf161a44 Mon Sep 17 00:00:00 2001
From: Ivan Ugryumov <smille2003 at yandex.ru>
Date: Wed, 14 Oct 2026 11:21:53 +0300
Subject: [PATCH 4/6] Use CheckVariablyModifiedTypeUse for array subscripts as
well
---
clang/lib/Sema/SemaExpr.cpp | 30 ++++--------------------------
1 file changed, 4 insertions(+), 26 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index bfad1c3cf32468..04b5dda1c9f763 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5718,32 +5718,10 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc,
assert(VK == VK_PRValue || LangOpts.CPlusPlus ||
!ResultType.isCForbiddenLValueType());
- if (LHSExp->IgnoreParenImpCasts()->getType()->isVariablyModifiedType() &&
- FunctionScopes.size() > 1) {
- if (auto *TT =
- LHSExp->IgnoreParenImpCasts()->getType()->getAs<TypedefType>()) {
- for (auto I = FunctionScopes.rbegin(),
- E = std::prev(FunctionScopes.rend());
- I != E; ++I) {
- auto *CSI = dyn_cast<CapturingScopeInfo>(*I);
- if (CSI == nullptr)
- break;
- DeclContext *DC = nullptr;
- if (auto *LSI = dyn_cast<LambdaScopeInfo>(CSI))
- DC = LSI->CallOperator;
- else if (auto *CRSI = dyn_cast<CapturedRegionScopeInfo>(CSI))
- DC = CRSI->TheCapturedDecl;
- else if (auto *BSI = dyn_cast<BlockScopeInfo>(CSI))
- DC = BSI->TheDecl;
- if (DC) {
- if (DC->containsDecl(TT->getDecl()))
- break;
- captureVariablyModifiedType(
- Context, LHSExp->IgnoreParenImpCasts()->getType(), CSI);
- }
- }
- }
- }
+ QualType BaseType = LHSExp->IgnoreParenImpCasts()->getType();
+ if (BaseType->getAs<TypedefType>() &&
+ !CheckVariablyModifiedTypeUse(BaseType, LLoc))
+ return ExprError();
return new (Context)
ArraySubscriptExpr(LHSExp, RHSExp, ResultType, VK, OK, RLoc);
>From 311ae64ec9d4260ae4443f6aa4fe82c5077cedda Mon Sep 17 00:00:00 2001
From: Ivan Ugryumov <smille2003 at yandex.ru>
Date: Sun, 18 Oct 2026 09:15:53 +0300
Subject: [PATCH 5/6] Don't record VLA bounds in prototypes; capture on casts
instead of subscripts
---
clang/lib/Sema/SemaCast.cpp | 11 ++++++++++
clang/lib/Sema/SemaExpr.cpp | 5 -----
clang/lib/Sema/SemaType.cpp | 7 ++++---
.../vla-lambda-enclosing-function.cpp | 18 ++++++++++++++++
.../test/Sema/vla-enclosing-function-blocks.c | 10 +++++++++
clang/test/SemaCXX/vla-enclosing-function.cpp | 21 +++++++++++++++++++
6 files changed, 64 insertions(+), 8 deletions(-)
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index c797bdb11e0cbd..03df93370dd394 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -340,6 +340,9 @@ Sema::BuildCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind,
SourceRange AngleBrackets, SourceRange Parens) {
ExprResult Ex = E;
QualType DestType = DestTInfo->getType();
+ if (!CheckVariablyModifiedTypeUse(DestType,
+ DestTInfo->getTypeLoc().getBeginLoc()))
+ return ExprError();
// If the type is dependent, we won't do the semantic analysis now.
bool TypeDependent =
@@ -3477,6 +3480,10 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc,
TypeSourceInfo *CastTypeInfo,
SourceLocation RPLoc,
Expr *CastExpr) {
+ if (!CheckVariablyModifiedTypeUse(CastTypeInfo->getType(),
+ CastTypeInfo->getTypeLoc().getBeginLoc()))
+ return ExprError();
+
CastOperation Op(*this, CastTypeInfo->getType(), CastExpr);
Op.DestRange = CastTypeInfo->getTypeLoc().getSourceRange();
Op.OpRange = CastOperation::OpRangeType(LPLoc, LPLoc, CastExpr->getEndLoc());
@@ -3507,6 +3514,10 @@ ExprResult Sema::BuildCXXFunctionalCastExpr(TypeSourceInfo *CastTypeInfo,
Expr *CastExpr,
SourceLocation RPLoc) {
assert(LPLoc.isValid() && "List-initialization shouldn't get here.");
+ if (!CheckVariablyModifiedTypeUse(Type,
+ CastTypeInfo->getTypeLoc().getBeginLoc()))
+ return ExprError();
+
CastOperation Op(*this, Type, CastExpr);
Op.DestRange = CastTypeInfo->getTypeLoc().getSourceRange();
Op.OpRange =
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 04b5dda1c9f763..6311f89baf459a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5718,11 +5718,6 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc,
assert(VK == VK_PRValue || LangOpts.CPlusPlus ||
!ResultType.isCForbiddenLValueType());
- QualType BaseType = LHSExp->IgnoreParenImpCasts()->getType();
- if (BaseType->getAs<TypedefType>() &&
- !CheckVariablyModifiedTypeUse(BaseType, LLoc))
- return ExprError();
-
return new (Context)
ArraySubscriptExpr(LHSExp, RHSExp, ResultType, VK, OK, RLoc);
}
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 3bcc09a571c765..e3ed5b0eff097b 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -2371,10 +2371,11 @@ QualType Sema::BuildArrayType(QualType T, ArraySizeModifier ASM,
}
// Remember where the bound of a VLA is evaluated. Bounds in a function
- // prototype are evaluated in that function, which doesn't exist yet;
- // those are not recorded and never diagnosed.
+ // prototype are evaluated in that function, which doesn't exist yet, so they
+ // are not recorded and their uses are not diagnosed.
if (const VariableArrayType *VAT = Context.getAsVariableArrayType(T))
- if (CurContext->isFunctionOrMethod())
+ if (CurContext->isFunctionOrMethod() &&
+ !(getCurScope() && getCurScope()->isFunctionPrototypeScope()))
if (const Expr *Size = VAT->getSizeExpr())
VLASizeExprOwners.try_emplace(Size, CurContext);
diff --git a/clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp b/clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp
index d3d245eed22fdd..7360ad571c6275 100644
--- a/clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp
+++ b/clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp
@@ -17,6 +17,24 @@ int by_copy_t() {
return [] { X x; return (int)sizeof(x); }();
}
+// An explicit cast to such a type captures the bound as well.
+// CHECK-LABEL: define {{.*}} @_Z14cast_in_lambdav(
+// CHECK: store i64 {{%.*}}, ptr {{%.*}}
+// CHECK-LABEL: define internal {{.*}} @"_ZZ14cast_in_lambdavENK3$_0clEv"(
+// CHECK: [[BOUND:%[0-9]+]] = load i64, ptr {{%[0-9]+}}
+// CHECK: mul nuw i64 4, [[BOUND]]
+unsigned long cast_in_lambda() {
+ using P = int (*)[foo()];
+ return [] { return sizeof(((P)0)[1]); }();
+}
+
+// The bound of the lambda's own parameter is not captured.
+// CHECK-LABEL: define {{.*}} @_Z15own_param_boundv(
+// CHECK-LABEL: define internal {{.*}} @"_ZZ15own_param_boundvENK3$_0clEiPA_i"(ptr noundef nonnull align 1 dereferenceable(1) %this,
+int own_param_bound() {
+ return [](int m, int (*p)[m]) { decltype(p) q = p; return (int)sizeof(*q); }(1, nullptr);
+}
+
// CHECK-LABEL: define {{.*}} @_Z8nested_tv(
// CHECK-LABEL: define internal {{.*}} @"_ZZZ8nested_tvENK3$_0clEvENKUlvE_clEv"(
// CHECK: [[BOUND:%[0-9]+]] = load i64, ptr {{%[0-9]+}}
diff --git a/clang/test/Sema/vla-enclosing-function-blocks.c b/clang/test/Sema/vla-enclosing-function-blocks.c
index 7534089d6bf053..fbd7f4ebca7e90 100644
--- a/clang/test/Sema/vla-enclosing-function-blocks.c
+++ b/clang/test/Sema/vla-enclosing-function-blocks.c
@@ -17,6 +17,16 @@ void typedef_in_block(void) {
^{ typedef X Y; }(); // expected-error {{cannot be used in a block}}
}
+void cast_in_block(void) {
+ typedef int (*P)[size_tab()]; // expected-note {{size expression is evaluated here}}
+ ^{ (void)(P)0; }(); // expected-error {{cannot be used in a block}}
+}
+
+// Valid: VLA parameter of the block itself.
+int own_vla_param_in_block(void) {
+ return (^ int(int m, int (*p)[m]) { __typeof(p) q = p; return sizeof(*q); })(1, 0);
+}
+
// Valid: VLA declared inside the block itself.
int own_vla_in_block(void) {
return (^ int(void) { int a[size_tab()]; return sizeof(a); })();
diff --git a/clang/test/SemaCXX/vla-enclosing-function.cpp b/clang/test/SemaCXX/vla-enclosing-function.cpp
index 516a3ee604cd8b..7ce2fcfc91a0f3 100644
--- a/clang/test/SemaCXX/vla-enclosing-function.cpp
+++ b/clang/test/SemaCXX/vla-enclosing-function.cpp
@@ -30,11 +30,28 @@ void pointer_to_vla() {
struct S { unsigned long f(P p) { return sizeof(*p); } }; // expected-error {{cannot be used in a local class}}
}
+void casts() {
+ using P = int (*)[foo()]; // expected-note 3 {{size expression is evaluated here}}
+ struct S {
+ void f() {
+ (void)(P)0; // expected-error {{cannot be used in a local class}}
+ (void)static_cast<P>(nullptr); // expected-error {{cannot be used in a local class}}
+ (void)P(nullptr); // expected-error {{cannot be used in a local class}}
+ }
+ };
+}
+
void typedef_in_member_function() {
using X = int[foo()]; // expected-note {{size expression is evaluated here}}
struct S { void f() { typedef X Y; } }; // expected-error {{cannot be used in a local class}}
}
+// FIXME: Bounds in a function prototype are not recorded, so this is not
+// diagnosed.
+void param_bound(int n, int (*p)[n]) {
+ struct S { void f() { decltype(p) q; } };
+}
+
template <typename T> void in_template() {
using X = int[foo()]; // expected-note {{size expression is evaluated here}}
struct S { S() { X x; } }; // expected-error {{cannot be used in a local class}}
@@ -70,6 +87,10 @@ void param_vla_in_local_class() {
struct S { unsigned long f(int n, int (*p)[n]) { return sizeof(*p); } };
}
+unsigned long lambda_own_vla_param() {
+ return [](int m, int (*p)[m]) { decltype(p) q = p; return sizeof(*q); }(1, nullptr);
+}
+
int lambda_sizeof() {
using X = int[foo()];
return [&] { return (int)sizeof(X); }();
>From 2fbf3098481da28ddf3a920e8430280b8729fc2f Mon Sep 17 00:00:00 2001
From: Ivan Ugryumov <smille2003 at yandex.ru>
Date: Sun, 18 Oct 2026 14:00:13 +0300
Subject: [PATCH 6/6] Check all variably modified sizeof operands, not only
typedefs
---
clang/lib/Sema/SemaExpr.cpp | 3 +--
.../test/CodeGenCXX/vla-lambda-enclosing-function.cpp | 11 +++++++++++
clang/test/Sema/vla-enclosing-function-blocks.c | 7 +++++++
clang/test/SemaCXX/vla-enclosing-function.cpp | 11 +++++++++++
4 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 6311f89baf459a..a87e257566e476 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4948,8 +4948,7 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
ExprKind))
return true;
- if (ExprType->getAs<TypedefType>() &&
- !CheckVariablyModifiedTypeUse(ExprType, OpLoc))
+ if (!CheckVariablyModifiedTypeUse(ExprType, OpLoc))
return true;
return false;
diff --git a/clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp b/clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp
index 7360ad571c6275..3b8043eb3c311d 100644
--- a/clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp
+++ b/clang/test/CodeGenCXX/vla-lambda-enclosing-function.cpp
@@ -28,6 +28,17 @@ unsigned long cast_in_lambda() {
return [] { return sizeof(((P)0)[1]); }();
}
+// So does sizeof of such a type.
+// CHECK-LABEL: define {{.*}} @_Z13sizeof_lambdav(
+// CHECK: store i64 {{%.*}}, ptr {{%.*}}
+// CHECK-LABEL: define internal {{.*}} @"_ZZ13sizeof_lambdavENK3$_0clEv"(
+// CHECK: [[BOUND:%[0-9]+]] = load i64, ptr {{%[0-9]+}}
+// CHECK: mul nuw i64 4, [[BOUND]]
+unsigned long sizeof_lambda() {
+ using X = int[foo()];
+ return [] { return sizeof(X); }();
+}
+
// The bound of the lambda's own parameter is not captured.
// CHECK-LABEL: define {{.*}} @_Z15own_param_boundv(
// CHECK-LABEL: define internal {{.*}} @"_ZZ15own_param_boundvENK3$_0clEiPA_i"(ptr noundef nonnull align 1 dereferenceable(1) %this,
diff --git a/clang/test/Sema/vla-enclosing-function-blocks.c b/clang/test/Sema/vla-enclosing-function-blocks.c
index fbd7f4ebca7e90..16d15fcfac2faa 100644
--- a/clang/test/Sema/vla-enclosing-function-blocks.c
+++ b/clang/test/Sema/vla-enclosing-function-blocks.c
@@ -22,6 +22,13 @@ void cast_in_block(void) {
^{ (void)(P)0; }(); // expected-error {{cannot be used in a block}}
}
+// FIXME: The operand of sizeof is checked in an unevaluated context, so this
+// is not diagnosed.
+void sizeof_in_block(void) {
+ typedef int X[size_tab()];
+ ^{ (void)sizeof(X); }();
+}
+
// Valid: VLA parameter of the block itself.
int own_vla_param_in_block(void) {
return (^ int(int m, int (*p)[m]) { __typeof(p) q = p; return sizeof(*q); })(1, 0);
diff --git a/clang/test/SemaCXX/vla-enclosing-function.cpp b/clang/test/SemaCXX/vla-enclosing-function.cpp
index 7ce2fcfc91a0f3..1375f0ca9a349c 100644
--- a/clang/test/SemaCXX/vla-enclosing-function.cpp
+++ b/clang/test/SemaCXX/vla-enclosing-function.cpp
@@ -52,6 +52,17 @@ void param_bound(int n, int (*p)[n]) {
struct S { void f() { decltype(p) q; } };
}
+// FIXME: The operand of sizeof is checked in an unevaluated context, so this
+// is not diagnosed.
+void sizeof_type() {
+ using X = int[foo()];
+ int a[foo()];
+ struct S {
+ unsigned long f() { return sizeof(X); }
+ unsigned long g() { return sizeof(decltype(a)); }
+ };
+}
+
template <typename T> void in_template() {
using X = int[foo()]; // expected-note {{size expression is evaluated here}}
struct S { S() { X x; } }; // expected-error {{cannot be used in a local class}}
More information about the cfe-commits
mailing list