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 <[email protected]>
Date: Wed, 7 Oct 2026 11:17:49 +0300
Subject: [PATCH 1/2] [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 <[email protected]>
Date: Thu, 8 Oct 2026 09:57:42 +0300
Subject: [PATCH 2/2] 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()];

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to