Skip to content

Diagnose use of VLAs in a coroutine #70341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,10 @@ Bug Fixes to C++ Support
(`#46200 <https://github.com/llvm/llvm-project/issues/46200>`_)
(`#57812 <https://github.com/llvm/llvm-project/issues/57812>`_)

- Diagnose use of a variable-length array in a coroutine. The design of
coroutines is such that it is not possible to support VLA use. Fixes:
(`#65858 <https://github.com/llvm/llvm-project/issues/65858>`_)

- Fix bug where we were overriding zero-initialization of class members when
default initializing a base class in a constant expression context. Fixes:
(`#69890 <https://github.com/llvm/llvm-project/issues/69890>`_)
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ def ext_vla_folded_to_constant : ExtWarn<
InGroup<GNUFoldingConstant>;
def err_vla_unsupported : Error<
"variable length arrays are not supported for %select{the current target|'%1'}0">;
def err_vla_in_coroutine_unsupported : Error<
"variable length arrays in a coroutine are not supported">;
def note_vla_unsupported : Note<
"variable length arrays are not supported for the current target">;

Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Sema/ScopeInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ class FunctionScopeInfo {
/// First SEH '__try' statement in the current function.
SourceLocation FirstSEHTryLoc;

/// First use of a VLA within the current function.
SourceLocation FirstVLALoc;

private:
/// Used to determine if errors occurred in this function or block.
DiagnosticErrorTrap ErrorTrap;
Expand Down Expand Up @@ -473,6 +476,11 @@ class FunctionScopeInfo {
FirstSEHTryLoc = TryLoc;
}

void setHasVLA(SourceLocation VLALoc) {
if (FirstVLALoc.isInvalid())
FirstVLALoc = VLALoc;
}

bool NeedsScopeChecking() const {
return !HasDroppedStmt && (HasIndirectGoto || HasMustTail ||
(HasBranchProtectedScope && HasBranchIntoScope));
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Sema/SemaCoroutine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,11 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) {
if (FD->hasAttr<AlwaysInlineAttr>())
Diag(FD->getLocation(), diag::warn_always_inline_coroutine);

// The design of coroutines means we cannot allow use of VLAs within one, so
// diagnose if we've seen a VLA in the body of this function.
if (Fn->FirstVLALoc.isValid())
Diag(Fn->FirstVLALoc, diag::err_vla_in_coroutine_unsupported);

// [stmt.return.coroutine]p1:
// A coroutine shall not enclose a return statement ([stmt.return]).
if (Fn->FirstReturnLoc.isValid()) {
Expand Down
18 changes: 12 additions & 6 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2706,12 +2706,18 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
}
}

if (T->isVariableArrayType() && !Context.getTargetInfo().isVLASupported()) {
// CUDA device code and some other targets don't support VLAs.
bool IsCUDADevice = (getLangOpts().CUDA && getLangOpts().CUDAIsDevice);
targetDiag(Loc,
IsCUDADevice ? diag::err_cuda_vla : diag::err_vla_unsupported)
<< (IsCUDADevice ? CurrentCUDATarget() : 0);
if (T->isVariableArrayType()) {
if (!Context.getTargetInfo().isVLASupported()) {
// CUDA device code and some other targets don't support VLAs.
bool IsCUDADevice = (getLangOpts().CUDA && getLangOpts().CUDAIsDevice);
targetDiag(Loc,
IsCUDADevice ? diag::err_cuda_vla : diag::err_vla_unsupported)
<< (IsCUDADevice ? CurrentCUDATarget() : 0);
} else if (sema::FunctionScopeInfo *FSI = getCurFunction()) {
// VLAs are supported on this target, but we may need to do delayed
// checking that the VLA is not being used within a coroutine.
FSI->setHasVLA(Loc);
}
}

// If this is not C99, diagnose array size modifiers on non-VLAs.
Expand Down
29 changes: 29 additions & 0 deletions clang/test/SemaCXX/coroutine-vla.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -Wno-vla-cxx-extension -verify
#include "Inputs/std-coroutine.h"

struct promise;

struct coroutine : std::coroutine_handle<promise> {
using promise_type = ::promise;
};

struct promise
{
coroutine get_return_object();
std::suspend_always initial_suspend() noexcept;
std::suspend_always final_suspend() noexcept;
void return_void();
void unhandled_exception();
};

coroutine foo(int n) {
int array[n]; // expected-error {{variable length arrays in a coroutine are not supported}}
co_return;
}

void lambda() {
[](int n) -> coroutine {
int array[n]; // expected-error {{variable length arrays in a coroutine are not supported}}
co_return;
}(10);
}