[clang] [Clang] support storage-class specifiers in C23 compound literals (PR #212559)
Oleksandr Tarasiuk via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 29 03:26:03 PDT 2026
https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/212559
>From 0c41fd3fcbbdd487201d26a0fbadbebf24c4b669 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Tue, 28 Jul 2026 17:14:33 +0300
Subject: [PATCH 1/4] [Clang] support storage-class specifiers in C23 compound
literals
---
clang/docs/ReleaseNotes.md | 2 +
clang/include/clang/AST/Expr.h | 66 +++-
clang/include/clang/AST/Stmt.h | 17 +
.../clang/Basic/DiagnosticParseKinds.td | 4 +
.../clang/Basic/DiagnosticSemaKinds.td | 18 +-
clang/include/clang/Parse/Parser.h | 6 +-
clang/include/clang/Sema/DelayedDiagnostic.h | 15 +-
clang/include/clang/Sema/Initialization.h | 24 +-
clang/include/clang/Sema/Scope.h | 6 +-
clang/include/clang/Sema/Sema.h | 24 +-
clang/lib/AST/ASTImporter.cpp | 6 +-
clang/lib/AST/Expr.cpp | 9 +-
clang/lib/AST/ExprConstant.cpp | 93 +++--
clang/lib/AST/StmtPrinter.cpp | 24 +-
clang/lib/AST/StmtProfile.cpp | 3 +
clang/lib/CodeGen/CGExpr.cpp | 17 +-
clang/lib/CodeGen/CGExprAgg.cpp | 7 +-
clang/lib/CodeGen/CGExprConstant.cpp | 10 +-
clang/lib/CodeGen/CodeGenFunction.cpp | 4 +-
clang/lib/Parse/ParseExpr.cpp | 98 ++++-
clang/lib/Sema/CheckExprLifetime.cpp | 3 +-
clang/lib/Sema/DelayedDiagnostic.cpp | 1 +
clang/lib/Sema/Scope.cpp | 15 +-
clang/lib/Sema/SemaDecl.cpp | 39 +-
clang/lib/Sema/SemaDeclAttr.cpp | 8 +
clang/lib/Sema/SemaExpr.cpp | 370 ++++++++++++------
clang/lib/Sema/SemaInit.cpp | 45 ++-
clang/lib/Sema/TreeTransform.h | 18 +-
clang/lib/Serialization/ASTReaderStmt.cpp | 3 +
clang/lib/Serialization/ASTWriterStmt.cpp | 3 +
clang/test/AST/c23-compound-literal-print.c | 33 ++
clang/test/C/drs/dr3xx.c | 1 +
clang/test/CodeGen/c23-compound-literal.c | 275 +++++++++++++
clang/test/CodeGenObjC/c23-compound-literal.m | 19 +
clang/test/Parser/expressions.c | 18 +-
clang/test/Sema/c23-compound-literal.c | 343 ++++++++++++++++
clang/test/Sema/c23-compound-literal.m | 11 +
clang/test/Sema/constexpr.c | 2 +-
38 files changed, 1418 insertions(+), 242 deletions(-)
create mode 100644 clang/test/AST/c23-compound-literal-print.c
create mode 100644 clang/test/CodeGen/c23-compound-literal.c
create mode 100644 clang/test/CodeGenObjC/c23-compound-literal.m
create mode 100644 clang/test/Sema/c23-compound-literal.c
create mode 100644 clang/test/Sema/c23-compound-literal.m
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index c348ddaf23017..bd7689b2fc3c3 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -142,6 +142,8 @@ features cannot lower the translation-unit ABI level;
#### C23 Feature Support
+- Clang now supports storage-class specifiers in compound literals. (#GH129365)
+
### Objective-C Language Changes
### Non-comprehensive list of changes in this release
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index f95f87cc4e8e0..6454a750b61c9 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -765,6 +765,8 @@ class Expr : public ValueStmt {
/// evaluation is not part of the evaluation, but all other temporaries
/// are destroyed.
ImmediateInvocation,
+ /// The initializer of a compound literal constant.
+ CompoundLiteralInitializer,
};
/// Evaluate an expression that is required to be a constant expression. Does
@@ -3606,7 +3608,7 @@ class MemberExpr final
}
};
-/// CompoundLiteralExpr - [C99 6.5.2.5]
+/// CompoundLiteralExpr - [C99 6.5.2.5, C23 6.5.3.6]
///
class CompoundLiteralExpr : public Expr {
/// LParenLoc - If non-null, this is the location of the left paren in a
@@ -3624,11 +3626,20 @@ class CompoundLiteralExpr : public Expr {
mutable APValue *StaticValue = nullptr;
public:
- CompoundLiteralExpr(SourceLocation lparenloc, TypeSourceInfo *tinfo,
- QualType T, ExprValueKind VK, Expr *init, bool fileScope)
+ CompoundLiteralExpr(
+ SourceLocation LParenLoc, TypeSourceInfo *TInfo, QualType T,
+ ExprValueKind VK, Expr *Init, bool FileScope, StorageClass SC = SC_None,
+ ThreadStorageClassSpecifier TSC = TSCS_unspecified,
+ ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified)
: Expr(CompoundLiteralExprClass, T, VK, OK_Ordinary),
- LParenLoc(lparenloc), TInfoAndScope(tinfo, fileScope), Init(init) {
+ LParenLoc(LParenLoc), TInfoAndScope(TInfo, FileScope), Init(Init) {
assert(Init && "Init is a nullptr");
+ assert((ConstexprKind == ConstexprSpecKind::Unspecified ||
+ ConstexprKind == ConstexprSpecKind::Constexpr) &&
+ "invalid compound literal constexpr specifier");
+ setStorageClass(SC);
+ setTSCSpec(TSC);
+ setConstexpr(ConstexprKind == ConstexprSpecKind::Constexpr);
setDependence(computeDependence(this));
}
@@ -3649,11 +3660,52 @@ class CompoundLiteralExpr : public Expr {
TypeSourceInfo *getTypeSourceInfo() const {
return TInfoAndScope.getPointer();
}
- void setTypeSourceInfo(TypeSourceInfo *tinfo) {
- TInfoAndScope.setPointer(tinfo);
+
+ void setTypeSourceInfo(TypeSourceInfo *TInfo) {
+ TInfoAndScope.setPointer(TInfo);
+ }
+
+ bool hasStaticStorage() const {
+ return isGLValue() && !hasThreadStorage() &&
+ (isFileScope() || getStorageClass() == SC_Static);
+ }
+
+ bool hasThreadStorage() const { return getTSCSpec() != TSCS_unspecified; }
+
+ bool hasGlobalStorage() const {
+ return hasStaticStorage() || hasThreadStorage();
+ }
+
+ StorageClass getStorageClass() const {
+ return static_cast<StorageClass>(CompoundLiteralExprBits.SClass);
+ }
+
+ void setStorageClass(StorageClass SC) {
+ assert((SC == SC_None || SC == SC_Static || SC == SC_Register) &&
+ "invalid compound literal storage class");
+ CompoundLiteralExprBits.SClass = SC;
+ assert(getStorageClass() == SC && "truncation");
+ }
+
+ ThreadStorageClassSpecifier getTSCSpec() const {
+ return static_cast<ThreadStorageClassSpecifier>(
+ CompoundLiteralExprBits.TSCSpec);
+ }
+
+ void setTSCSpec(ThreadStorageClassSpecifier TSC) {
+ assert((TSC == TSCS_unspecified || TSC == TSCS_thread_local ||
+ TSC == TSCS__Thread_local) &&
+ "invalid compound literal thread storage class");
+ CompoundLiteralExprBits.TSCSpec = TSC;
+ assert(getTSCSpec() == TSC && "truncation");
+ }
+
+ bool isConstexpr() const { return CompoundLiteralExprBits.IsConstexpr; }
+
+ void setConstexpr(bool IsConstexpr) {
+ CompoundLiteralExprBits.IsConstexpr = IsConstexpr;
}
- bool hasStaticStorage() const { return isFileScope() && isGLValue(); }
APValue &getOrCreateStaticValue(ASTContext &Ctx) const;
APValue &getStaticValue() const;
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 69db8252f931e..791def70e72fd 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -668,6 +668,22 @@ class alignas(void *) Stmt {
SourceLocation OpLoc;
};
+ class CompoundLiteralExprBitfields {
+ friend class CompoundLiteralExpr;
+
+ LLVM_PREFERRED_TYPE(ExprBitfields)
+ unsigned : NumExprBits;
+
+ LLVM_PREFERRED_TYPE(StorageClass)
+ unsigned SClass : 3;
+
+ LLVM_PREFERRED_TYPE(ThreadStorageClassSpecifier)
+ unsigned TSCSpec : 2;
+
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned IsConstexpr : 1;
+ };
+
class InitListExprBitfields {
friend class ASTStmtReader;
friend class InitListExpr;
@@ -1363,6 +1379,7 @@ class alignas(void *) Stmt {
MemberExprBitfields MemberExprBits;
CastExprBitfields CastExprBits;
BinaryOperatorBitfields BinaryOperatorBits;
+ CompoundLiteralExprBitfields CompoundLiteralExprBits;
InitListExprBitfields InitListExprBits;
ParenListExprBitfields ParenListExprBits;
GenericSelectionExprBitfields GenericSelectionExprBits;
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 2672bfb2952c8..17ba28c5c094c 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -185,6 +185,10 @@ def warn_c11_compat_keyword : Warning<
def warn_c23_compat_keyword : Warning<
"'%0' is incompatible with C standards before C23">,
InGroup<CPre23Compat>, DefaultIgnore;
+def warn_c23_compat_compound_literal_storage_class : Warning<
+ "compound literal storage-class specifiers are incompatible with C "
+ "standards before C23">,
+ InGroup<CPre23Compat>, DefaultIgnore;
def warn_c2y_compat_keyword : Warning<
"'%0' is incompatible with C standards before C2y">,
InGroup<CPre2yCompat>, DefaultIgnore;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index cce6f70a58893..85528249c4be4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2471,7 +2471,8 @@ def err_init_conversion_failed : Error<
"base class|a constructor delegation|a vector element|a matrix element|a "
"block element|a block element|a complex element|a lambda capture|a compound"
" literal initializer|a related result|a parameter of CF audited function|a "
- "structured binding|a member subobject}0 "
+ "structured binding|a member subobject|a constexpr compound literal "
+ "initializer}0 "
"%diff{of type $ with an %select{rvalue|lvalue}2 of type $|"
"with an %select{rvalue|lvalue}2 of incompatible type}1,3"
"%select{|: different classes%diff{ ($ vs $)|}5,6"
@@ -3177,6 +3178,15 @@ def err_c23_constexpr_init_type_mismatch : Error<
"constexpr initializer for type %0 is of type %1">;
def err_c23_constexpr_pointer_not_null : Error<
"constexpr pointer initializer is not null">;
+def err_register_compound_literal_file_scope : Error<
+ "file-scope compound literal specifies 'register'">;
+def err_thread_local_compound_literal_without_static : Error<
+ "compound literal with 'thread_local' storage duration at block scope must "
+ "also specify 'static'">;
+def err_constexpr_compound_literal_invalid_type : Error<
+ "constexpr compound literal cannot have type %0">;
+def err_compound_literal_initializer_not_constant : Error<
+ "initializer of compound literal must be a constant expression">;
// C++ Concepts
def err_concept_decls_may_only_appear_in_global_namespace_scope : Error<
@@ -3761,6 +3771,9 @@ def err_aix_attr_unsupported : Error<"%0 attribute is not yet supported on AIX">
def err_tls_var_aligned_over_maximum : Error<
"alignment (%0) of thread-local variable %1 is greater than the maximum supported "
"alignment (%2) for a thread-local variable on this target">;
+def err_tls_compound_literal_aligned_over_maximum : Error<
+ "alignment (%0) of thread-local compound literal is greater than the maximum "
+ "supported alignment (%1) for thread-local storage on this target">;
def err_only_annotate_after_access_spec : Error<
"access specifier can only have annotation attributes">;
@@ -7950,7 +7963,8 @@ def err_typecheck_sclass_func : Error<"illegal storage class on function">;
def err_static_block_func : Error<
"function declared in block scope cannot have 'static' storage class">;
def err_typecheck_address_of : Error<"address of %select{bit-field"
- "|vector element|property expression|register variable|matrix element}0 requested">;
+ "|vector element|property expression|register variable|matrix element"
+ "|register compound literal}0 requested">;
def ext_typecheck_addrof_void : Extension<
"ISO C forbids taking the address of an expression of type 'void'">;
def err_unqualified_pointer_member_function : Error<
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 163aa483a84e3..43c88344fbb2e 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -4294,7 +4294,8 @@ class Parser : public CodeCompletionHandler {
/// \endverbatim
ExprResult ParseCompoundLiteralExpression(ParsedType Ty,
SourceLocation LParenLoc,
- SourceLocation RParenLoc);
+ SourceLocation RParenLoc,
+ const DeclSpec *DS = nullptr);
/// ParseGenericSelectionExpression - Parse a C11 generic-selection
/// [C11 6.5.1.1].
@@ -5091,6 +5092,9 @@ class Parser : public CodeCompletionHandler {
return isTypeIdInParens(isAmbiguous);
}
+ bool isCompoundLiteralStorageClassSpecifier(const Token &Tok) const;
+ void ParseCompoundLiteralStorageClassSpecifiers(DeclSpec &DS);
+
/// Finish parsing a C++ unqualified-id that is a template-id of
/// some form.
///
diff --git a/clang/include/clang/Sema/DelayedDiagnostic.h b/clang/include/clang/Sema/DelayedDiagnostic.h
index 0105089a393f1..914fb2d6bc370 100644
--- a/clang/include/clang/Sema/DelayedDiagnostic.h
+++ b/clang/include/clang/Sema/DelayedDiagnostic.h
@@ -125,7 +125,12 @@ class AccessedEntity {
/// the complete parsing of the current declaration.
class DelayedDiagnostic {
public:
- enum DDKind : unsigned char { Availability, Access, ForbiddenType };
+ enum DDKind : unsigned char {
+ Availability,
+ Access,
+ ForbiddenType,
+ ForbiddenStatic
+ };
DDKind Kind;
bool Triggered;
@@ -167,6 +172,14 @@ class DelayedDiagnostic {
return DD;
}
+ static DelayedDiagnostic makeForbiddenStatic(SourceLocation Loc) {
+ DelayedDiagnostic DD;
+ DD.Kind = ForbiddenStatic;
+ DD.Triggered = false;
+ DD.Loc = Loc;
+ return DD;
+ }
+
AccessedEntity &getAccessData() {
assert(Kind == Access && "Not an access diagnostic.");
return *reinterpret_cast<AccessedEntity*>(AccessData);
diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h
index 2c2df1f03747b..b338151cded83 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -131,6 +131,9 @@ class alignas(8) InitializedEntity {
/// object initialized via parenthesized aggregate initialization.
EK_ParenAggInitMember,
+ /// The entity initialized by a constexpr compound literal.
+ EK_ConstexprCompoundLiteralInit,
+
// Note: err_init_conversion_failed in DiagnosticSemaKinds.td uses this
// enum as an index for its first %select. When modifying this list,
// that diagnostic text needs to be updated as well.
@@ -216,8 +219,8 @@ class alignas(8) InitializedEntity {
/// integer indicating whether the parameter is "consumed".
llvm::PointerIntPair<ParmVarDecl *, 1> Parameter;
- /// When Kind == EK_Temporary or EK_CompoundLiteralInit, the type
- /// source information for the temporary.
+ /// When Kind is EK_Temporary, EK_CompoundLiteralInit, or
+ /// EK_ConstexprCompoundLiteralInit, the type source information.
TypeSourceInfo *TypeInfo;
struct LN LocAndNRVO;
@@ -465,13 +468,21 @@ class alignas(8) InitializedEntity {
}
/// Create the entity for a compound literal initializer.
- static InitializedEntity InitializeCompoundLiteralInit(TypeSourceInfo *TSI) {
- InitializedEntity Result(EK_CompoundLiteralInit, SourceLocation(),
- TSI->getType());
+ static InitializedEntity
+ InitializeCompoundLiteralInit(TypeSourceInfo *TSI, QualType Type,
+ ConstexprSpecKind ConstexprKind) {
+ InitializedEntity Result(ConstexprKind == ConstexprSpecKind::Constexpr
+ ? EK_ConstexprCompoundLiteralInit
+ : EK_CompoundLiteralInit,
+ SourceLocation(), Type);
Result.TypeInfo = TSI;
return Result;
}
+ bool isConstexprCompoundLiteral() const {
+ return Kind == EK_ConstexprCompoundLiteralInit;
+ }
+
/// Determine the kind of initialization.
EntityKind getKind() const { return Kind; }
@@ -486,7 +497,8 @@ class alignas(8) InitializedEntity {
/// Retrieve complete type-source information for the object being
/// constructed, if known.
TypeSourceInfo *getTypeSourceInfo() const {
- if (Kind == EK_Temporary || Kind == EK_CompoundLiteralInit)
+ if (Kind == EK_Temporary || Kind == EK_CompoundLiteralInit ||
+ Kind == EK_ConstexprCompoundLiteralInit)
return TypeInfo;
return nullptr;
diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h
index 58ca2c0738f3c..4f69a18fb72a5 100644
--- a/clang/include/clang/Sema/Scope.h
+++ b/clang/include/clang/Sema/Scope.h
@@ -632,8 +632,10 @@ class Scope {
/// loops where the condition follows the loop body.
void LeaveLoopBody();
- /// containedInPrototypeScope - Return true if this or a parent scope
- /// is a FunctionPrototypeScope.
+ /// Return the innermost function prototype scope containing this scope.
+ const Scope *getEnclosingFunctionPrototypeScope() const;
+
+ /// Return true if this or a parent scope is a function prototype scope.
bool containedInPrototypeScope() const;
void PushUsingDirective(UsingDirectiveDecl *UDir) {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d46edeb0d2872..45955c40756be 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4051,6 +4051,8 @@ class Sema final : public SemaBase {
/// Returns true if the variable declaration is a redeclaration.
bool CheckVariableDeclaration(VarDecl *NewVD, LookupResult &Previous);
void CheckVariableDeclarationType(VarDecl *NewVD);
+ bool CheckConstexprType(SourceLocation Loc, QualType T, unsigned DiagID);
+ void DiagnoseStaticInInline(SourceLocation Loc, const FunctionDecl *FD);
void CheckCompleteVariableDeclaration(VarDecl *VD);
NamedDecl *ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
@@ -7595,13 +7597,23 @@ class Sema final : public SemaBase {
/// the ParenListExpr into a sequence of comma binary operators.
ExprResult MaybeConvertParenListExprToParenExpr(Scope *S, Expr *ME);
- ExprResult ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty,
- SourceLocation RParenLoc, Expr *InitExpr);
+private:
+ ExprResult BuildCompoundLiteralExpr(
+ SourceLocation LParenLoc, TypeSourceInfo *TInfo, SourceLocation RParenLoc,
+ Expr *LiteralExpr, StorageClass SC, SourceLocation StorageClassLoc,
+ ThreadStorageClassSpecifier TSC, SourceLocation ThreadStorageClassLoc,
+ ConstexprSpecKind ConstexprKind, SourceLocation ConstexprLoc);
- ExprResult BuildCompoundLiteralExpr(SourceLocation LParenLoc,
- TypeSourceInfo *TInfo,
- SourceLocation RParenLoc,
- Expr *LiteralExpr);
+public:
+ ExprResult ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty,
+ SourceLocation RParenLoc, Expr *InitExpr,
+ const DeclSpec *DS = nullptr);
+
+ ExprResult BuildCompoundLiteralExpr(
+ SourceLocation LParenLoc, TypeSourceInfo *TInfo, SourceLocation RParenLoc,
+ Expr *LiteralExpr, StorageClass SC = SC_None,
+ ThreadStorageClassSpecifier TSC = TSCS_unspecified,
+ ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified);
ExprResult ActOnInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList,
SourceLocation RBraceLoc);
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 3ad71a223903c..97261a632390b 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -8100,8 +8100,10 @@ ExpectedStmt ASTNodeImporter::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
return std::move(Err);
return new (Importer.getToContext()) CompoundLiteralExpr(
- ToLParenLoc, ToTypeSourceInfo, ToType, E->getValueKind(),
- ToInitializer, E->isFileScope());
+ ToLParenLoc, ToTypeSourceInfo, ToType, E->getValueKind(), ToInitializer,
+ E->isFileScope(), E->getStorageClass(), E->getTSCSpec(),
+ E->isConstexpr() ? ConstexprSpecKind::Constexpr
+ : ConstexprSpecKind::Unspecified);
}
ExpectedStmt ASTNodeImporter::VisitAtomicExpr(AtomicExpr *E) {
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 9a9a76e265f6a..de6aaf503f767 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3416,8 +3416,13 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef,
// This handles gcc's extension that allows global initializers like
// "struct x {int x;} x = (struct x) {};".
// FIXME: This accepts other cases it shouldn't!
- const Expr *Exp = cast<CompoundLiteralExpr>(this)->getInitializer();
- return Exp->isConstantInitializer(Ctx, false, Culprit);
+ const auto *CLE = cast<CompoundLiteralExpr>(this);
+ if (CLE->hasThreadStorage()) {
+ if (Culprit)
+ *Culprit = this;
+ return false;
+ }
+ return CLE->getInitializer()->isConstantInitializer(Ctx, false, Culprit);
}
case DesignatedInitUpdateExprClass: {
const DesignatedInitUpdateExpr *DIUE = cast<DesignatedInitUpdateExpr>(this);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index a3385d3b7318f..c5d31c96c980f 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -157,6 +157,7 @@ namespace {
case ConstantExprKind::Normal:
case ConstantExprKind::ClassTemplateArgument:
case ConstantExprKind::ImmediateInvocation:
+ case ConstantExprKind::CompoundLiteralInitializer:
// Note that non-type template arguments of class type are emitted as
// template parameter objects.
return false;
@@ -171,6 +172,7 @@ namespace {
switch (Kind) {
case ConstantExprKind::Normal:
case ConstantExprKind::ImmediateInvocation:
+ case ConstantExprKind::CompoundLiteralInitializer:
return false;
case ConstantExprKind::ClassTemplateArgument:
@@ -813,6 +815,8 @@ namespace {
/// not supported by the interpreter, an error is triggered.
bool EnableNewConstInterp;
+ ConstantExprKind ConstantKind;
+
/// BottomFrame - The frame in which evaluation started. This must be
/// initialized after CurrentCall and CallStackDepth.
CallStackFrame BottomFrame;
@@ -913,11 +917,13 @@ namespace {
/// initialization.
uint64_t ArrayInitIndex = -1;
- EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode)
+ EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode,
+ ConstantExprKind Kind = ConstantExprKind::Normal)
: State(const_cast<ASTContext &>(C), S), CurrentCall(nullptr),
CallStackDepth(0), NextCallIndex(1),
StepsLeft(C.getLangOpts().ConstexprStepLimit),
EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp),
+ ConstantKind(Kind),
BottomFrame(*this, SourceLocation(), /*Callee=*/nullptr,
/*This=*/nullptr,
/*CallExpr=*/nullptr, CallRef()),
@@ -1980,7 +1986,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
return false;
case Expr::CompoundLiteralExprClass: {
const CompoundLiteralExpr *CLE = cast<CompoundLiteralExpr>(E);
- return CLE->isFileScope() && CLE->isLValue();
+ return CLE->hasStaticStorage() && CLE->isLValue();
}
case Expr::MaterializeTemporaryExprClass:
// A materialized temporary might have been lifetime-extended to static
@@ -4653,6 +4659,11 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
}
bool IsAccess = isAnyAccess(AK);
+ bool IsConstexprInitializer =
+ Info.ConstantKind == ConstantExprKind::CompoundLiteralInitializer;
+ if (const auto *VD = dyn_cast_if_present<VarDecl>(
+ Info.EvaluatingDecl.dyn_cast<const ValueDecl *>()))
+ IsConstexprInitializer |= VD->isConstexpr();
// C++11 DR1311: An lvalue-to-rvalue conversion on a volatile-qualified type
// is not a constant expression (even if the object is non-volatile). We also
@@ -4731,11 +4742,6 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
}
bool IsConstant = BaseType.isConstant(Info.Ctx);
- bool ConstexprVar = false;
- if (const auto *VD = dyn_cast_if_present<VarDecl>(
- Info.EvaluatingDecl.dyn_cast<const ValueDecl *>()))
- ConstexprVar = VD->isConstexpr();
-
// Unless we're looking at a local variable or argument in a constexpr call,
// the variable we're reading must be const (unless we are binding to a
// reference).
@@ -4755,7 +4761,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
return CompleteObject();
} else if (VD->isConstexpr()) {
// OK, we can read this variable.
- } else if (Info.getLangOpts().C23 && ConstexprVar) {
+ } else if (Info.getLangOpts().C23 && IsConstexprInitializer) {
Info.FFDiag(E);
return CompleteObject();
} else if (BaseType->isIntegralOrEnumerationType()) {
@@ -4879,6 +4885,14 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
assert(BaseVal && "got reference to unevaluated temporary");
} else if (const CompoundLiteralExpr *CLE =
dyn_cast_or_null<CompoundLiteralExpr>(Base)) {
+ if (CLE->hasThreadStorage()) {
+ if (IsAccess) {
+ Info.FFDiag(E);
+ return CompleteObject();
+ }
+ return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
+ }
+
// According to GCC info page:
//
// 6.28 Compound Literals
@@ -9754,6 +9768,11 @@ bool
LValueExprEvaluator::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
assert((!Info.getLangOpts().CPlusPlus || E->isFileScope()) &&
"lvalue compound literal in c++?");
+ if (E->hasThreadStorage()) {
+ Info.FFDiag(E);
+ return false;
+ }
+
APValue *Lit;
// If CompountLiteral has static storage, its value can be used outside
// this expression. So evaluate it once and store it in ASTContext.
@@ -21977,13 +21996,14 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx,
assert(!isValueDependent() &&
"Expression evaluator can't be called on a dependent expression.");
bool IsConst;
- if (FastEvaluateAsRValue(this, Result.Val, Ctx, IsConst) &&
+ if (Kind != ConstantExprKind::CompoundLiteralInitializer &&
+ FastEvaluateAsRValue(this, Result.Val, Ctx, IsConst) &&
Result.Val.hasValue())
return true;
ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsConstantExpr");
EvaluationMode EM = EvaluationMode::ConstantExpression;
- EvalInfo Info(Ctx, Result, EM);
+ EvalInfo Info(Ctx, Result, EM, Kind);
Info.InConstantContext = true;
if (Info.EnableNewConstInterp) {
@@ -22278,6 +22298,27 @@ static ICEDiag CheckEvalInICE(const Expr* E, const ASTContext &Ctx) {
return NoDiag();
}
+static const Expr *getMemberAccess(const Expr *E) {
+ E = E->IgnoreParenImpCasts();
+ while (const auto *ME = dyn_cast<MemberExpr>(E)) {
+ if (ME->isArrow())
+ return nullptr;
+ E = ME->getBase()->IgnoreParenImpCasts();
+ }
+ return E;
+}
+
+static bool isConstexprNamedConstant(const Expr *E) {
+ const auto *DRE = dyn_cast_or_null<DeclRefExpr>(getMemberAccess(E));
+ const auto *VD = DRE ? dyn_cast<VarDecl>(DRE->getDecl()) : nullptr;
+ return VD && VD->isConstexpr();
+}
+
+static bool isCompoundLiteralConstant(const Expr *E) {
+ const auto *CLE = dyn_cast_or_null<CompoundLiteralExpr>(getMemberAccess(E));
+ return CLE && CLE->isConstexpr();
+}
+
static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
assert(!E->isValueDependent() && "Should not see value dependent exprs!");
if (!E->getType()->isIntegralOrEnumerationType())
@@ -22288,6 +22329,13 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
#define STMT(Node, Base) case Expr::Node##Class:
#define EXPR(Node, Base)
#include "clang/AST/StmtNodes.inc"
+ case Expr::CompoundLiteralExprClass: {
+ const CompoundLiteralExpr *CLE = cast<CompoundLiteralExpr>(E);
+ if (CLE->isConstexpr())
+ return CheckEvalInICE(E, Ctx);
+ return ICEDiag(IK_NotICE, E->getBeginLoc());
+ }
+
case Expr::PredefinedExprClass:
case Expr::FloatingLiteralClass:
case Expr::ImaginaryLiteralClass:
@@ -22299,7 +22347,6 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case Expr::OMPArrayShapingExprClass:
case Expr::OMPIteratorExprClass:
case Expr::CompoundAssignOperatorClass:
- case Expr::CompoundLiteralExprClass:
case Expr::ExtVectorElementExprClass:
case Expr::MatrixElementExprClass:
case Expr::DesignatedInitExprClass:
@@ -22377,20 +22424,9 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
return ICEDiag(IK_NotICE, E->getBeginLoc());
case Expr::MemberExprClass: {
- if (Ctx.getLangOpts().C23) {
- const Expr *ME = E->IgnoreParenImpCasts();
- while (const auto *M = dyn_cast<MemberExpr>(ME)) {
- if (M->isArrow())
- return ICEDiag(IK_NotICE, E->getBeginLoc());
- ME = M->getBase()->IgnoreParenImpCasts();
- }
- const auto *DRE = dyn_cast<DeclRefExpr>(ME);
- if (DRE) {
- if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
- VD && VD->isConstexpr())
- return CheckEvalInICE(E, Ctx);
- }
- }
+ if (Ctx.getLangOpts().C23 &&
+ (isConstexprNamedConstant(E) || isCompoundLiteralConstant(E)))
+ return CheckEvalInICE(E, Ctx);
return ICEDiag(IK_NotICE, E->getBeginLoc());
}
@@ -22629,8 +22665,8 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case Expr::ObjCBridgedCastExprClass: {
const Expr *SubExpr = cast<CastExpr>(E)->getSubExpr();
if (isa<ExplicitCastExpr>(E)) {
- if (const FloatingLiteral *FL
- = dyn_cast<FloatingLiteral>(SubExpr->IgnoreParenImpCasts())) {
+ const Expr *Sub = SubExpr->IgnoreParenImpCasts();
+ if (const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(Sub)) {
unsigned DestWidth = Ctx.getIntWidth(E->getType());
bool DestSigned = E->getType()->isSignedIntegerOrEnumerationType();
APSInt IgnoredVal(DestWidth, !DestSigned);
@@ -22644,6 +22680,9 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
return ICEDiag(IK_NotICE, E->getBeginLoc());
return NoDiag();
}
+ if (Ctx.getLangOpts().C23 && Sub->getType()->isArithmeticType() &&
+ (isConstexprNamedConstant(Sub) || isCompoundLiteralConstant(Sub)))
+ return CheckEvalInICE(E, Ctx);
}
switch (cast<CastExpr>(E)->getCastKind()) {
case CK_LValueToRValue:
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 877191d456b35..5bc65c6a76cf2 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1909,7 +1909,29 @@ void StmtPrinter::VisitCStyleCastExpr(CStyleCastExpr *Node) {
void StmtPrinter::VisitCompoundLiteralExpr(CompoundLiteralExpr *Node) {
OS << '(';
- Node->getType().print(OS, Policy);
+ if (Node->getStorageClass() == SC_Static)
+ OS << "static ";
+ else if (Node->getStorageClass() == SC_Register)
+ OS << "register ";
+ switch (Node->getTSCSpec()) {
+ case TSCS_thread_local:
+ OS << "thread_local ";
+ break;
+ case TSCS__Thread_local:
+ OS << "_Thread_local ";
+ break;
+ case TSCS___thread:
+ OS << "__thread ";
+ break;
+ case TSCS_unspecified:
+ break;
+ }
+ QualType T = Node->getType();
+ if (Node->isConstexpr()) {
+ T = Node->getTypeSourceInfo()->getType();
+ OS << "constexpr ";
+ }
+ T.print(OS, Policy);
OS << ')';
PrintExpr(Node->getInitializer());
}
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 00c132f1ed9e0..ef617ddfd49fb 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -1600,6 +1600,9 @@ void StmtProfiler::VisitMemberExpr(const MemberExpr *S) {
void StmtProfiler::VisitCompoundLiteralExpr(const CompoundLiteralExpr *S) {
VisitExpr(S);
ID.AddBoolean(S->isFileScope());
+ ID.AddInteger(S->getStorageClass());
+ ID.AddInteger(S->getTSCSpec());
+ ID.AddBoolean(S->isConstexpr());
}
void StmtProfiler::VisitCastExpr(const CastExpr *S) {
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 9201e40bc13a1..fadf00a606f3e 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5993,8 +5993,17 @@ CodeGenFunction::EmitLValueForFieldInitialization(LValue Base,
}
LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){
- if (E->isFileScope()) {
+ if (E->isFileScope() || E->hasGlobalStorage()) {
+ if (E->getType()->isVariablyModifiedType())
+ EmitVariablyModifiedType(E->getType());
+
ConstantAddress GlobalPtr = CGM.GetAddrOfConstantCompoundLiteral(E);
+ if (E->hasThreadStorage()) {
+ llvm::Value *V = Builder.CreateThreadLocalAddress(GlobalPtr.getPointer());
+ return MakeAddrLValue(
+ Address(V, ConvertTypeForMem(E->getType()), GlobalPtr.getAlignment()),
+ E->getType(), AlignmentSource::Decl);
+ }
return MakeAddrLValue(GlobalPtr, E->getType(), AlignmentSource::Decl);
}
if (E->getType()->isVariablyModifiedType())
@@ -6005,8 +6014,10 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){
const Expr *InitExpr = E->getInitializer();
LValue Result = MakeAddrLValue(DeclPtr, E->getType(), AlignmentSource::Decl);
- EmitAnyExprToMem(InitExpr, DeclPtr, E->getType().getQualifiers(),
- /*Init*/ true);
+ if (E->getType()->isAtomicType())
+ EmitAtomicInit(const_cast<Expr *>(InitExpr), Result);
+ else
+ EmitInitializationToLValue(InitExpr, Result);
// Block-scope compound literals are destroyed at the end of the enclosing
// scope in C.
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index bc35ffdaad2bd..c1309c456d4e3 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -789,9 +789,10 @@ void AggExprEmitter::VisitOpaqueValueExpr(OpaqueValueExpr *e) {
}
void AggExprEmitter::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
- if (Dest.isPotentiallyAliased()) {
- // Just emit a load of the lvalue + a copy, because our compound literal
- // might alias the destination.
+ if (E->hasGlobalStorage() || E->getType()->isAtomicType() ||
+ (E->getStorageClass() == SC_Register &&
+ E->getType().isVolatileQualified()) ||
+ Dest.isPotentiallyAliased()) {
EmitAggLoadOfLValue(E);
return;
}
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 257de0145855b..2123eab890779 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1089,8 +1089,8 @@ tryEmitGlobalCompoundLiteral(ConstantEmitter &emitter,
llvm::Constant *C = emitter.tryEmitForInitializer(E->getInitializer(),
addressSpace, E->getType());
if (!C) {
- assert(!E->isFileScope() &&
- "file-scope compound literal did not have constant initializer!");
+ assert(!E->isFileScope() && !E->hasGlobalStorage() &&
+ "global compound literal did not have constant initializer!");
return ConstantAddress::invalid();
}
@@ -1098,7 +1098,8 @@ tryEmitGlobalCompoundLiteral(ConstantEmitter &emitter,
CGM.getModule(), C->getType(),
E->getType().isConstantStorage(CGM.getContext(), true, false),
llvm::GlobalValue::InternalLinkage, C, ".compoundliteral", nullptr,
- llvm::GlobalVariable::NotThreadLocal,
+ E->hasThreadStorage() ? CGM.GetDefaultLLVMTLSModel()
+ : llvm::GlobalVariable::NotThreadLocal,
CGM.getContext().getTargetAddressSpace(addressSpace));
emitter.finalize(GV);
GV->setAlignment(Align.getAsAlign());
@@ -2770,7 +2771,8 @@ void CodeGenModule::setAddrOfConstantCompoundLiteral(
ConstantAddress
CodeGenModule::GetAddrOfConstantCompoundLiteral(const CompoundLiteralExpr *E) {
- assert(E->isFileScope() && "not a file-scope compound literal expr");
+ assert((E->isFileScope() || E->hasGlobalStorage()) &&
+ "not a global compound literal expression");
ConstantEmitter emitter(*this);
return tryEmitGlobalCompoundLiteral(emitter, E);
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 119aebb673789..861dfa1f9f1c7 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1360,8 +1360,10 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
else
Ty = VD->getType();
- if (Ty->isVariablyModifiedType())
+ if (Ty->isVariablyModifiedType()) {
+ RunCleanupsScope Scope(*this);
EmitVariablyModifiedType(Ty);
+ }
}
}
// Emit a location at the end of the prologue.
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 6ca036664094e..55e1d489efb39 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -2656,6 +2656,59 @@ bool Parser::tryParseOpenMPArrayShapingCastPart() {
return !ErrorFound;
}
+bool Parser::isCompoundLiteralStorageClassSpecifier(const Token &Tok) const {
+ if (!getLangOpts().C23)
+ return false;
+ switch (Tok.getKind()) {
+ case tok::kw_constexpr:
+ case tok::kw_register:
+ case tok::kw_static:
+ case tok::kw_thread_local:
+ case tok::kw__Thread_local:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void Parser::ParseCompoundLiteralStorageClassSpecifiers(DeclSpec &DS) {
+ DS.SetRangeStart(Tok.getLocation());
+ const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();
+ while (isCompoundLiteralStorageClassSpecifier(Tok)) {
+ SourceLocation Loc = Tok.getLocation();
+ const char *PrevSpec = nullptr;
+ unsigned DiagID = 0;
+ bool IsInvalid = false;
+ switch (Tok.getKind()) {
+ case tok::kw_static:
+ IsInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_static, Loc,
+ PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_register:
+ IsInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_register, Loc,
+ PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_thread_local:
+ case tok::kw__Thread_local:
+ IsInvalid = DS.SetStorageClassSpecThread(DeclSpec::TSCS__Thread_local,
+ Loc, PrevSpec, DiagID);
+ break;
+ case tok::kw_constexpr:
+ IsInvalid = DS.SetConstexprSpec(ConstexprSpecKind::Constexpr, Loc,
+ PrevSpec, DiagID);
+ break;
+ default:
+ llvm_unreachable("unexpected compound literal storage class specifier");
+ }
+
+ if (IsInvalid)
+ Diag(Loc, DiagID) << PrevSpec;
+
+ DS.SetRangeEnd(Loc);
+ ConsumeToken();
+ }
+}
+
ExprResult
Parser::ParseParenExpression(ParenParseOption &ExprType, bool StopIfCastExpr,
ParenExprKind ParenBehavior,
@@ -2671,7 +2724,7 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool StopIfCastExpr,
PreferredType.enterParenExpr(Tok.getLocation(), OpenLoc);
ExprResult Result(true);
- bool isAmbiguousTypeId;
+ bool isAmbiguousTypeId = false;
CastTy = nullptr;
if (Tok.is(tok::code_completion)) {
@@ -2773,7 +2826,8 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool StopIfCastExpr,
BridgeKeywordLoc, Ty.get(),
RParenLoc, SubExpr.get());
} else if (ExprType >= ParenParseOption::CompoundLiteral &&
- isTypeIdInParens(isAmbiguousTypeId)) {
+ (isCompoundLiteralStorageClassSpecifier(Tok) ||
+ isTypeIdInParens(isAmbiguousTypeId))) {
// Otherwise, this is a compound literal expression or cast expression.
@@ -2789,7 +2843,12 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool StopIfCastExpr,
return res;
}
- // Parse the type declarator.
+ DeclSpec CompoundDS(AttrFactory);
+ if (isCompoundLiteralStorageClassSpecifier(Tok)) {
+ ParseCompoundLiteralStorageClassSpecifiers(CompoundDS);
+ CompoundDS.Finish(Actions, Actions.getASTContext().getPrintingPolicy());
+ }
+
DeclSpec DS(AttrFactory);
ParseSpecifierQualifierList(DS);
Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
@@ -2817,12 +2876,32 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool StopIfCastExpr,
RParenLoc = T.getCloseLocation();
if (ParenBehavior == ParenExprKind::Unknown && Tok.is(tok::l_brace)) {
ExprType = ParenParseOption::CompoundLiteral;
+
+ if (CompoundDS.getBeginLoc().isValid())
+ Diag(CompoundDS.getBeginLoc(),
+ diag::warn_c23_compat_compound_literal_storage_class);
+
TypeResult Ty;
{
InMessageExpressionRAIIObject InMessage(*this, false);
Ty = Actions.ActOnTypeName(DeclaratorInfo);
}
- return ParseCompoundLiteralExpression(Ty.get(), OpenLoc, RParenLoc);
+ return ParseCompoundLiteralExpression(Ty.get(), OpenLoc, RParenLoc,
+ &CompoundDS);
+ }
+
+ if (!DeclaratorInfo.isInvalidType()) {
+ if (CompoundDS.getParsedSpecifiers() &
+ DeclSpec::PQ_StorageClassSpecifier) {
+ SourceLocation Loc = CompoundDS.getStorageClassSpecLoc();
+ if (Loc.isInvalid())
+ Loc = CompoundDS.getThreadStorageClassSpecLoc();
+ Diag(Loc, diag::err_typename_invalid_storageclass);
+ }
+ if (CompoundDS.hasConstexprSpecifier())
+ Diag(CompoundDS.getConstexprSpecLoc(),
+ diag::err_typename_invalid_constexpr)
+ << static_cast<int>(CompoundDS.getConstexprSpecifier());
}
if (ParenBehavior == ParenExprKind::Unknown && Tok.is(tok::l_paren)) {
@@ -2996,17 +3075,18 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool StopIfCastExpr,
return Result;
}
-ExprResult
-Parser::ParseCompoundLiteralExpression(ParsedType Ty,
- SourceLocation LParenLoc,
- SourceLocation RParenLoc) {
+ExprResult Parser::ParseCompoundLiteralExpression(ParsedType Ty,
+ SourceLocation LParenLoc,
+ SourceLocation RParenLoc,
+ const DeclSpec *DS) {
assert(Tok.is(tok::l_brace) && "Not a compound literal!");
if (!getLangOpts().C99) // Compound literals don't exist in C90.
Diag(LParenLoc, diag::ext_c99_compound_literal);
PreferredType.enterTypeCast(Tok.getLocation(), Ty.get());
ExprResult Result = ParseInitializer();
if (!Result.isInvalid() && Ty)
- return Actions.ActOnCompoundLiteral(LParenLoc, Ty, RParenLoc, Result.get());
+ return Actions.ActOnCompoundLiteral(LParenLoc, Ty, RParenLoc, Result.get(),
+ DS);
return Result;
}
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index fafee76ec18c3..e5cfc3086fb58 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -131,6 +131,7 @@ getEntityLifetime(const InitializedEntity *Entity,
case InitializedEntity::EK_Temporary:
case InitializedEntity::EK_CompoundLiteralInit:
+ case InitializedEntity::EK_ConstexprCompoundLiteralInit:
case InitializedEntity::EK_RelatedResult:
// We don't yet know the storage duration of the surrounding temporary.
// Assume it's got full-expression duration for now, it will patch up our
@@ -658,7 +659,7 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
case Stmt::CompoundLiteralExprClass: {
if (auto *CLE = dyn_cast<CompoundLiteralExpr>(Init)) {
- if (!CLE->isFileScope())
+ if (!CLE->isFileScope() && !CLE->hasGlobalStorage())
Visit(Path, Local(CLE), RK);
}
break;
diff --git a/clang/lib/Sema/DelayedDiagnostic.cpp b/clang/lib/Sema/DelayedDiagnostic.cpp
index cb2721b92090e..4f94da0e64fba 100644
--- a/clang/lib/Sema/DelayedDiagnostic.cpp
+++ b/clang/lib/Sema/DelayedDiagnostic.cpp
@@ -68,6 +68,7 @@ void DelayedDiagnostic::Destroy() {
break;
case ForbiddenType:
+ case ForbiddenStatic:
break;
}
}
diff --git a/clang/lib/Sema/Scope.cpp b/clang/lib/Sema/Scope.cpp
index fc79b1a056ed9..1cd7a21893ba4 100644
--- a/clang/lib/Sema/Scope.cpp
+++ b/clang/lib/Sema/Scope.cpp
@@ -103,14 +103,15 @@ void Scope::Init(Scope *parent, unsigned flags) {
NRVO = std::nullopt;
}
-bool Scope::containedInPrototypeScope() const {
- const Scope *S = this;
- while (S) {
+const Scope *Scope::getEnclosingFunctionPrototypeScope() const {
+ for (const Scope *S = this; S; S = S->getParent())
if (S->isFunctionPrototypeScope())
- return true;
- S = S->getParent();
- }
- return false;
+ return S;
+ return nullptr;
+}
+
+bool Scope::containedInPrototypeScope() const {
+ return getEnclosingFunctionPrototypeScope() != nullptr;
}
void Scope::EnterLoopBody(LabelDecl *LD) {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a553c6994f0ed..cf09bf2615b07 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7419,7 +7419,7 @@ static void checkDLLAttributeRedeclaration(Sema &S, NamedDecl *OldDecl,
/// Given that we are within the definition of the given function,
/// will that definition behave like C99's 'inline', where the
/// definition is discarded except for optimization purposes?
-static bool isFunctionDefinitionDiscarded(Sema &S, FunctionDecl *FD) {
+static bool isFunctionDefinitionDiscarded(Sema &S, const FunctionDecl *FD) {
// Try to avoid calling GetGVALinkageForFunction.
// All cases of this require the 'inline' keyword.
@@ -7433,6 +7433,14 @@ static bool isFunctionDefinitionDiscarded(Sema &S, FunctionDecl *FD) {
return S.Context.GetGVALinkageForFunction(FD) == GVA_AvailableExternally;
}
+void Sema::DiagnoseStaticInInline(SourceLocation Loc, const FunctionDecl *FD) {
+ if (!FD || !isFunctionDefinitionDiscarded(*this, FD))
+ return;
+
+ Diag(Loc, diag::warn_static_local_in_extern_inline);
+ MaybeSuggestAddingStaticToDecl(FD);
+}
+
/// Determine whether a variable is extern "C" prior to attaching
/// an initializer. We can't just call isExternC() here, because that
/// will also compute and cache whether the declaration is externally
@@ -8190,14 +8198,9 @@ NamedDecl *Sema::ActOnVariableDeclarator(
// be marked 'static'. Also note that it's possible to get these
// semantics in C++ using __attribute__((gnu_inline)).
if (SC == SC_Static && S->getFnParent() != nullptr &&
- !NewVD->getType().isConstQualified()) {
- FunctionDecl *CurFD = getCurFunctionDecl();
- if (CurFD && isFunctionDefinitionDiscarded(*this, CurFD)) {
- Diag(D.getDeclSpec().getStorageClassSpecLoc(),
- diag::warn_static_local_in_extern_inline);
- MaybeSuggestAddingStaticToDecl(CurFD);
- }
- }
+ !NewVD->getType().isConstQualified())
+ DiagnoseStaticInInline(D.getDeclSpec().getStorageClassSpecLoc(),
+ getCurFunctionDecl());
if (D.getDeclSpec().isModulePrivateSpecified()) {
if (IsVariableTemplateSpecialization)
@@ -8898,32 +8901,31 @@ static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND,
return false;
}
-static bool CheckC23ConstexprVarType(Sema &SemaRef, SourceLocation VarLoc,
- QualType T) {
- QualType CanonT = SemaRef.Context.getCanonicalType(T);
+bool Sema::CheckConstexprType(SourceLocation Loc, QualType T, unsigned DiagID) {
+ QualType CanonT = Context.getCanonicalType(T);
// C23 6.7.1p5: An object declared with storage-class specifier constexpr or
// any of its members, even recursively, shall not have an atomic type, or a
// variably modified type, or a type that is volatile or restrict qualified.
if (CanonT->isVariablyModifiedType()) {
- SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T;
+ Diag(Loc, DiagID) << T;
return true;
}
// Arrays are qualified by their element type, so get the base type (this
// works on non-arrays as well).
- CanonT = SemaRef.Context.getBaseElementType(CanonT);
+ CanonT = Context.getBaseElementType(CanonT);
if (CanonT->isAtomicType() || CanonT.isVolatileQualified() ||
CanonT.isRestrictQualified()) {
- SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T;
+ Diag(Loc, DiagID) << T;
return true;
}
if (CanonT->isRecordType()) {
const RecordDecl *RD = CanonT->getAsRecordDecl();
if (!RD->isInvalidDecl() &&
- llvm::any_of(RD->fields(), [&SemaRef, VarLoc](const FieldDecl *F) {
- return CheckC23ConstexprVarType(SemaRef, VarLoc, F->getType());
+ llvm::any_of(RD->fields(), [this, Loc, DiagID](const FieldDecl *F) {
+ return CheckConstexprType(Loc, F->getType(), DiagID);
}))
return true;
}
@@ -9199,7 +9201,8 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
}
if (getLangOpts().C23 && NewVD->isConstexpr() &&
- CheckC23ConstexprVarType(*this, NewVD->getLocation(), T)) {
+ CheckConstexprType(NewVD->getLocation(), T,
+ diag::err_c23_constexpr_invalid_type)) {
NewVD->setInvalidDecl();
return;
}
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 1b272b5416860..cf6ce9b5508be 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -9017,6 +9017,14 @@ void Sema::PopParsingDeclaration(ParsingDeclState state, Decl *decl) {
case DelayedDiagnostic::ForbiddenType:
handleDelayedForbiddenType(*this, diag, decl);
break;
+
+ case DelayedDiagnostic::ForbiddenStatic:
+ if (const FunctionDecl *FD = decl->getAsFunction();
+ FD && FD->isThisDeclarationADefinition()) {
+ diag.Triggered = true;
+ DiagnoseStaticInInline(diag.Loc, FD);
+ }
+ break;
}
}
} while ((pool = pool->getParent()));
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index ed3d27b5adc27..b5f19964cbefd 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -63,6 +63,7 @@
#include "clang/Sema/SemaOpenMP.h"
#include "clang/Sema/SemaPseudoObject.h"
#include "clang/Sema/Template.h"
+#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ConvertUTF.h"
@@ -123,6 +124,40 @@ static void DiagnoseUnusedOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc) {
}
}
+static bool checkThreadLocalCompoundLiteral(Sema &S, SourceLocation Loc,
+ QualType T) {
+ if (S.getLangOpts().ObjCAutoRefCount && T->isObjCLifetimeType()) {
+ Qualifiers::ObjCLifetime Lifetime = T.getObjCLifetime();
+ if (Lifetime == Qualifiers::OCL_None)
+ Lifetime = T->getObjCARCImplicitLifetime();
+
+ if (Lifetime && Lifetime != Qualifiers::OCL_ExplicitNone) {
+ S.Diag(Loc, diag::err_arc_thread_ownership)
+ << S.Context.getLifetimeQualifiedType(T, Lifetime);
+ return true;
+ }
+ }
+
+ if (T.isDestructedType()) {
+ S.Diag(Loc, diag::err_thread_nontrivial_dtor);
+ return true;
+ }
+
+ unsigned MaxAlign = S.Context.getTargetInfo().getMaxTLSAlign();
+ if (!MaxAlign || T->isDependentType())
+ return false;
+
+ CharUnits MaxAlignChars = S.Context.toCharUnitsFromBits(MaxAlign);
+ CharUnits TypeAlign = S.Context.getTypeAlignInChars(T);
+ if (TypeAlign <= MaxAlignChars)
+ return false;
+
+ S.Diag(Loc, diag::err_tls_compound_literal_aligned_over_maximum)
+ << (unsigned)TypeAlign.getQuantity()
+ << (unsigned)MaxAlignChars.getQuantity();
+ return true;
+}
+
void Sema::NoteDeletedFunction(FunctionDecl *Decl) {
assert(Decl && Decl->isDeleted());
@@ -519,6 +554,54 @@ SourceRange Sema::getExprRange(Expr *E) const {
// Standard Promotions and Conversions
//===----------------------------------------------------------------------===//
+namespace {
+enum {
+ AO_Bit_Field = 0,
+ AO_Vector_Element = 1,
+ AO_Property_Expansion = 2,
+ AO_Register_Variable = 3,
+ AO_Matrix_Element = 4,
+ AO_Register_Compound_Literal = 5,
+ AO_No_Error = 6
+};
+}
+
+using PrimaryObject = llvm::PointerUnion<ValueDecl *, CompoundLiteralExpr *>;
+
+static PrimaryObject getPrimaryObject(Expr *E) {
+ E = E->IgnoreParens();
+ switch (E->getStmtClass()) {
+ case Stmt::DeclRefExprClass:
+ return cast<DeclRefExpr>(E)->getDecl();
+ case Stmt::CompoundLiteralExprClass:
+ return cast<CompoundLiteralExpr>(E);
+ case Stmt::MemberExprClass:
+ if (cast<MemberExpr>(E)->isArrow())
+ return {};
+ return getPrimaryObject(cast<MemberExpr>(E)->getBase());
+ case Stmt::ArraySubscriptExprClass: {
+ Expr *Base = cast<ArraySubscriptExpr>(E)->getBase()->IgnoreParens();
+ if (auto *ICE = dyn_cast<ImplicitCastExpr>(Base);
+ ICE && ICE->getSubExpr()->getType()->isArrayType())
+ return getPrimaryObject(ICE->getSubExpr());
+ return {};
+ }
+ case Stmt::UnaryOperatorClass: {
+ UnaryOperator *UO = cast<UnaryOperator>(E);
+ if (UO->getOpcode() == UO_Real || UO->getOpcode() == UO_Imag ||
+ UO->getOpcode() == UO_Extension)
+ return getPrimaryObject(UO->getSubExpr());
+ return {};
+ }
+ case Stmt::ImplicitCastExprClass:
+ return getPrimaryObject(cast<ImplicitCastExpr>(E)->getSubExpr());
+ case Stmt::CXXUuidofExprClass:
+ return cast<CXXUuidofExpr>(E)->getGuidDecl();
+ default:
+ return {};
+ }
+}
+
/// DefaultFunctionArrayConversion (C99 6.3.2.1p3, C99 6.3.2.1p4).
ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, bool Diagnose) {
// Handle any placeholder expressions which made it here.
@@ -552,6 +635,13 @@ ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, bool Diagnose) {
// T" can be converted to an rvalue of type "pointer to T".
//
if (getLangOpts().C99 || getLangOpts().CPlusPlus || E->isLValue()) {
+ if (!getLangOpts().CPlusPlus) {
+ auto *CLE = getPrimaryObject(E).dyn_cast<CompoundLiteralExpr *>();
+ if (CLE && CLE->getStorageClass() == SC_Register)
+ return Diag(E->getExprLoc(), diag::err_typecheck_address_of)
+ << AO_Register_Compound_Literal << CLE->getSourceRange();
+ }
+
ExprResult Res = ImpCastExprToType(E, Context.getArrayDecayedType(Ty),
CK_ArrayToPointerDecay);
if (Res.isInvalid())
@@ -7425,9 +7515,9 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl);
}
-ExprResult
-Sema::ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty,
- SourceLocation RParenLoc, Expr *InitExpr) {
+ExprResult Sema::ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty,
+ SourceLocation RParenLoc, Expr *InitExpr,
+ const DeclSpec *DS) {
assert(Ty && "ActOnCompoundLiteral(): missing type");
assert(InitExpr && "ActOnCompoundLiteral(): missing expression");
@@ -7436,13 +7526,90 @@ Sema::ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty,
if (!TInfo)
TInfo = Context.getTrivialTypeSourceInfo(literalType);
- return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, InitExpr);
-}
-
-ExprResult
-Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
- SourceLocation RParenLoc, Expr *LiteralExpr) {
+ StorageClass SC = SC_None;
+ ThreadStorageClassSpecifier TSC = TSCS_unspecified;
+ ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified;
+ SourceLocation StorageClassLoc;
+ SourceLocation ThreadStorageClassLoc;
+ SourceLocation ConstexprLoc;
+ if (DS) {
+ if (DS->getStorageClassSpec() == DeclSpec::SCS_static)
+ SC = SC_Static;
+ else if (DS->getStorageClassSpec() == DeclSpec::SCS_register)
+ SC = SC_Register;
+
+ TSC = DS->getThreadStorageClassSpec();
+ ConstexprKind = DS->getConstexprSpecifier();
+ StorageClassLoc = DS->getStorageClassSpecLoc();
+ ThreadStorageClassLoc = DS->getThreadStorageClassSpecLoc();
+ ConstexprLoc = DS->getConstexprSpecLoc();
+ }
+
+ return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, InitExpr, SC,
+ StorageClassLoc, TSC, ThreadStorageClassLoc,
+ ConstexprKind, ConstexprLoc);
+}
+
+ExprResult Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc,
+ TypeSourceInfo *TInfo,
+ SourceLocation RParenLoc,
+ Expr *LiteralExpr, StorageClass SC,
+ ThreadStorageClassSpecifier TSC,
+ ConstexprSpecKind ConstexprKind) {
+ return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, LiteralExpr, SC,
+ LParenLoc, TSC, LParenLoc, ConstexprKind,
+ LParenLoc);
+}
+
+ExprResult Sema::BuildCompoundLiteralExpr(
+ SourceLocation LParenLoc, TypeSourceInfo *TInfo, SourceLocation RParenLoc,
+ Expr *LiteralExpr, StorageClass SC, SourceLocation StorageClassLoc,
+ ThreadStorageClassSpecifier TSC, SourceLocation ThreadStorageClassLoc,
+ ConstexprSpecKind ConstexprKind, SourceLocation ConstexprLoc) {
QualType literalType = TInfo->getType();
+ const Scope *S = getCurScope();
+ const Scope *PrototypeScope = S->getEnclosingFunctionPrototypeScope();
+
+ bool HasStatic = SC == SC_Static;
+ bool HasRegister = SC == SC_Register;
+ bool HasThreadStorage = TSC != TSCS_unspecified;
+ bool HasConstexpr = ConstexprKind == ConstexprSpecKind::Constexpr;
+
+ bool IsPrototypeScope = PrototypeScope;
+ bool IsFunctionDeclarationScope =
+ PrototypeScope && PrototypeScope->isFunctionDeclarationScope();
+ bool IsFileScope = !CurContext->isFunctionOrMethod() &&
+ !S->isInCFunctionScope() && !PrototypeScope;
+
+ if (HasConstexpr && literalType->isObjectType())
+ literalType.addConst();
+
+ if (getLangOpts().C23 &&
+ (SC != SC_None || HasThreadStorage || HasConstexpr)) {
+ if (HasRegister && IsFileScope) {
+ Diag(StorageClassLoc, diag::err_register_compound_literal_file_scope);
+ return ExprError();
+ }
+ if (HasThreadStorage && !IsFileScope && !HasStatic) {
+ Diag(ThreadStorageClassLoc,
+ diag::err_thread_local_compound_literal_without_static);
+ return ExprError();
+ }
+ if (HasThreadStorage && !Context.getTargetInfo().isTLSSupported()) {
+ Diag(ThreadStorageClassLoc, diag::err_thread_unsupported);
+ return ExprError();
+ }
+ if (HasConstexpr &&
+ CheckConstexprType(ConstexprLoc, literalType,
+ diag::err_constexpr_compound_literal_invalid_type))
+ return ExprError();
+ }
+
+ if (getLangOpts().C23 && IsFileScope &&
+ literalType->isVariablyModifiedType()) {
+ Diag(LParenLoc, diag::err_vm_decl_in_file_scope);
+ return ExprError();
+ }
if (literalType->isArrayType()) {
if (RequireCompleteSizedType(
@@ -7477,32 +7644,47 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
return ExprError();
}
} else if (!literalType->isDependentType() &&
- RequireCompleteType(LParenLoc, literalType,
- diag::err_typecheck_decl_incomplete_type,
- SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd())))
+ RequireCompleteType(
+ LParenLoc, literalType,
+ diag::err_typecheck_decl_incomplete_type,
+ SourceRange(LParenLoc,
+ LiteralExpr->getSourceRange().getEnd())))
return ExprError();
- InitializedEntity Entity
- = InitializedEntity::InitializeCompoundLiteralInit(TInfo);
- InitializationKind Kind
- = InitializationKind::CreateCStyleCast(LParenLoc,
- SourceRange(LParenLoc, RParenLoc),
- /*InitList=*/true);
+ InitializedEntity Entity = InitializedEntity::InitializeCompoundLiteralInit(
+ TInfo, literalType, ConstexprKind);
+ InitializationKind Kind = InitializationKind::CreateCStyleCast(
+ LParenLoc, SourceRange(LParenLoc, RParenLoc),
+ /*InitList=*/true);
InitializationSequence InitSeq(*this, Entity, Kind, LiteralExpr);
- ExprResult Result = InitSeq.Perform(*this, Entity, Kind, LiteralExpr,
- &literalType);
+ ExprResult Result =
+ InitSeq.Perform(*this, Entity, Kind, LiteralExpr, &literalType);
if (Result.isInvalid())
return ExprError();
LiteralExpr = Result.get();
- // We treat the compound literal as being at file scope if it's not in a
- // function or method body, or within the function's prototype scope. This
- // means the following compound literal is not at file scope:
- // void func(char *para[(int [1]){ 0 }[0]);
- const Scope *S = getCurScope();
- bool IsFileScope = !CurContext->isFunctionOrMethod() &&
- !S->isInCFunctionScope() &&
- (!S || !S->isFunctionPrototypeScope());
+ if (HasConstexpr && !LiteralExpr->isTypeDependent() &&
+ !LiteralExpr->isValueDependent() && !literalType->isDependentType()) {
+ SmallVector<PartialDiagnosticAt, 4> Notes;
+ Expr::EvalResult Eval;
+ Eval.Diag = &Notes;
+ if (!LiteralExpr->EvaluateAsConstantExpr(
+ Eval, Context,
+ Expr::ConstantExprKind::CompoundLiteralInitializer) ||
+ Notes.size() > 0) {
+ SourceLocation DiagLoc = ConstexprLoc;
+ if (Notes.size() == 1 && Notes.front().second.getDiagID() ==
+ diag::note_invalid_subexpr_in_const_expr) {
+ DiagLoc = Notes.front().first;
+ Notes.clear();
+ }
+ Diag(DiagLoc, diag::err_compound_literal_initializer_not_constant)
+ << LiteralExpr->getSourceRange();
+ for (const PartialDiagnosticAt &Note : Notes)
+ Diag(Note.first, Note.second);
+ return ExprError();
+ }
+ }
// In C, compound literals are l-values for some reason.
// For GCC compatibility, in C++, file-scope array compound literals with
@@ -7527,33 +7709,52 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
? VK_PRValue
: VK_LValue;
+ bool HasGlobalStorage =
+ IsFileScope || (getLangOpts().C23 && (HasStatic || HasThreadStorage));
+
+ if (HasThreadStorage && checkThreadLocalCompoundLiteral(
+ *this, ThreadStorageClassLoc, literalType))
+ return ExprError();
+
// C99 6.5.2.5
// "If the compound literal occurs outside the body of a function, the
// initializer list shall consist of constant expressions."
- if (IsFileScope)
+ if (HasGlobalStorage || HasConstexpr)
if (auto ILE = dyn_cast<InitListExpr>(LiteralExpr))
for (unsigned i = 0, j = ILE->getNumInits(); i != j; i++) {
Expr *Init = ILE->getInit(i);
- if (!Init->isTypeDependent() && !Init->isValueDependent() &&
+ if (!HasConstexpr && !Init->isTypeDependent() &&
+ !Init->isValueDependent() &&
!Init->isConstantInitializer(Context)) {
Diag(Init->getExprLoc(), diag::err_init_element_not_constant)
<< Init->getSourceBitField();
return ExprError();
}
- ILE->setInit(i, ConstantExpr::Create(Context, Init));
+ Expr::EvalResult Eval;
+ if (HasConstexpr &&
+ Init->EvaluateAsRValue(Eval, Context,
+ /*InConstantContext=*/true) &&
+ Eval.Val.hasValue())
+ ILE->setInit(i, ConstantExpr::Create(Context, Init, Eval.Val));
+ else
+ ILE->setInit(i, ConstantExpr::Create(Context, Init));
}
- auto *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, VK,
- LiteralExpr, IsFileScope);
- if (IsFileScope) {
- if (!LiteralExpr->isTypeDependent() &&
- !LiteralExpr->isValueDependent() &&
- !literalType->isDependentType()) // C99 6.5.2.5p3
+ auto *E = new (Context)
+ CompoundLiteralExpr(LParenLoc, TInfo, literalType, VK, LiteralExpr,
+ IsFileScope, SC, TSC, ConstexprKind);
+
+ if (HasGlobalStorage && !HasConstexpr) {
+ if (!LiteralExpr->isTypeDependent() && !LiteralExpr->isValueDependent() &&
+ !literalType->isDependentType()) { // C99 6.5.2.5p3
if (CheckForConstantInitializer(LiteralExpr))
return ExprError();
- } else if (literalType.getAddressSpace() != LangAS::opencl_private &&
- literalType.getAddressSpace() != LangAS::Default) {
+ }
+ }
+
+ if (!IsFileScope && literalType.getAddressSpace() != LangAS::opencl_private &&
+ literalType.getAddressSpace() != LangAS::Default) {
// Embedded-C extensions to C99 6.5.2.5:
// "If the compound literal occurs inside the body of a function, the
// type name shall not be qualified by an address-space qualifier."
@@ -7562,7 +7763,17 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
return ExprError();
}
- if (!IsFileScope && !getLangOpts().CPlusPlus) {
+ if (HasStatic && !literalType.isConstQualified()) {
+ if (!IsPrototypeScope) {
+ DiagnoseStaticInInline(StorageClassLoc, getCurFunctionDecl());
+ } else if (IsFunctionDeclarationScope &&
+ DelayedDiagnostics.shouldDelayDiagnostics()) {
+ DelayedDiagnostics.add(
+ sema::DelayedDiagnostic::makeForbiddenStatic(StorageClassLoc));
+ }
+ }
+
+ if (!HasGlobalStorage && !IsPrototypeScope && !getLangOpts().CPlusPlus) {
// Compound literals that have automatic storage duration are destroyed at
// the end of the scope in C; in C++, they're just temporaries.
@@ -14843,78 +15054,6 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op,
}
}
-/// getPrimaryDecl - Helper function for CheckAddressOfOperand().
-/// This routine allows us to typecheck complex/recursive expressions
-/// where the declaration is needed for type checking. We only need to
-/// handle cases when the expression references a function designator
-/// or is an lvalue. Here are some examples:
-/// - &(x) => x
-/// - &*****f => f for f a function designator.
-/// - &s.xx => s
-/// - &s.zz[1].yy -> s, if zz is an array
-/// - *(x + 1) -> x, if x is an array
-/// - &"123"[2] -> 0
-/// - & __real__ x -> x
-///
-/// FIXME: We don't recurse to the RHS of a comma, nor handle pointers to
-/// members.
-static ValueDecl *getPrimaryDecl(Expr *E) {
- switch (E->getStmtClass()) {
- case Stmt::DeclRefExprClass:
- return cast<DeclRefExpr>(E)->getDecl();
- case Stmt::MemberExprClass:
- // If this is an arrow operator, the address is an offset from
- // the base's value, so the object the base refers to is
- // irrelevant.
- if (cast<MemberExpr>(E)->isArrow())
- return nullptr;
- // Otherwise, the expression refers to a part of the base
- return getPrimaryDecl(cast<MemberExpr>(E)->getBase());
- case Stmt::ArraySubscriptExprClass: {
- // FIXME: This code shouldn't be necessary! We should catch the implicit
- // promotion of register arrays earlier.
- Expr* Base = cast<ArraySubscriptExpr>(E)->getBase();
- if (ImplicitCastExpr* ICE = dyn_cast<ImplicitCastExpr>(Base)) {
- if (ICE->getSubExpr()->getType()->isArrayType())
- return getPrimaryDecl(ICE->getSubExpr());
- }
- return nullptr;
- }
- case Stmt::UnaryOperatorClass: {
- UnaryOperator *UO = cast<UnaryOperator>(E);
-
- switch(UO->getOpcode()) {
- case UO_Real:
- case UO_Imag:
- case UO_Extension:
- return getPrimaryDecl(UO->getSubExpr());
- default:
- return nullptr;
- }
- }
- case Stmt::ParenExprClass:
- return getPrimaryDecl(cast<ParenExpr>(E)->getSubExpr());
- case Stmt::ImplicitCastExprClass:
- // If the result of an implicit cast is an l-value, we care about
- // the sub-expression; otherwise, the result here doesn't matter.
- return getPrimaryDecl(cast<ImplicitCastExpr>(E)->getSubExpr());
- case Stmt::CXXUuidofExprClass:
- return cast<CXXUuidofExpr>(E)->getGuidDecl();
- default:
- return nullptr;
- }
-}
-
-namespace {
-enum {
- AO_Bit_Field = 0,
- AO_Vector_Element = 1,
- AO_Property_Expansion = 2,
- AO_Register_Variable = 3,
- AO_Matrix_Element = 4,
- AO_No_Error = 5
-};
-}
/// Diagnose invalid operand for address of operations.
///
/// \param Type The type of operand which cannot have its address taken.
@@ -15018,7 +15157,8 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
// Technically, there should be a check for array subscript
// expressions here, but the result of one is always an lvalue anyway.
}
- ValueDecl *dcl = getPrimaryDecl(op);
+ PrimaryObject Primary = getPrimaryObject(op);
+ ValueDecl *dcl = Primary.dyn_cast<ValueDecl *>();
if (auto *FD = dyn_cast_or_null<FunctionDecl>(dcl))
if (!checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true,
@@ -15169,6 +15309,10 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
NonTypeTemplateParmDecl, BindingDecl, MSGuidDecl,
UnnamedGlobalConstantDecl>(dcl))
llvm_unreachable("Unknown/unexpected decl type");
+ } else if (!getLangOpts().CPlusPlus) {
+ if (auto *CLE = Primary.dyn_cast<CompoundLiteralExpr *>();
+ CLE && CLE->getStorageClass() == SC_Register)
+ AddressOfError = AO_Register_Compound_Literal;
}
if (AddressOfError != AO_No_Error) {
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 09d9f1eabd058..7f0a691a1cb28 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -194,18 +194,15 @@ static void updateGNUCompoundLiteralRValue(Expr *E) {
}
}
-static bool initializingConstexprVariable(const InitializedEntity &Entity) {
- Decl *D = Entity.getDecl();
- const InitializedEntity *Parent = &Entity;
-
- while (Parent) {
- D = Parent->getDecl();
- Parent = Parent->getParent();
+static bool initializingConstexprObject(const InitializedEntity &Entity) {
+ for (const InitializedEntity *Parent = &Entity; Parent;
+ Parent = Parent->getParent()) {
+ if (Parent->isConstexprCompoundLiteral())
+ return true;
+ if (const auto *VD = dyn_cast_if_present<VarDecl>(Parent->getDecl());
+ VD && VD->isConstexpr())
+ return true;
}
-
- if (const auto *VD = dyn_cast_if_present<VarDecl>(D); VD && VD->isConstexpr())
- return true;
-
return false;
}
@@ -1311,6 +1308,7 @@ static void warnBracedScalarInit(Sema &S, const InitializedEntity &Entity,
case InitializedEntity::EK_New:
case InitializedEntity::EK_Temporary:
case InitializedEntity::EK_CompoundLiteralInit:
+ case InitializedEntity::EK_ConstexprCompoundLiteralInit:
// No warning, braces are part of the syntax of the underlying construct.
break;
@@ -1436,7 +1434,8 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity,
// parts.
CheckComplexType(Entity, IList, DeclType, Index,
StructuredList, StructuredIndex);
- } else if (DeclType->isScalarType()) {
+ } else if (DeclType->isScalarType() ||
+ (!SemaRef.getLangOpts().CPlusPlus && DeclType->isAtomicType())) {
CheckScalarType(Entity, IList, DeclType, Index,
StructuredList, StructuredIndex);
} else if (DeclType->isVectorType()) {
@@ -1626,7 +1625,7 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
if (!VerifyOnly)
CheckStringInit(expr, ElemType, arrayType, SemaRef, Entity,
SemaRef.getLangOpts().C23 &&
- initializingConstexprVariable(Entity));
+ initializingConstexprObject(Entity));
if (StructuredList)
UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
++Index;
@@ -2177,7 +2176,7 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
if (!VerifyOnly)
CheckStringInit(
IList->getInit(Index), DeclType, arrayType, SemaRef, Entity,
- SemaRef.getLangOpts().C23 && initializingConstexprVariable(Entity));
+ SemaRef.getLangOpts().C23 && initializingConstexprObject(Entity));
if (StructuredList) {
UpdateStructuredListElement(StructuredList, StructuredIndex,
IList->getInit(Index));
@@ -3809,6 +3808,7 @@ DeclarationName InitializedEntity::getName() const {
case EK_BlockElement:
case EK_LambdaToBlockConversionBlockElement:
case EK_CompoundLiteralInit:
+ case EK_ConstexprCompoundLiteralInit:
case EK_RelatedResult:
return DeclarationName();
}
@@ -3844,6 +3844,7 @@ ValueDecl *InitializedEntity::getDecl() const {
case EK_LambdaToBlockConversionBlockElement:
case EK_LambdaCapture:
case EK_CompoundLiteralInit:
+ case EK_ConstexprCompoundLiteralInit:
case EK_RelatedResult:
return nullptr;
}
@@ -3868,6 +3869,7 @@ bool InitializedEntity::allowsNRVO() const {
case EK_New:
case EK_Temporary:
case EK_CompoundLiteralInit:
+ case EK_ConstexprCompoundLiteralInit:
case EK_Base:
case EK_Delegating:
case EK_ArrayElement:
@@ -3907,6 +3909,9 @@ unsigned InitializedEntity::dumpImpl(raw_ostream &OS) const {
case EK_New: OS << "New"; break;
case EK_Temporary: OS << "Temporary"; break;
case EK_CompoundLiteralInit: OS << "CompoundLiteral";break;
+ case EK_ConstexprCompoundLiteralInit:
+ OS << "ConstexprCompoundLiteral";
+ break;
case EK_RelatedResult: OS << "RelatedResult"; break;
case EK_Base: OS << "Base"; break;
case EK_Delegating: OS << "Delegating"; break;
@@ -4930,7 +4935,8 @@ static void TryReferenceListInitialization(Sema &S,
return;
}
// Can't reference initialize a compound literal.
- if (Entity.getKind() == InitializedEntity::EK_CompoundLiteralInit) {
+ if (Entity.getKind() == InitializedEntity::EK_CompoundLiteralInit ||
+ Entity.getKind() == InitializedEntity::EK_ConstexprCompoundLiteralInit) {
Sequence.SetFailed(InitializationSequence::FK_ReferenceBindingToInitList);
return;
}
@@ -7196,6 +7202,7 @@ static AssignmentAction getAssignmentAction(const InitializedEntity &Entity,
case InitializedEntity::EK_LambdaToBlockConversionBlockElement:
case InitializedEntity::EK_LambdaCapture:
case InitializedEntity::EK_CompoundLiteralInit:
+ case InitializedEntity::EK_ConstexprCompoundLiteralInit:
return AssignmentAction::Initializing;
}
@@ -7223,6 +7230,7 @@ static bool shouldBindAsTemporary(const InitializedEntity &Entity) {
case InitializedEntity::EK_LambdaToBlockConversionBlockElement:
case InitializedEntity::EK_LambdaCapture:
case InitializedEntity::EK_CompoundLiteralInit:
+ case InitializedEntity::EK_ConstexprCompoundLiteralInit:
case InitializedEntity::EK_TemplateParameter:
return false;
@@ -7265,6 +7273,7 @@ static bool shouldDestroyEntity(const InitializedEntity &Entity) {
case InitializedEntity::EK_ArrayElement:
case InitializedEntity::EK_Exception:
case InitializedEntity::EK_CompoundLiteralInit:
+ case InitializedEntity::EK_ConstexprCompoundLiteralInit:
case InitializedEntity::EK_RelatedResult:
return true;
}
@@ -7306,6 +7315,7 @@ static SourceLocation getInitializationLoc(const InitializedEntity &Entity,
case InitializedEntity::EK_BlockElement:
case InitializedEntity::EK_LambdaToBlockConversionBlockElement:
case InitializedEntity::EK_CompoundLiteralInit:
+ case InitializedEntity::EK_ConstexprCompoundLiteralInit:
case InitializedEntity::EK_RelatedResult:
return Initializer->getBeginLoc();
}
@@ -7562,6 +7572,7 @@ static bool isExplicitTemporary(const InitializedEntity &Entity,
switch (Entity.getKind()) {
case InitializedEntity::EK_Temporary:
case InitializedEntity::EK_CompoundLiteralInit:
+ case InitializedEntity::EK_ConstexprCompoundLiteralInit:
case InitializedEntity::EK_RelatedResult:
break;
default:
@@ -8579,7 +8590,7 @@ ExprResult InitializationSequence::Perform(Sema &S,
return ExprError();
CurInit = CurInitExprRes;
- if (S.getLangOpts().C23 && initializingConstexprVariable(Entity)) {
+ if (S.getLangOpts().C23 && initializingConstexprObject(Entity)) {
CheckC23ConstexprInitConversion(S, SourceType, Entity.getType(),
CurInit.get());
@@ -8623,7 +8634,7 @@ ExprResult InitializationSequence::Perform(Sema &S,
CheckStringInit(CurInit.get(), UpdateType ? *ResultType : Ty,
S.Context.getAsArrayType(Ty), S, Entity,
S.getLangOpts().C23 &&
- initializingConstexprVariable(Entity));
+ initializingConstexprObject(Entity));
break;
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 0725664a4e050..239700db3d0eb 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3103,12 +3103,13 @@ class TreeTransform {
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
- ExprResult RebuildCompoundLiteralExpr(SourceLocation LParenLoc,
- TypeSourceInfo *TInfo,
- SourceLocation RParenLoc,
- Expr *Init) {
- return getSema().BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc,
- Init);
+ ExprResult RebuildCompoundLiteralExpr(
+ SourceLocation LParenLoc, TypeSourceInfo *TInfo, SourceLocation RParenLoc,
+ Expr *Init, StorageClass SC = SC_None,
+ ThreadStorageClassSpecifier TSC = TSCS_unspecified,
+ ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified) {
+ return getSema().BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, Init,
+ SC, TSC, ConstexprKind);
}
/// Build a new extended vector or matrix element access expression.
@@ -14325,7 +14326,10 @@ TreeTransform<Derived>::TransformCompoundLiteralExpr(CompoundLiteralExpr *E) {
return getDerived().RebuildCompoundLiteralExpr(
E->getLParenLoc(), NewT,
- /*FIXME:*/ E->getInitializer()->getEndLoc(), Init.get());
+ /*FIXME:*/ E->getInitializer()->getEndLoc(), Init.get(),
+ E->getStorageClass(), E->getTSCSpec(),
+ E->isConstexpr() ? ConstexprSpecKind::Constexpr
+ : ConstexprSpecKind::Unspecified);
}
template<typename Derived>
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 97f1857e200ca..2e8c055a8ac6a 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1239,6 +1239,9 @@ void ASTStmtReader::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
E->setTypeSourceInfo(readTypeSourceInfo());
E->setInitializer(Record.readSubExpr());
E->setFileScope(Record.readInt());
+ E->setStorageClass(static_cast<StorageClass>(Record.readInt()));
+ E->setTSCSpec(static_cast<ThreadStorageClassSpecifier>(Record.readInt()));
+ E->setConstexpr(Record.readInt());
}
void ASTStmtReader::VisitExtVectorElementExpr(ExtVectorElementExpr *E) {
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 70477f4cf4001..f0ff9eb1ed80f 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1206,6 +1206,9 @@ void ASTStmtWriter::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
Record.AddTypeSourceInfo(E->getTypeSourceInfo());
Record.AddStmt(E->getInitializer());
Record.push_back(E->isFileScope());
+ Record.push_back(E->getStorageClass());
+ Record.push_back(E->getTSCSpec());
+ Record.push_back(E->isConstexpr());
Code = serialization::EXPR_COMPOUND_LITERAL;
}
diff --git a/clang/test/AST/c23-compound-literal-print.c b/clang/test/AST/c23-compound-literal-print.c
new file mode 100644
index 0000000000000..c8e4fb7d1e8e3
--- /dev/null
+++ b/clang/test/AST/c23-compound-literal-print.c
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -std=c23 -ast-print %s | FileCheck %s
+// RUN: %clang_cc1 -std=c23 -emit-pch -o %t %s
+// RUN: %clang_cc1 -std=c23 -include-pch %t -ast-print -x c /dev/null | FileCheck %s
+
+// CHECK-LABEL: int f1(void)
+// CHECK: return (constexpr int){1} + (static _Thread_local int){2} + (register int){3} + (constexpr const int){4};
+int f1(void) {
+ return (constexpr int){1} + (_Thread_local static int){2} + (register int){3} +
+ (constexpr const int){4};
+}
+
+// CHECK-LABEL: int f2(void)
+// CHECK: return (int[3]){1, 2, 3}[0] + (constexpr int[3]){1, 2, 3}[0];
+int f2(void) {
+ return (int[3]){1, 2, 3}[0] + (constexpr int[3]){1, 2, 3}[0];
+}
+
+typedef const int T;
+
+// CHECK-LABEL: int f3(void)
+// CHECK: return (constexpr T){1} + (constexpr int[]){1}[0] + (constexpr const int[]){1}[0] + (constexpr T[]){1}[0];
+int f3(void) {
+ return (constexpr T){1} + (constexpr int[]){1}[0] +
+ (constexpr const int[]){1}[0] + (constexpr T[]){1}[0];
+}
+
+// CHECK-LABEL: void f4(void)
+// CHECK: typedef typeof (*(int (*)[(static _Thread_local int){1}])0) T1;
+// CHECK: typedef typeof (*(int (*)[(register int){1}])0) T2;
+void f4(void) {
+ typedef typeof(*(int (*)[(static thread_local int){1}])0) T1;
+ typedef typeof(*(int (*)[(register int){1}])0) T2;
+}
diff --git a/clang/test/C/drs/dr3xx.c b/clang/test/C/drs/dr3xx.c
index 0f67470b013a5..e5d85dca78122 100644
--- a/clang/test/C/drs/dr3xx.c
+++ b/clang/test/C/drs/dr3xx.c
@@ -226,6 +226,7 @@ extern int dr339_v;
void *dr339 = &(int (*)[dr339_v]){ 0 }; /* c89only-warning {{variable length arrays are a C99 feature}}
c99andup-warning {{variable length array used}}
c89only-warning {{compound literals are a C99-specific feature}}
+ c23andup-error {{variably modified type declaration not allowed at file scope}}
*/
/* WG14 DR340: yes
diff --git a/clang/test/CodeGen/c23-compound-literal.c b/clang/test/CodeGen/c23-compound-literal.c
new file mode 100644
index 0000000000000..d255e72a23d1b
--- /dev/null
+++ b/clang/test/CodeGen/c23-compound-literal.c
@@ -0,0 +1,275 @@
+// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+struct S { int a; int b; };
+
+// CHECK-LABEL: define dso_local i32 @f1()
+// CHECK: %[[VALUE:.*]] = load i32, ptr @.compoundliteral, align 4
+// CHECK-NEXT: ret i32 %[[VALUE]]
+int f1(void) {
+ return (static int){42};
+}
+
+// CHECK-LABEL: define dso_local i32 @f2()
+// CHECK: %.compoundliteral = alloca i32
+// CHECK: store i32 7, ptr %.compoundliteral
+// CHECK-NEXT: %[[VALUE:.*]] = load i32, ptr %.compoundliteral, align 4
+// CHECK-NEXT: ret i32 %[[VALUE]]
+int f2(void) {
+ return (constexpr int){7};
+}
+
+// CHECK-LABEL: define dso_local ptr @f3()
+// CHECK: ret ptr @.compoundliteral.1
+const int *f3(void) {
+ return &(static constexpr int){15};
+}
+
+// CHECK-LABEL: define dso_local i32 @f4()
+// CHECK: [[ADDR:%.*]] = call align 4 ptr @llvm.threadlocal.address.p0(ptr align 4 @.compoundliteral.2)
+// CHECK-NEXT: %[[VALUE:.*]] = load i32, ptr [[ADDR]], align 4
+// CHECK-NEXT: ret i32 %[[VALUE]]
+int f4(void) {
+ return (thread_local static int){2};
+}
+
+// CHECK-LABEL: define dso_local ptr @f5()
+// CHECK: [[ADDR:%.*]] = call align 4 ptr @llvm.threadlocal.address.p0(ptr align 4 @.compoundliteral.3)
+// CHECK-NEXT: ret ptr [[ADDR]]
+int *f5(void) {
+ return &(thread_local static int){2};
+}
+
+// CHECK-LABEL: define dso_local i32 @f6()
+// CHECK: store ptr @.compoundliteral.4, ptr %a, align 8
+// CHECK-NEXT: %[[BASE:.*]] = load ptr, ptr %a, align 8
+// CHECK-NEXT: %[[MEMBER:.*]] = getelementptr inbounds nuw %struct.S, ptr %[[BASE]], i32 0, i32 0
+// CHECK-NEXT: %[[VALUE:.*]] = load i32, ptr %[[MEMBER]], align 4
+// CHECK-NEXT: ret i32 %[[VALUE]]
+int f6(void) {
+ struct S *a = &(static struct S){1, 2};
+ return a->a;
+}
+
+// CHECK-LABEL: define dso_local i64 @f7()
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %retval, ptr align 4 @.compoundliteral.5, i64 8, i1 true)
+// CHECK-NEXT: %[[VALUE:.*]] = load i64, ptr %retval, align 4
+// CHECK-NEXT: ret i64 %[[VALUE]]
+struct S f7(void) {
+ return (static volatile struct S){9, 10};
+}
+
+// CHECK-LABEL: define dso_local i64 @f8()
+// CHECK: [[ADDR:%.*]] = call align 4 ptr @llvm.threadlocal.address.p0(ptr align 4 @.compoundliteral.6)
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %retval, ptr align 4 [[ADDR]], i64 8, i1 true)
+// CHECK-NEXT: %[[VALUE:.*]] = load i64, ptr %retval, align 4
+// CHECK-NEXT: ret i64 %[[VALUE]]
+struct S f8(void) {
+ return (thread_local static volatile struct S){11, 12};
+}
+
+// CHECK-LABEL: define dso_local i64 @f9()
+// CHECK: %.compoundliteral = alloca %struct.S
+// CHECK: store i32 13
+// CHECK: store i32 14
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %retval, ptr align 4 %.compoundliteral, i64 8, i1 true)
+// CHECK-NEXT: %[[VALUE:.*]] = load i64, ptr %retval, align 4
+// CHECK-NEXT: ret i64 %[[VALUE]]
+struct S f9(void) {
+ return (register volatile struct S){13, 14};
+}
+
+// CHECK-LABEL: define dso_local i32 @f10()
+// CHECK: %.compoundliteral = alloca %struct.S
+// CHECK: store i32 3
+// CHECK: store i32 4
+// CHECK: store ptr %.compoundliteral, ptr %a, align 8
+// CHECK-NEXT: %[[BASE:.*]] = load ptr, ptr %a, align 8
+// CHECK-NEXT: %[[MEMBER:.*]] = getelementptr inbounds nuw %struct.S, ptr %[[BASE]], i32 0, i32 0
+// CHECK-NEXT: %[[VALUE:.*]] = load i32, ptr %[[MEMBER]], align 4
+// CHECK-NEXT: ret i32 %[[VALUE]]
+int f10(void) {
+ const struct S *a = &(constexpr struct S){3, 4};
+ return a->a;
+}
+
+// CHECK-LABEL: define dso_local i32 @f11()
+// CHECK: %[[VALUE:.*]] = load i32, ptr @.compoundliteral.7, align 4
+// CHECK-NEXT: ret i32 %[[VALUE]]
+int f11(void) {
+ return (static int[]){5, 6, 7}[0];
+}
+
+// CHECK-LABEL: define dso_local i32 @f12()
+// CHECK: %.compoundliteral = alloca [3 x i32]
+// CHECK: store i32 8, ptr %.compoundliteral
+// CHECK: store i32 9
+// CHECK: store i32 10
+// CHECK: %[[ELEMENT:.*]] = getelementptr inbounds [3 x i32], ptr %.compoundliteral, i64 0, i64 0
+// CHECK-NEXT: %[[VALUE:.*]] = load i32, ptr %[[ELEMENT]], align 4
+// CHECK-NEXT: ret i32 %[[VALUE]]
+int f12(void) {
+ return (constexpr int[]){8, 9, 10}[0];
+}
+
+// CHECK-LABEL: define dso_local i32 @f13()
+// CHECK: %.compoundliteral = alloca i32
+// CHECK: store i32 99, ptr %.compoundliteral
+// CHECK-NEXT: %[[VALUE:.*]] = load i32, ptr %.compoundliteral, align 4
+// CHECK-NEXT: ret i32 %[[VALUE]]
+int f13(void) {
+ return (register constexpr int){99};
+}
+
+// CHECK-LABEL: define dso_local i32 @f14()
+// CHECK: %a = alloca i32
+// CHECK: store i32 16, ptr %a
+// CHECK-NEXT: %[[VALUE:.*]] = load atomic i32, ptr %a seq_cst, align 4
+// CHECK-NEXT: ret i32 %[[VALUE]]
+int f14(void) {
+ register _Atomic int a = 16;
+ return a;
+}
+
+// CHECK-LABEL: define dso_local i32 @f15()
+// CHECK: %.compoundliteral = alloca i32
+// CHECK: store i32 16, ptr %.compoundliteral
+// CHECK-NEXT: %[[VALUE:.*]] = load atomic i32, ptr %.compoundliteral seq_cst, align 4
+// CHECK-NEXT: ret i32 %[[VALUE]]
+int f15(void) {
+ return (register _Atomic int){16};
+}
+
+// CHECK-LABEL: define dso_local i64 @f16()
+// CHECK: %a = alloca %struct.S, align 8
+// CHECK: store i32 17
+// CHECK: store i32 18
+// CHECK: %[[VALUE:.*]] = load atomic i64, ptr %a seq_cst, align 8
+// CHECK-NEXT: store i64 %[[VALUE]], ptr %retval, align 4
+// CHECK-NEXT: %[[RESULT:.*]] = load i64, ptr %retval, align 4
+// CHECK-NEXT: ret i64 %[[RESULT]]
+struct S f16(void) {
+ register _Atomic(struct S) a = {(struct S){17, 18}};
+ return a;
+}
+
+// CHECK-LABEL: define dso_local i64 @f17()
+// CHECK: %.compoundliteral = alloca %struct.S, align 8
+// CHECK: store i32 17
+// CHECK: store i32 18
+// CHECK: %[[VALUE:.*]] = load atomic i64, ptr %.compoundliteral seq_cst, align 8
+// CHECK-NEXT: store i64 %[[VALUE]], ptr %retval, align 4
+// CHECK-NEXT: %[[RESULT:.*]] = load i64, ptr %retval, align 4
+// CHECK-NEXT: ret i64 %[[RESULT]]
+struct S f17(void) {
+ return (register _Atomic(struct S)){(struct S){17, 18}};
+}
+
+// CHECK-LABEL: define dso_local i32 @f18()
+// CHECK: %.compoundliteral = alloca i32
+// CHECK: store i32 5, ptr %.compoundliteral
+// CHECK-NEXT: %[[VALUE:.*]] = load i32, ptr %.compoundliteral, align 4
+// CHECK-NEXT: ret i32 %[[VALUE]]
+int f18(void) {
+ return (int){5};
+}
+
+// CHECK-LABEL: define dso_local i32 @f19(i32 noundef %a)
+// CHECK: %[[OLD:.*]] = load i32, ptr %a.addr, align 4
+// CHECK-NEXT: %[[INC:.*]] = add nsw i32 %[[OLD]], 1
+// CHECK-NEXT: store i32 %[[INC]], ptr %a.addr, align 4
+// CHECK: load ptr, ptr @.compoundliteral.8, align 8
+// CHECK: %[[RESULT:.*]] = load i32, ptr %a.addr, align 4
+// CHECK-NEXT: ret i32 %[[RESULT]]
+int f19(int a) {
+ (void)(static int (*)[a++]){0};
+ return a;
+}
+
+// CHECK-LABEL: define dso_local i32 @f20(i32 noundef %a)
+// CHECK: %[[OLD:.*]] = load i32, ptr %a.addr, align 4
+// CHECK-NEXT: %[[INC:.*]] = add nsw i32 %[[OLD]], 1
+// CHECK-NEXT: store i32 %[[INC]], ptr %a.addr, align 4
+// CHECK: %[[ADDR:.*]] = call align 8 ptr @llvm.threadlocal.address.p0(ptr align 8 @.compoundliteral.9)
+// CHECK-NEXT: load ptr, ptr %[[ADDR]], align 8
+// CHECK: %[[RESULT:.*]] = load i32, ptr %a.addr, align 4
+// CHECK-NEXT: ret i32 %[[RESULT]]
+int f20(int a) {
+ (void)(thread_local static int (*)[a++]){0};
+ return a;
+}
+
+int f21(void);
+
+// CHECK-LABEL: define dso_local i32 @f22(
+// CHECK: %.compoundliteral = alloca i32
+// CHECK: [[CALL:%call]] = call i32 @f21()
+// CHECK-NEXT: store i32 [[CALL]], ptr %.compoundliteral
+// CHECK-NEXT: %[[BOUND:.*]] = load i32, ptr %.compoundliteral, align 4
+// CHECK-NEXT: zext i32 %[[BOUND]] to i64
+// CHECK: ret i32
+// CHECK-NEXT: }
+int f22(int a[(int){f21()}]) {
+ return a[0];
+}
+
+// CHECK-LABEL: define dso_local i32 @f23(
+// CHECK: %.compoundliteral = alloca i32
+// CHECK: [[CALL:%call]] = call i32 @f21()
+// CHECK-NEXT: store i32 [[CALL]], ptr %.compoundliteral
+// CHECK-NEXT: %[[BOUND:.*]] = load i32, ptr %.compoundliteral, align 4
+// CHECK-NEXT: zext i32 %[[BOUND]] to i64
+// CHECK: ret i32
+// CHECK-NEXT: }
+int f23(int a[(register int){f21()}]) {
+ return a[0];
+}
+
+// CHECK-LABEL: define dso_local i32 @f24(
+// CHECK: %[[BOUND:.*]] = load volatile i32, ptr @.compoundliteral.10, align 4
+// CHECK-NEXT: zext i32 %[[BOUND]] to i64
+// CHECK: ret i32
+// CHECK-NEXT: }
+int f24(int a[(static volatile int){13}]) {
+ return a[0];
+}
+
+// CHECK-LABEL: define dso_local i32 @f25(
+// CHECK: [[ADDR:%.*]] = call align 4 ptr @llvm.threadlocal.address.p0(ptr align 4 @.compoundliteral.11)
+// CHECK-NEXT: %[[BOUND:.*]] = load volatile i32, ptr [[ADDR]], align 4
+// CHECK-NEXT: zext i32 %[[BOUND]] to i64
+// CHECK: ret i32
+// CHECK-NEXT: }
+int f25(int a[(static thread_local volatile int){14}]) {
+ return a[0];
+}
+
+// CHECK-LABEL: define dso_local i32 @f26(
+// CHECK-NOT: call i32 @f21()
+// CHECK-NOT: load volatile i32
+// CHECK: ret i32 0
+int f26(int b(int a[((int){f21()} + (static volatile int){15})])) {
+ return 0;
+}
+
+// CHECK-LABEL: define dso_local i32 @f27(
+// CHECK: %.compoundliteral = alloca i32
+// CHECK: [[CALL:%call]] = call i32 @f21()
+// CHECK-NEXT: store i32 [[CALL]], ptr %.compoundliteral
+// CHECK-NEXT: load i32, ptr %.compoundliteral, align 4
+// CHECK-NEXT: ret i32 0
+int f27(int (*a(void))[((void)(int){f21()}, 1)]) {
+ return 0;
+}
+
+int f28(const int *);
+
+// CHECK-LABEL: define dso_local i32 @f29(
+// CHECK: %.compoundliteral = alloca i32
+// CHECK: store i32 3, ptr %.compoundliteral
+// CHECK-NEXT: %[[CALL:.*]] = call i32 @f28(ptr noundef %.compoundliteral)
+// CHECK-NEXT: zext i32 %[[CALL]] to i64
+// CHECK: ret i32
+// CHECK-NEXT: }
+int f29(int a[f28(&(constexpr int){3})]) {
+ return a[0];
+}
diff --git a/clang/test/CodeGenObjC/c23-compound-literal.m b/clang/test/CodeGenObjC/c23-compound-literal.m
new file mode 100644
index 0000000000000..245284cb23017
--- /dev/null
+++ b/clang/test/CodeGenObjC/c23-compound-literal.m
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -std=c23 -triple x86_64-apple-macosx10.15 -emit-llvm -fobjc-arc -disable-llvm-passes -o - %s | FileCheck %s
+
+typedef struct {
+ id a;
+} S;
+int f1(const S *);
+
+// CHECK-LABEL: define i32 @f2(
+// CHECK: %.compoundliteral = alloca %struct.S, align 8
+// CHECK: store ptr null, ptr %{{.*}}, align 8
+// CHECK: %[[CALL:.*]] = call i32 @f1(ptr noundef %.compoundliteral)
+// CHECK-NEXT: zext i32 %[[CALL]] to i64
+// CHECK: %[[RESULT:.*]] = load i32, ptr %{{.*}}, align 4
+// CHECK-NEXT: call void @__destructor_8_s0(ptr %.compoundliteral)
+// CHECK-NEXT: ret i32 %[[RESULT]]
+// CHECK-NEXT: }
+int f2(int a[f1(&(constexpr S){.a = 0})]) {
+ return a[0];
+}
diff --git a/clang/test/Parser/expressions.c b/clang/test/Parser/expressions.c
index 82556c091206f..2921158dd5f35 100644
--- a/clang/test/Parser/expressions.c
+++ b/clang/test/Parser/expressions.c
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c17 -fsyntax-only -verify=expected,c17 %s
+// RUN: %clang_cc1 -std=c23 -Wpre-c23-compat -fsyntax-only -verify=expected,c23 %s
+// RUN: %clang_cc1 -std=c23 -triple x86_64-apple-darwin10 -fsyntax-only -verify=expected,notls %s
void test1(void) {
if (sizeof (int){ 1}) {} // sizeof compound literal
@@ -73,3 +75,17 @@ void test8(void) {
callee(foobar, // expected-error {{use of undeclared identifier 'foobar'}}
fizbin); // expected-error {{use of undeclared identifier 'fizbin'}}
}
+
+void test9(void) {
+ (void)(static int){1}; // c17-error {{expected expression}} \
+ // c23-warning {{compound literal storage-class specifiers are incompatible with C standards before C23}}
+
+ (void)(register int){2}; // c17-error {{expected expression}} \
+ // c23-warning {{compound literal storage-class specifiers are incompatible with C standards before C23}}
+
+ (void)(constexpr int){3}; // c17-error {{use of undeclared identifier 'constexpr'}} \
+ // c23-warning {{compound literal storage-class specifiers are incompatible with C standards before C23}}
+
+ (void)(_Thread_local static int){4}; // c17-error {{expected expression}} notls-error {{thread-local storage is not supported for the current target}} \
+ // c23-warning {{compound literal storage-class specifiers are incompatible with C standards before C23}}
+}
diff --git a/clang/test/Sema/c23-compound-literal.c b/clang/test/Sema/c23-compound-literal.c
new file mode 100644
index 0000000000000..44759108bcd3a
--- /dev/null
+++ b/clang/test/Sema/c23-compound-literal.c
@@ -0,0 +1,343 @@
+// RUN: %clang_cc1 -std=c23 -triple x86_64-unknown-linux-gnu -verify -fsyntax-only %s
+// RUN: %clang_cc1 -std=c23 -triple x86_64-scei-ps4 -verify=expected,ps4 -fsyntax-only %s
+
+#define M static
+struct S { int a; char b; };
+int f1(void);
+
+void test1(void) {
+ (void)(constexpr int){1};
+ (void)&(static int){42};
+ (void)(register int){0};
+ (void)(static thread_local int){1};
+ (void)(constexpr struct S){1, 'a'};
+ (void)(static struct S){2, 'b'};
+ (void)(register struct S){3, 'c'};
+}
+
+void test2(void) {
+ (void)(static constexpr int){1};
+ (void)(constexpr static int){2};
+ (void)(static thread_local int){3};
+ (void)(thread_local static int){4};
+ (void)(constexpr register int){5};
+ (void)(constexpr static thread_local int){6}; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
+}
+
+void test3(void) {
+ (void)(static static int){1}; // expected-warning {{duplicate 'static' declaration specifier}}
+ (void)(constexpr constexpr int){2}; // expected-warning {{duplicate 'constexpr' declaration specifier}}
+ (void)(register register int){3}; // expected-warning {{duplicate 'register' declaration specifier}}
+ (void)(thread_local thread_local int){4}; // expected-warning {{duplicate '_Thread_local' declaration specifier}} expected-error {{compound literal with 'thread_local' storage duration at block scope must also specify 'static'}}
+}
+
+void test4(void) {
+ (void)(register static int){1}; // expected-error {{cannot combine with previous 'register' declaration specifier}}
+ (void)(register thread_local int){2}; // expected-error {{cannot combine with previous 'register' declaration specifier}}
+ (void)(register constexpr int){3};
+ (void)(static register int){4}; // expected-error {{cannot combine with previous 'static' declaration specifier}}
+ (void)(register _Atomic int){5};
+}
+
+void test5(void) {
+ (void)&(thread_local int){1}; // expected-error {{compound literal with 'thread_local' storage duration at block scope must also specify 'static'}}
+}
+
+int *a1 = &(register int){42}; // expected-error {{file-scope compound literal specifies 'register'}}
+
+void test6(void) {
+ (void)(constexpr volatile int){1}; // expected-error {{constexpr compound literal cannot have type 'const volatile int'}}
+ (void)(constexpr _Atomic int){1}; // expected-error {{constexpr compound literal cannot have type 'const _Atomic(int)'}}
+
+ int c;
+ (void)(constexpr int[c]){0}; // expected-error {{constexpr compound literal cannot have type 'const int[c]'}}
+}
+
+void test7(void) {
+ (void)(constexpr int){f1()}; // expected-error {{initializer of compound literal must be a constant expression}}
+ (void)(static int){f1()}; // expected-error {{initializer element is not a compile-time constant}}
+ (void)(register constexpr int){f1()}; // expected-error {{initializer of compound literal must be a constant expression}}
+ (void)(constexpr int){1 / 0};
+ // expected-error at -1 {{initializer of compound literal must be a constant expression}}
+ // expected-note at -2 {{division by zero}}
+ // expected-warning at -3 {{division by zero is undefined}}
+}
+
+const int a2 = 1;
+const double a3 = 1.0;
+
+void test8(void) {
+ (void)(constexpr int){a2}; // expected-error {{initializer of compound literal must be a constant expression}}
+ (void)(constexpr double){a3}; // expected-error {{initializer of compound literal must be a constant expression}}
+
+ const int a = 2;
+ (void)(constexpr int){a}; // expected-error {{initializer of compound literal must be a constant expression}}
+
+ struct S1 {
+ int a;
+ };
+
+ (void)(constexpr int){(constexpr int){1}};
+ (void)(constexpr struct S1){.a = (constexpr int){1}};
+ (void)(constexpr int){(int){1}};
+ (void)(constexpr int){(static int){1}};
+}
+
+void test9(void) {
+ int *a = &(register int){1}; // expected-error {{address of register compound literal requested}}
+ int *b = (register int[1]){1}; // expected-error {{address of register compound literal requested}}
+ struct S2 { int a; };
+ int *c = &(register struct S2){1}.a; // expected-error {{address of register compound literal requested}}
+ double *d = &__real__ (register _Complex double){1}; // expected-error {{address of register compound literal requested}}
+ double *e = &__imag__ (register _Complex double){1}; // expected-error {{address of register compound literal requested}}
+ int *f = &_Generic(0, int: (register int){1}); // expected-error {{address of register compound literal requested}}
+ int *g = _Generic(0, int: (register int[1]){1}); // expected-error {{address of register compound literal requested}}
+ int *h = &_Generic(0, int: (register struct S2){1}).a; // expected-error {{address of register compound literal requested}}
+ int *i = &__extension__ (register int){1}; // expected-error {{address of register compound literal requested}}
+}
+
+int a5[1];
+struct S3 {
+ int *a;
+};
+struct S4 {
+ int a[1];
+};
+
+int *test10(void) {
+ return &((register struct S3){a5}.a[0]);
+}
+
+int *test11(void) {
+ return &(register struct S4){{1}}.a[0]; // expected-error {{address of register compound literal requested}}
+}
+
+enum { E1 = (constexpr int){42} };
+enum { E2 = (constexpr int){} };
+static int a6[(constexpr int){10}];
+static int a7[(constexpr int){} + 1];
+static const int *a8 = &(constexpr int){5};
+static int a9 = (constexpr int){7};
+static_assert(!(constexpr int){});
+
+struct S5 { int a; };
+enum { E3 = (constexpr struct S5){42}.a };
+enum { E4 = (int)(constexpr double){1.0} };
+
+union U1 {
+ int a;
+ long b;
+};
+enum { E5 = (constexpr union U1){.a = 9}.a };
+enum {
+ E6 = (constexpr union U1){.a = 9}.b
+ // expected-error at -1 {{expression is not an integer constant expression}}
+ // expected-note at -2 {{read of member 'b' of union with active member 'a' is not allowed in a constant expression}}
+};
+
+void test12(void) {
+ static int a = (constexpr int){8};
+ int b[(constexpr int){3}];
+ switch ((constexpr int){1}) {
+ case (constexpr int){1}:
+ break;
+ }
+}
+
+void test13(void) {
+ (constexpr int){1} = 2; // expected-error {{read-only variable is not assignable}}
+ (constexpr int[1]){1}[0] = 2; // expected-error {{read-only variable is not assignable}}
+ (constexpr struct S){1, 'a'}.a = 2; // expected-error {{read-only variable is not assignable}}
+}
+
+int *a10 = &(static int){100};
+int *a11 = &(thread_local int){200}; // expected-error {{initializer element is not a compile-time constant}}
+const int *a12 = &(constexpr int){300};
+
+void test14(void) {
+ const int *a = (constexpr int[]){1, 2, 3};
+ int *b = (static int[]){4, 5, 6};
+}
+
+int a13 = (thread_local int){1}; // expected-error {{initializer element is not a compile-time constant}}
+int a14 = (thread_local static int){1}; // expected-error {{initializer element is not a compile-time constant}}
+int a15 = 1 + (thread_local int){1}; // expected-error {{initializer element is not a compile-time constant}}
+int a16 = +(thread_local int){1}; // expected-error {{initializer element is not a compile-time constant}}
+int a17 = (thread_local struct S5){1}.a; // expected-error {{initializer element is not a compile-time constant}}
+int a18 = *(thread_local int[1]){1}; // expected-error {{initializer element is not a compile-time constant}}
+
+struct S7 {
+ unsigned char a;
+};
+
+void test15(void) {
+ static int a;
+ (void)(constexpr int *){&a}; // expected-error {{constexpr pointer initializer is not null}}
+ (void)(constexpr struct S7){-1}; // expected-error {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned char'}}
+}
+
+int a19[3];
+static int *a20 = a19 + (constexpr int){1};
+static int *a21 = (constexpr int){0}; // expected-warning {{expression which evaluates to zero treated as a null pointer constant of type 'int *'}}
+static int *a22 = (constexpr int *){0};
+
+struct S8 {
+ int *restrict a;
+};
+void test16(void) {
+ (void)(constexpr struct S8){0}; // expected-error {{constexpr compound literal cannot have type 'int *restrict'}}
+}
+
+void test17(void) {
+ (void)(constexpr char[]){"\xFF"};
+ (void)(constexpr unsigned char[]){"\xFF"}; // expected-error {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
+}
+
+void test18(void) {
+ static const int *a = &(constexpr int){1}; // expected-error {{initializer element is not a compile-time constant}}
+ static const int *b = &(static constexpr int){1};
+}
+
+void f2(int a[sizeof((static int){1})]);
+void f3(int a[sizeof((register int){1})]);
+void f4(int a[sizeof((constexpr int){1})]);
+void f5(int a[sizeof((thread_local static int){1})]);
+int f6(void);
+
+void f7(int a[(constexpr int){f6()}]); // expected-error {{initializer of compound literal must be a constant expression}}
+void f8(int a[(static int){f6()}]); // expected-error {{initializer element is not a compile-time constant}}
+
+typedef void F1(int a[(int){f6()}]);
+typedef void F2(int a[(thread_local int){1}]); // expected-error {{compound literal with 'thread_local' storage duration at block scope must also specify 'static'}}
+void (*a23)(int a[(register int){f6()}]);
+
+typedef int T __attribute__((address_space(1)));
+void f9(int a[sizeof((T){0})]); // expected-error {{compound literal in function scope may not be qualified with an address space}}
+int f10(int a[sizeof((T){0})]) { // expected-error {{compound literal in function scope may not be qualified with an address space}}
+ return a[0];
+}
+
+void test19(void) {
+ (static T){0}; // expected-error {{compound literal in function scope may not be qualified with an address space}}
+}
+
+int f11(
+ typeof(sizeof((register T){0})) a(void)) { // expected-error {{compound literal in function scope may not be qualified with an address space}}
+ return a();
+}
+
+inline int f12(void) { // expected-note {{use 'static' to give inline function 'f12' internal linkage}}
+ return (static int){1}; // expected-warning {{non-constant static local variable in inline function may be different in different files}}
+}
+
+inline int f13(int a[(static int){1}]) { // expected-warning {{non-constant static local variable in inline function may be different in different files}} \
+ // expected-note {{use 'static' to give inline function 'f13' internal linkage}}
+ return a[0];
+}
+
+inline int f14(int a[(static int){1}]);
+
+inline int f15(int a[(static const int){1}]) {
+ return a[0];
+}
+
+extern inline int f16(int a[(static int){1}]) {
+ return a[0];
+}
+
+static inline int f17(int a[(static int){1}]) {
+ return a[0];
+}
+
+inline int f18(int b(int a[sizeof((static int){1})])) {
+ return 0;
+}
+
+inline int f19(int (*a(void))[sizeof((static int){1})]) { // expected-warning {{non-constant static local variable in inline function may be different in different files}}
+ // expected-note at -1 {{use 'static' to give inline function 'f19' internal linkage}}
+ return 0;
+}
+
+inline typeof((static int){1}) f20(void) {
+ return 0;
+}
+typeof((register int){1}) f21(void); // expected-error {{file-scope compound literal specifies 'register'}}
+
+inline int f22(int a) __attribute__((enable_if((static int){1}, "enabled"))) { // expected-warning {{non-constant static local variable in inline function may be different in different files}} \
+ // expected-note {{use 'static' to give inline function 'f22' internal linkage}}
+ return a;
+}
+
+int f23(int a) __attribute__((enable_if(sizeof((register T){0}), "enabled"))) { // expected-error {{compound literal in function scope may not be qualified with an address space}}
+ return a;
+}
+
+int f24(int a) __attribute__((enable_if( sizeof((register T){0}), "enabled"))); // expected-error {{compound literal in function scope may not be qualified with an address space}}
+int f24(int a) {
+ return a;
+}
+
+int a24;
+int a25 = sizeof((thread_local static int (*)[a24]){0}); // expected-error {{variably modified type declaration not allowed at file scope}}
+
+int *f25(void) {
+ return &(static int){1};
+}
+
+int *f26(void) {
+ return &(thread_local static int){1};
+}
+
+int *f27(void) {
+ return &(int){1}; // expected-warning {{address of stack memory associated with compound literal '{1}' returned}}
+}
+
+const int *f28(void) {
+ return &(constexpr int){1}; // expected-warning {{address of stack memory associated with compound literal '{1}' returned}}
+}
+
+inline int f29(void) { // expected-note {{use 'static' to give inline function 'f29' internal linkage}}
+ return (thread_local static int){1}; // expected-warning {{non-constant static local variable in inline function may be different in different files}}
+}
+
+void test20(void) {
+ (void)(M const int){1};
+ (void)(constexpr unsigned long){2};
+ (void)sizeof (static int){1};
+ (void)sizeof (static thread_local int){1};
+ (void)sizeof (constexpr int){1};
+}
+
+void test21(void) {
+ (void)(const static int){1}; // expected-error {{type name does not allow storage class to be specified}}
+ (void)(const int register){1}; // expected-error {{type name does not allow storage class to be specified}}
+}
+
+void test22(void) {
+ (void)(auto int){1}; // expected-error {{expected expression}}
+ (void)(extern int){2}; // expected-error {{expected expression}}
+ (void)(typedef int){3}; // expected-error {{expected expression}}
+ (void)(__auto_type int){4}; // expected-error {{expected expression}}
+ (void)(__thread int){5}; // expected-error {{expected expression}}
+}
+
+void test23(void) {
+ (void)(static int constexpr){1}; // expected-error {{type name does not allow constexpr specifier to be specified}}
+ (void)(static int thread_local){1}; // expected-error {{type name does not allow storage class to be specified}}
+ (void)(static int _Thread_local){1}; // expected-error {{type name does not allow storage class to be specified}}
+ (void)(static int __thread){1}; // expected-error {{type name does not allow storage class to be specified}}
+}
+
+void test24(void) {
+ (void)(static int)1; // expected-error {{type name does not allow storage class to be specified}}
+ (void)sizeof(register int); // expected-error {{type name does not allow storage class to be specified}}
+ (void)_Alignof(thread_local int); // expected-error {{type name does not allow storage class to be specified}}
+ (void)(constexpr int)1; // expected-error {{type name does not allow constexpr specifier to be specified}}
+}
+
+int a26 = (static)3; // expected-error {{type name requires a specifier or qualifier}}
+
+typedef int A __attribute__((aligned(64)));
+A *f30(void) {
+ return &(thread_local static A){1}; // ps4-error {{alignment (64) of thread-local compound literal is greater than the maximum supported alignment (32) for thread-local storage on this target}}
+}
diff --git a/clang/test/Sema/c23-compound-literal.m b/clang/test/Sema/c23-compound-literal.m
new file mode 100644
index 0000000000000..142e5a49499eb
--- /dev/null
+++ b/clang/test/Sema/c23-compound-literal.m
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -std=c23 -fobjc-arc -verify %s
+
+typedef struct {
+ id a;
+} S;
+
+void test1(void) {
+ (void)(thread_local static __unsafe_unretained id){0};
+ (void)(thread_local static id){0}; // expected-error {{thread-local variable has non-trivial ownership: type is '__strong id'}}
+ (void)(thread_local static S){0}; // expected-error {{type of thread-local variable has non-trivial destruction}}
+}
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index 0a9b5724a8343..936e0a3a32e19 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -22,7 +22,7 @@ struct S3 {
constexpr; // expected-error {{'constexpr' can only be used in variable declarations}}
constexpr int V1 = 3;
constexpr float V2 = 7.0;
-int V3 = (constexpr)3; // expected-error {{expected expression}}
+int V3 = (constexpr)3; // expected-error {{type name requires a specifier or qualifier}}
void f2() {
constexpr int a = 0;
>From f7af8028bf85cdc59b72cdeeb89fa17379910742 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Jul 2026 10:04:08 +0300
Subject: [PATCH 2/4] use hasGlobalStorage for compound literal emission
---
clang/lib/CodeGen/CGExpr.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index fadf00a606f3e..705d2dbb49a80 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5993,7 +5993,7 @@ CodeGenFunction::EmitLValueForFieldInitialization(LValue Base,
}
LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){
- if (E->isFileScope() || E->hasGlobalStorage()) {
+ if (E->hasGlobalStorage()) {
if (E->getType()->isVariablyModifiedType())
EmitVariablyModifiedType(E->getType());
>From 6ac710f83045b359a0ca12d448480edcd8e418df Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Jul 2026 11:32:54 +0300
Subject: [PATCH 3/4] parse all storage-class specifiers in compound literals
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 ++
clang/lib/Parse/ParseExpr.cpp | 15 +++++++++++++++
clang/lib/Sema/SemaExpr.cpp | 11 +++++++++--
clang/test/Sema/c23-compound-literal.c | 6 +++---
4 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 85528249c4be4..19d086abd8c0e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3183,6 +3183,8 @@ def err_register_compound_literal_file_scope : Error<
def err_thread_local_compound_literal_without_static : Error<
"compound literal with 'thread_local' storage duration at block scope must "
"also specify 'static'">;
+def err_compound_literal_invalid_storage_class : Error<
+ "storage class specifier '%0' is not permitted in a compound literal">;
def err_constexpr_compound_literal_invalid_type : Error<
"constexpr compound literal cannot have type %0">;
def err_compound_literal_initializer_not_constant : Error<
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 55e1d489efb39..9ae51290fa0ad 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -2660,11 +2660,14 @@ bool Parser::isCompoundLiteralStorageClassSpecifier(const Token &Tok) const {
if (!getLangOpts().C23)
return false;
switch (Tok.getKind()) {
+ case tok::kw_auto:
case tok::kw_constexpr:
+ case tok::kw_extern:
case tok::kw_register:
case tok::kw_static:
case tok::kw_thread_local:
case tok::kw__Thread_local:
+ case tok::kw_typedef:
return true;
default:
return false;
@@ -2680,10 +2683,22 @@ void Parser::ParseCompoundLiteralStorageClassSpecifiers(DeclSpec &DS) {
unsigned DiagID = 0;
bool IsInvalid = false;
switch (Tok.getKind()) {
+ case tok::kw_typedef:
+ IsInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_typedef, Loc,
+ PrevSpec, DiagID, Policy);
+ break;
+ case tok::kw_extern:
+ IsInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_extern, Loc,
+ PrevSpec, DiagID, Policy);
+ break;
case tok::kw_static:
IsInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_static, Loc,
PrevSpec, DiagID, Policy);
break;
+ case tok::kw_auto:
+ IsInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc,
+ PrevSpec, DiagID, Policy);
+ break;
case tok::kw_register:
IsInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_register, Loc,
PrevSpec, DiagID, Policy);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b5f19964cbefd..9d2c027e29fa5 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7533,10 +7533,17 @@ ExprResult Sema::ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty,
SourceLocation ThreadStorageClassLoc;
SourceLocation ConstexprLoc;
if (DS) {
- if (DS->getStorageClassSpec() == DeclSpec::SCS_static)
+ DeclSpec::SCS StorageClassSpec = DS->getStorageClassSpec();
+ if (StorageClassSpec == DeclSpec::SCS_static)
SC = SC_Static;
- else if (DS->getStorageClassSpec() == DeclSpec::SCS_register)
+ else if (StorageClassSpec == DeclSpec::SCS_register)
SC = SC_Register;
+ else if (StorageClassSpec != DeclSpec::SCS_unspecified) {
+ Diag(DS->getStorageClassSpecLoc(),
+ diag::err_compound_literal_invalid_storage_class)
+ << DeclSpec::getSpecifierName(StorageClassSpec);
+ return ExprError();
+ }
TSC = DS->getThreadStorageClassSpec();
ConstexprKind = DS->getConstexprSpecifier();
diff --git a/clang/test/Sema/c23-compound-literal.c b/clang/test/Sema/c23-compound-literal.c
index 44759108bcd3a..187ea81608b55 100644
--- a/clang/test/Sema/c23-compound-literal.c
+++ b/clang/test/Sema/c23-compound-literal.c
@@ -314,9 +314,9 @@ void test21(void) {
}
void test22(void) {
- (void)(auto int){1}; // expected-error {{expected expression}}
- (void)(extern int){2}; // expected-error {{expected expression}}
- (void)(typedef int){3}; // expected-error {{expected expression}}
+ (void)(auto int){1}; // expected-error {{storage class specifier 'auto' is not permitted in a compound literal}}
+ (void)(extern int){2}; // expected-error {{storage class specifier 'extern' is not permitted in a compound literal}}
+ (void)(typedef int){3}; // expected-error {{storage class specifier 'typedef' is not permitted in a compound literal}}
(void)(__auto_type int){4}; // expected-error {{expected expression}}
(void)(__thread int){5}; // expected-error {{expected expression}}
}
>From 58a103790483ce9d7a688b84fd62d3c1564764cb Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Jul 2026 13:25:41 +0300
Subject: [PATCH 4/4] fix compound literal aggrs emit
---
clang/lib/CodeGen/CGExprAgg.cpp | 13 ++++++-------
clang/test/CodeGen/c23-compound-literal.c | 15 +++++++++++++--
2 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index c1309c456d4e3..a54ea7c599b1a 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -789,15 +789,14 @@ void AggExprEmitter::VisitOpaqueValueExpr(OpaqueValueExpr *e) {
}
void AggExprEmitter::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
- if (E->hasGlobalStorage() || E->getType()->isAtomicType() ||
- (E->getStorageClass() == SC_Register &&
- E->getType().isVolatileQualified()) ||
- Dest.isPotentiallyAliased()) {
+ QualType Ty = E->getType();
+ if (Dest.isPotentiallyAliased() || E->hasGlobalStorage() ||
+ Ty->isAtomicType() || Ty.isVolatileQualified()) {
EmitAggLoadOfLValue(E);
return;
}
- AggValueSlot Slot = EnsureSlot(E->getType());
+ AggValueSlot Slot = EnsureSlot(Ty);
// Block-scope compound literals are destroyed at the end of the enclosing
// scope in C.
@@ -809,9 +808,9 @@ void AggExprEmitter::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
CGF.EmitAggExpr(E->getInitializer(), Slot);
if (Destruct)
- if (QualType::DestructionKind DtorKind = E->getType().isDestructedType())
+ if (QualType::DestructionKind DtorKind = Ty.isDestructedType())
CGF.pushLifetimeExtendedDestroy(
- CGF.getCleanupKind(DtorKind), Slot.getAddress(), E->getType(),
+ CGF.getCleanupKind(DtorKind), Slot.getAddress(), Ty,
CGF.getDestroyer(DtorKind), DtorKind & EHCleanup);
}
diff --git a/clang/test/CodeGen/c23-compound-literal.c b/clang/test/CodeGen/c23-compound-literal.c
index d255e72a23d1b..3f542021f5e21 100644
--- a/clang/test/CodeGen/c23-compound-literal.c
+++ b/clang/test/CodeGen/c23-compound-literal.c
@@ -51,11 +51,11 @@ int f6(void) {
}
// CHECK-LABEL: define dso_local i64 @f7()
-// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %retval, ptr align 4 @.compoundliteral.5, i64 8, i1 true)
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %retval, ptr align 4 @.compoundliteral.5, i64 8, i1 false)
// CHECK-NEXT: %[[VALUE:.*]] = load i64, ptr %retval, align 4
// CHECK-NEXT: ret i64 %[[VALUE]]
struct S f7(void) {
- return (static volatile struct S){9, 10};
+ return (static struct S){9, 10};
}
// CHECK-LABEL: define dso_local i64 @f8()
@@ -273,3 +273,14 @@ int f28(const int *);
int f29(int a[f28(&(constexpr int){3})]) {
return a[0];
}
+
+// CHECK-LABEL: define dso_local i64 @f30()
+// CHECK: %.compoundliteral = alloca %struct.S
+// CHECK: store i32 19
+// CHECK: store i32 20
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %retval, ptr align 4 %.compoundliteral, i64 8, i1 true)
+// CHECK-NEXT: %[[VALUE:.*]] = load i64, ptr %retval, align 4
+// CHECK-NEXT: ret i64 %[[VALUE]]
+struct S f30(void) {
+ return (volatile struct S){19, 20};
+}
More information about the cfe-commits
mailing list