diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3bbab951e8a8cc..de038fe2d13ecd 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -452,6 +452,8 @@ Improvements to Clang's diagnostics - Add ``-Wreturn-local-addr``, a GCC alias for ``-Wreturn-stack-address``. - Clang now suppresses ``-Wlogical-op-parentheses`` on ``(x && a || b)`` and ``(a || b && x)`` only when ``x`` is a string literal. +- Clang will now reject the GNU extension address of label in coroutines explicitly. + This fixes `Issue 56436 `_. Non-comprehensive list of changes in this release ------------------------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 02afb098b23950..50050e1885ae8c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11360,6 +11360,9 @@ def warn_non_aligned_allocation_function : Warning < def err_conflicting_aligned_options : Error < "conflicting option '-fcoro-aligned-allocation' and '-fno-aligned-allocation'" >; +def err_coro_invalid_addr_of_label : Error< + "the GNU address of label extension is not allowed in coroutines." +>; } // end of coroutines issue category let CategoryName = "Documentation Issue" in { diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 363fbf454879d5..65fa18fbb2903f 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -233,6 +233,9 @@ class FunctionScopeInfo { /// modified in the function. llvm::SmallPtrSet ModifiedNonNullParams; + /// The set of GNU address of label extension "&&label". + llvm::SmallVector AddrLabels; + public: /// Represents a simple identification of a weak object. /// diff --git a/clang/lib/Sema/ScopeInfo.cpp b/clang/lib/Sema/ScopeInfo.cpp index f4dda9f194e785..e313052b3ab38c 100644 --- a/clang/lib/Sema/ScopeInfo.cpp +++ b/clang/lib/Sema/ScopeInfo.cpp @@ -56,6 +56,7 @@ void FunctionScopeInfo::Clear() { ModifiedNonNullParams.clear(); Blocks.clear(); ByrefBlockVars.clear(); + AddrLabels.clear(); } static const NamedDecl *getBestPropertyDecl(const ObjCPropertyRefExpr *PropE) { diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index 4cedd53a76785a..79c08adb8fabc1 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -1125,6 +1125,12 @@ void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) { Diag(Fn->FirstCoroutineStmtLoc, diag::note_declared_coroutine_here) << Fn->getFirstCoroutineStmtKeyword(); } + + // Coroutines will get splitted into pieces. The GNU address of label + // extension wouldn't be meaningful in coroutines. + for (AddrLabelExpr *ALE : Fn->AddrLabels) + Diag(ALE->getBeginLoc(), diag::err_coro_invalid_addr_of_label); + CoroutineStmtBuilder Builder(*this, *FD, *Fn, Body); if (Builder.isInvalid() || !Builder.buildStatements()) return FD->setInvalidDecl(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index d90c28432a6b67..be3a6bd321edc4 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -16105,8 +16105,13 @@ ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, SourceLocation LabLoc, LabelDecl *TheDecl) { TheDecl->markUsed(Context); // Create the AST node. The address of a label always has type 'void*'. - return new (Context) AddrLabelExpr(OpLoc, LabLoc, TheDecl, - Context.getPointerType(Context.VoidTy)); + auto *Res = new (Context) AddrLabelExpr( + OpLoc, LabLoc, TheDecl, Context.getPointerType(Context.VoidTy)); + + if (getCurFunction()) + getCurFunction()->AddrLabels.push_back(Res); + + return Res; } void Sema::ActOnStartStmtExpr() { diff --git a/clang/test/SemaCXX/addr-label-in-coroutines.cpp b/clang/test/SemaCXX/addr-label-in-coroutines.cpp new file mode 100644 index 00000000000000..e37ee641343788 --- /dev/null +++ b/clang/test/SemaCXX/addr-label-in-coroutines.cpp @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s + +#include "Inputs/std-coroutine.h" + +struct resumable { + struct promise_type { + resumable get_return_object() { return {}; } + auto initial_suspend() { return std::suspend_always(); } + auto final_suspend() noexcept { return std::suspend_always(); } + void unhandled_exception() {} + void return_void(){}; + }; +}; + +resumable f1(int &out, int *inst) { + static void* dispatch_table[] = {&&inc, // expected-error {{the GNU address of label extension is not allowed in coroutines.}} + &&suspend, // expected-error {{the GNU address of label extension is not allowed in coroutines.}} + &&stop}; // expected-error {{the GNU address of label extension is not allowed in coroutines.}} + #define DISPATCH() goto *dispatch_table[*inst++] +inc: + out++; + DISPATCH(); + +suspend: + co_await std::suspend_always{}; + DISPATCH(); + +stop: + co_return; +} + +resumable f2(int &out, int *inst) { + void* dispatch_table[] = {nullptr, nullptr, nullptr}; + dispatch_table[0] = &&inc; // expected-error {{the GNU address of label extension is not allowed in coroutines.}} + dispatch_table[1] = &&suspend; // expected-error {{the GNU address of label extension is not allowed in coroutines.}} + dispatch_table[2] = &&stop; // expected-error {{the GNU address of label extension is not allowed in coroutines.}} + #define DISPATCH() goto *dispatch_table[*inst++] +inc: + out++; + DISPATCH(); + +suspend: + co_await std::suspend_always{}; + DISPATCH(); + +stop: + co_return; +} + +resumable f3(int &out, int *inst) { + void* dispatch_table[] = {nullptr, nullptr, nullptr}; + [&]() -> resumable { + dispatch_table[0] = &&inc; // expected-error {{the GNU address of label extension is not allowed in coroutines.}} + dispatch_table[1] = &&suspend; // expected-error {{the GNU address of label extension is not allowed in coroutines.}} + dispatch_table[2] = &&stop; // expected-error {{the GNU address of label extension is not allowed in coroutines.}} + #define DISPATCH() goto *dispatch_table[*inst++] + inc: + out++; + DISPATCH(); + + suspend: + co_await std::suspend_always{}; + DISPATCH(); + + stop: + co_return; + }(); + + co_return; +}