aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clang/docs/ReleaseNotes.rst3
-rw-r--r--clang/include/clang/AST/Expr.h11
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td12
-rw-r--r--clang/include/clang/Basic/TokenKinds.def2
-rw-r--r--clang/lib/AST/Decl.cpp25
-rw-r--r--clang/lib/AST/ExprConstant.cpp10
-rw-r--r--clang/lib/Parse/ParseDecl.cpp2
-rw-r--r--clang/lib/Sema/DeclSpec.cpp14
-rw-r--r--clang/lib/Sema/SemaDecl.cpp79
-rw-r--r--clang/lib/Sema/SemaInit.cpp119
-rw-r--r--clang/lib/Sema/SemaOverload.cpp65
-rw-r--r--clang/test/C/C2x/n3018.c87
-rw-r--r--clang/test/Parser/c23-constexpr.c15
-rw-r--r--clang/test/Sema/constexpr.c359
-rw-r--r--clang/www/c_status.html2
15 files changed, 766 insertions, 39 deletions
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b074055..0ff4a93 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -143,6 +143,9 @@ C23 Feature Support
macros typically exposed from ``<inttypes.h>``, such as ``PRIb8``.
(`#81896: <https://github.com/llvm/llvm-project/issues/81896>`_).
+- Clang now supports `N3018 The constexpr specifier for object definitions`
+ <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3018.htm>`_.
+
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 bf0622b..f7857d6 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -1875,6 +1875,17 @@ public:
llvm_unreachable("Unsupported character width!");
}
+ // Get code unit but preserve sign info.
+ int64_t getCodeUnitS(size_t I, uint64_t BitWidth) const {
+ int64_t V = getCodeUnit(I);
+ if (isOrdinary() || isWide()) {
+ unsigned Width = getCharByteWidth() * BitWidth;
+ llvm::APInt AInt(Width, (uint64_t)V);
+ V = AInt.getSExtValue();
+ }
+ return V;
+ }
+
unsigned getByteLength() const { return getCharByteWidth() * getLength(); }
unsigned getLength() const { return *getTrailingObjects<unsigned>(); }
unsigned getCharByteWidth() const { return StringLiteralBits.CharByteWidth; }
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 708b3c7..b007ff7 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2946,6 +2946,18 @@ def warn_private_extern : Warning<
def note_private_extern : Note<
"use __attribute__((visibility(\"hidden\"))) attribute instead">;
+// C23 constexpr
+def err_c23_constexpr_not_variable : Error<
+ "'constexpr' can only be used in variable declarations">;
+def err_c23_constexpr_invalid_type : Error<
+ "constexpr variable cannot have type %0">;
+def err_c23_constexpr_init_not_representable : Error<
+ "constexpr initializer evaluates to %0 which is not exactly representable in type %1">;
+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">;
+
// C++ Concepts
def err_concept_decls_may_only_appear_in_global_namespace_scope : Error<
"concept declarations may only appear in global or namespace scope">;
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 1d16e48..3a96f8a 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -393,7 +393,7 @@ CXX11_KEYWORD(alignas , KEYC23)
CXX11_UNARY_EXPR_OR_TYPE_TRAIT(alignof, AlignOf, KEYC23)
CXX11_KEYWORD(char16_t , KEYNOMS18)
CXX11_KEYWORD(char32_t , KEYNOMS18)
-CXX11_KEYWORD(constexpr , 0)
+CXX11_KEYWORD(constexpr , KEYC23)
CXX11_KEYWORD(decltype , 0)
CXX11_KEYWORD(noexcept , 0)
CXX11_KEYWORD(nullptr , KEYC23)
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 57a92357..59c039f 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2465,7 +2465,7 @@ bool VarDecl::mightBeUsableInConstantExpressions(const ASTContext &C) const {
// OpenCL permits const integral variables to be used in constant
// expressions, like in C++98.
- if (!Lang.CPlusPlus && !Lang.OpenCL)
+ if (!Lang.CPlusPlus && !Lang.OpenCL && !Lang.C23)
return false;
// Function parameters are never usable in constant expressions.
@@ -2487,14 +2487,19 @@ bool VarDecl::mightBeUsableInConstantExpressions(const ASTContext &C) const {
if (!getType().isConstant(C) || getType().isVolatileQualified())
return false;
- // In C++, const, non-volatile variables of integral or enumeration types
- // can be used in constant expressions.
- if (getType()->isIntegralOrEnumerationType())
+ // In C++, but not in C, const, non-volatile variables of integral or
+ // enumeration types can be used in constant expressions.
+ if (getType()->isIntegralOrEnumerationType() && !Lang.C23)
return true;
+ // C23 6.6p7: An identifier that is:
+ // ...
+ // - declared with storage-class specifier constexpr and has an object type,
+ // is a named constant, ... such a named constant is a constant expression
+ // with the type and value of the declared object.
// Additionally, in C++11, non-volatile constexpr variables can be used in
// constant expressions.
- return Lang.CPlusPlus11 && isConstexpr();
+ return (Lang.CPlusPlus11 || Lang.C23) && isConstexpr();
}
bool VarDecl::isUsableInConstantExpressions(const ASTContext &Context) const {
@@ -2572,11 +2577,11 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes,
IsConstantInitialization);
- // In C++, this isn't a constant initializer if we produced notes. In that
+ // In C++/C23, this isn't a constant initializer if we produced notes. In that
// case, we can't keep the result, because it may only be correct under the
// assumption that the initializer is a constant context.
- if (IsConstantInitialization && Ctx.getLangOpts().CPlusPlus &&
- !Notes.empty())
+ if (IsConstantInitialization &&
+ (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23) && !Notes.empty())
Result = false;
// Ensure the computed APValue is cleaned up later if evaluation succeeded,
@@ -2634,7 +2639,9 @@ bool VarDecl::checkForConstantInitialization(
// std::is_constant_evaluated()).
assert(!Eval->WasEvaluated &&
"already evaluated var value before checking for constant init");
- assert(getASTContext().getLangOpts().CPlusPlus && "only meaningful in C++");
+ assert((getASTContext().getLangOpts().CPlusPlus ||
+ getASTContext().getLangOpts().C23) &&
+ "only meaningful in C++/C23");
assert(!getInit()->isValueDependent());
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4b6ccafb..d8ca357 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -4133,6 +4133,10 @@ 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.
@@ -4152,6 +4156,9 @@ 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) {
+ Info.FFDiag(E);
+ return CompleteObject();
} else if (BaseType->isIntegralOrEnumerationType()) {
if (!IsConstant) {
if (!IsAccess)
@@ -15826,7 +15833,8 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
EStatus.Diag = &Notes;
EvalInfo Info(Ctx, EStatus,
- (IsConstantInitialization && Ctx.getLangOpts().CPlusPlus)
+ (IsConstantInitialization &&
+ (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
? EvalInfo::EM_ConstantExpression
: EvalInfo::EM_ConstantFold);
Info.setEvaluatingDecl(VD, Value);
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index ccbfea6..81f1c71 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4265,6 +4265,8 @@ void Parser::ParseDeclarationSpecifiers(
// constexpr, consteval, constinit specifiers
case tok::kw_constexpr:
+ if (getLangOpts().C23)
+ Diag(Tok, diag::warn_c23_compat_keyword) << Tok.getName();
isInvalid = DS.SetConstexprSpec(ConstexprSpecKind::Constexpr, Loc,
PrevSpec, DiagID);
break;
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index aede602..b79683b 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -1377,6 +1377,20 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) {
ThreadStorageClassSpec = TSCS_unspecified;
ThreadStorageClassSpecLoc = SourceLocation();
}
+ if (S.getLangOpts().C23 &&
+ getConstexprSpecifier() == ConstexprSpecKind::Constexpr) {
+ S.Diag(ConstexprLoc, diag::err_invalid_decl_spec_combination)
+ << DeclSpec::getSpecifierName(getThreadStorageClassSpec())
+ << SourceRange(getThreadStorageClassSpecLoc());
+ }
+ }
+
+ if (S.getLangOpts().C23 &&
+ getConstexprSpecifier() == ConstexprSpecKind::Constexpr &&
+ StorageClassSpec == SCS_extern) {
+ S.Diag(ConstexprLoc, diag::err_invalid_decl_spec_combination)
+ << DeclSpec::getSpecifierName(getStorageClassSpec())
+ << SourceRange(getStorageClassSpecLoc());
}
// If no type specifier was provided and we're parsing a language where
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 8fcaf6a..210e283 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5147,6 +5147,8 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_tag)
<< GetDiagnosticTypeSpecifierID(DS)
<< static_cast<int>(DS.getConstexprSpecifier());
+ else if (getLangOpts().C23)
+ Diag(DS.getConstexprSpecLoc(), diag::err_c23_constexpr_not_variable);
else
Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_wrong_decl_kind)
<< static_cast<int>(DS.getConstexprSpecifier());
@@ -8649,6 +8651,38 @@ 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);
+ // 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;
+ 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);
+
+ if (CanonT->isAtomicType() || CanonT.isVolatileQualified() ||
+ CanonT.isRestrictQualified()) {
+ SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T;
+ return true;
+ }
+
+ if (CanonT->isRecordType()) {
+ const RecordDecl *RD = CanonT->getAsRecordDecl();
+ if (llvm::any_of(RD->fields(), [&SemaRef, VarLoc](const FieldDecl *F) {
+ return CheckC23ConstexprVarType(SemaRef, VarLoc, F->getType());
+ }))
+ return true;
+ }
+
+ return false;
+}
+
void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
// If the decl is already known invalid, don't check it.
if (NewVD->isInvalidDecl())
@@ -8899,6 +8933,12 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) {
return;
}
+ if (getLangOpts().C23 && NewVD->isConstexpr() &&
+ CheckC23ConstexprVarType(*this, NewVD->getLocation(), T)) {
+ NewVD->setInvalidDecl();
+ return;
+ }
+
if (NewVD->isConstexpr() && !T->isDependentType() &&
RequireLiteralType(NewVD->getLocation(), T,
diag::err_constexpr_var_non_literal)) {
@@ -9281,6 +9321,22 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
FunctionDecl *NewFD = nullptr;
bool isInline = D.getDeclSpec().isInlineSpecified();
+ ConstexprSpecKind ConstexprKind = D.getDeclSpec().getConstexprSpecifier();
+ if (ConstexprKind == ConstexprSpecKind::Constinit ||
+ (SemaRef.getLangOpts().C23 &&
+ ConstexprKind == ConstexprSpecKind::Constexpr)) {
+
+ if (SemaRef.getLangOpts().C23)
+ SemaRef.Diag(D.getDeclSpec().getConstexprSpecLoc(),
+ diag::err_c23_constexpr_not_variable);
+ else
+ SemaRef.Diag(D.getDeclSpec().getConstexprSpecLoc(),
+ diag::err_constexpr_wrong_decl_kind)
+ << static_cast<int>(ConstexprKind);
+ ConstexprKind = ConstexprSpecKind::Unspecified;
+ D.getMutableDeclSpec().ClearConstexprSpec();
+ }
+
if (!SemaRef.getLangOpts().CPlusPlus) {
// Determine whether the function was written with a prototype. This is
// true when:
@@ -9314,15 +9370,6 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
}
ExplicitSpecifier ExplicitSpecifier = D.getDeclSpec().getExplicitSpecifier();
-
- ConstexprSpecKind ConstexprKind = D.getDeclSpec().getConstexprSpecifier();
- if (ConstexprKind == ConstexprSpecKind::Constinit) {
- SemaRef.Diag(D.getDeclSpec().getConstexprSpecLoc(),
- diag::err_constexpr_wrong_decl_kind)
- << static_cast<int>(ConstexprKind);
- ConstexprKind = ConstexprSpecKind::Unspecified;
- D.getMutableDeclSpec().ClearConstexprSpec();
- }
Expr *TrailingRequiresClause = D.getTrailingRequiresClause();
SemaRef.CheckExplicitObjectMemberFunction(DC, D, Name, R);
@@ -13909,7 +13956,9 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
VDecl->setStorageClass(SC_Extern);
// C99 6.7.8p4. All file scoped initializers need to be constant.
- if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl())
+ // Avoid duplicate diagnostics for constexpr variables.
+ if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl() &&
+ !VDecl->isConstexpr())
CheckForConstantInitializer(Init, DclT);
}
@@ -14520,9 +14569,13 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
QualType baseType = Context.getBaseElementType(type);
bool HasConstInit = true;
+ if (getLangOpts().C23 && var->isConstexpr() && !Init)
+ Diag(var->getLocation(), diag::err_constexpr_var_requires_const_init)
+ << var;
+
// Check whether the initializer is sufficiently constant.
- if (getLangOpts().CPlusPlus && !type->isDependentType() && Init &&
- !Init->isValueDependent() &&
+ if ((getLangOpts().CPlusPlus || (getLangOpts().C23 && var->isConstexpr())) &&
+ !type->isDependentType() && Init && !Init->isValueDependent() &&
(GlobalStorage || var->isConstexpr() ||
var->mightBeUsableInConstantExpressions(Context))) {
// If this variable might have a constant initializer or might be usable in
@@ -14530,7 +14583,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
// do this lazily, because the result might depend on things that change
// later, such as which constexpr functions happen to be defined.
SmallVector<PartialDiagnosticAt, 8> Notes;
- if (!getLangOpts().CPlusPlus11) {
+ if (!getLangOpts().CPlusPlus11 && !getLangOpts().C23) {
// Prior to C++11, in contexts where a constant initializer is required,
// the set of valid constant initializers is described by syntactic rules
// in [expr.const]p2-6.
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 060fe35..011deed 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -190,13 +190,35 @@ 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();
+ }
+
+ if (const auto *VD = dyn_cast_if_present<VarDecl>(D); VD && VD->isConstexpr())
+ return true;
+
+ return false;
+}
+
+static void CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
+ Sema &SemaRef, QualType &TT);
+
static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT,
- Sema &S) {
+ Sema &S, bool CheckC23ConstexprInit = false) {
// Get the length of the string as parsed.
auto *ConstantArrayTy =
cast<ConstantArrayType>(Str->getType()->getAsArrayTypeUnsafe());
uint64_t StrLength = ConstantArrayTy->getSize().getZExtValue();
+ if (CheckC23ConstexprInit)
+ if (const StringLiteral *SL = dyn_cast<StringLiteral>(Str->IgnoreParens()))
+ CheckC23ConstexprInitStringLiteral(SL, S, DeclT);
+
if (const IncompleteArrayType *IAT = dyn_cast<IncompleteArrayType>(AT)) {
// C99 6.7.8p14. We have an array of character type with unknown size
// being initialized to a string literal.
@@ -1476,7 +1498,9 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
if (IsStringInit(expr, arrayType, SemaRef.Context) == SIF_None) {
// FIXME: Should we do this checking in verify-only mode?
if (!VerifyOnly)
- CheckStringInit(expr, ElemType, arrayType, SemaRef);
+ CheckStringInit(expr, ElemType, arrayType, SemaRef,
+ SemaRef.getLangOpts().C23 &&
+ initializingConstexprVariable(Entity));
if (StructuredList)
UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
++Index;
@@ -1941,7 +1965,9 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
// constant for each string.
// FIXME: Should we do these checks in verify-only mode too?
if (!VerifyOnly)
- CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef);
+ CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef,
+ SemaRef.getLangOpts().C23 &&
+ initializingConstexprVariable(Entity));
if (StructuredList) {
UpdateStructuredListElement(StructuredList, StructuredIndex,
IList->getInit(Index));
@@ -8377,6 +8403,9 @@ static void DiagnoseNarrowingInInitList(Sema &S,
QualType EntityType,
const Expr *PostInit);
+static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
+ QualType ToType, Expr *Init);
+
/// Provide warnings when std::move is used on construction.
static void CheckMoveOnConstruction(Sema &S, const Expr *InitExpr,
bool IsReturnStmt) {
@@ -9203,6 +9232,23 @@ ExprResult InitializationSequence::Perform(Sema &S,
return ExprError();
CurInit = CurInitExprRes;
+ if (S.getLangOpts().C23 && initializingConstexprVariable(Entity)) {
+ CheckC23ConstexprInitConversion(S, SourceType, Entity.getType(),
+ CurInit.get());
+
+ // C23 6.7.1p6: If an object or subobject declared with storage-class
+ // specifier constexpr has pointer, integer, or arithmetic type, any
+ // explicit initializer value for it shall be null, an integer
+ // constant expression, or an arithmetic constant expression,
+ // respectively.
+ Expr::EvalResult ER;
+ if (Entity.getType()->getAs<PointerType>() &&
+ CurInit.get()->EvaluateAsRValue(ER, S.Context) &&
+ !ER.Val.isNullPointer()) {
+ S.Diag(Kind.getLocation(), diag::err_c23_constexpr_pointer_not_null);
+ }
+ }
+
bool Complained;
if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(),
Step->Type, SourceType,
@@ -9220,7 +9266,9 @@ ExprResult InitializationSequence::Perform(Sema &S,
QualType Ty = Step->Type;
bool UpdateType = ResultType && Entity.getType()->isIncompleteArrayType();
CheckStringInit(CurInit.get(), UpdateType ? *ResultType : Ty,
- S.Context.getAsArrayType(Ty), S);
+ S.Context.getAsArrayType(Ty), S,
+ S.getLangOpts().C23 &&
+ initializingConstexprVariable(Entity));
break;
}
@@ -10509,6 +10557,69 @@ static void DiagnoseNarrowingInInitList(Sema &S,
S.getLocForEndOfToken(PostInit->getEndLoc()), ")");
}
+static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType,
+ QualType ToType, Expr *Init) {
+ assert(S.getLangOpts().C23);
+ ImplicitConversionSequence ICS = S.TryImplicitConversion(
+ Init->IgnoreParenImpCasts(), ToType, /*SuppressUserConversions*/ false,
+ Sema::AllowedExplicit::None,
+ /*InOverloadResolution*/ false,
+ /*CStyle*/ false,
+ /*AllowObjCWritebackConversion=*/false);
+
+ if (!ICS.isStandard())
+ return;
+
+ APValue Value;
+ QualType PreNarrowingType;
+ // Reuse C++ narrowing check.
+ switch (ICS.Standard.getNarrowingKind(
+ S.Context, Init, Value, PreNarrowingType,
+ /*IgnoreFloatToIntegralConversion*/ false)) {
+ // The value doesn't fit.
+ case NK_Constant_Narrowing:
+ S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_not_representable)
+ << Value.getAsString(S.Context, PreNarrowingType) << ToType;
+ return;
+
+ // Conversion to a narrower type.
+ case NK_Type_Narrowing:
+ S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_type_mismatch)
+ << ToType << FromType;
+ return;
+
+ // Since we only reuse narrowing check for C23 constexpr variables here, we're
+ // not really interested in these cases.
+ case NK_Dependent_Narrowing:
+ case NK_Variable_Narrowing:
+ case NK_Not_Narrowing:
+ return;
+ }
+ llvm_unreachable("unhandled case in switch");
+}
+
+static void CheckC23ConstexprInitStringLiteral(const StringLiteral *SE,
+ Sema &SemaRef, QualType &TT) {
+ assert(SemaRef.getLangOpts().C23);
+ // character that string literal contains fits into TT - target type.
+ const ArrayType *AT = SemaRef.Context.getAsArrayType(TT);
+ QualType CharType = AT->getElementType();
+ uint32_t BitWidth = SemaRef.Context.getTypeSize(CharType);
+ bool isUnsigned = CharType->isUnsignedIntegerType();
+ llvm::APSInt Value(BitWidth, isUnsigned);
+ for (unsigned I = 0, N = SE->getLength(); I != N; ++I) {
+ int64_t C = SE->getCodeUnitS(I, SemaRef.Context.getCharWidth());
+ Value = C;
+ if (Value != C) {
+ SemaRef.Diag(SemaRef.getLocationOfStringLiteralByte(SE, I),
+ diag::err_c23_constexpr_init_not_representable)
+ << C << CharType;
+ return;
+ }
+ }
+ return;
+}
+
//===----------------------------------------------------------------------===//
// Initialization helper functions
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 7d38043..a03f3ea 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -332,7 +332,8 @@ static const Expr *IgnoreNarrowingConversion(ASTContext &Ctx,
NarrowingKind StandardConversionSequence::getNarrowingKind(
ASTContext &Ctx, const Expr *Converted, APValue &ConstantValue,
QualType &ConstantType, bool IgnoreFloatToIntegralConversion) const {
- assert(Ctx.getLangOpts().CPlusPlus && "narrowing check outside C++");
+ assert((Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23) &&
+ "narrowing check outside C++");
// C++11 [dcl.init.list]p7:
// A narrowing conversion is an implicit conversion ...
@@ -414,20 +415,41 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
if (Initializer->isValueDependent())
return NK_Dependent_Narrowing;
- if (Initializer->isCXX11ConstantExpr(Ctx, &ConstantValue)) {
+ Expr::EvalResult R;
+ if ((Ctx.getLangOpts().C23 && Initializer->EvaluateAsRValue(R, Ctx)) ||
+ Initializer->isCXX11ConstantExpr(Ctx, &ConstantValue)) {
// Constant!
+ if (Ctx.getLangOpts().C23)
+ ConstantValue = R.Val;
assert(ConstantValue.isFloat());
llvm::APFloat FloatVal = ConstantValue.getFloat();
// Convert the source value into the target type.
bool ignored;
- llvm::APFloat::opStatus ConvertStatus = FloatVal.convert(
- Ctx.getFloatTypeSemantics(ToType),
- llvm::APFloat::rmNearestTiesToEven, &ignored);
- // If there was no overflow, the source value is within the range of
- // values that can be represented.
- if (ConvertStatus & llvm::APFloat::opOverflow) {
- ConstantType = Initializer->getType();
- return NK_Constant_Narrowing;
+ llvm::APFloat Converted = FloatVal;
+ llvm::APFloat::opStatus ConvertStatus =
+ Converted.convert(Ctx.getFloatTypeSemantics(ToType),
+ llvm::APFloat::rmNearestTiesToEven, &ignored);
+ Converted.convert(Ctx.getFloatTypeSemantics(FromType),
+ llvm::APFloat::rmNearestTiesToEven, &ignored);
+ if (Ctx.getLangOpts().C23) {
+ if (FloatVal.isNaN() && Converted.isNaN() &&
+ !FloatVal.isSignaling() && !Converted.isSignaling()) {
+ // Quiet NaNs are considered the same value, regardless of
+ // payloads.
+ return NK_Not_Narrowing;
+ }
+ // For normal values, check exact equality.
+ if (!Converted.bitwiseIsEqual(FloatVal)) {
+ ConstantType = Initializer->getType();
+ return NK_Constant_Narrowing;
+ }
+ } else {
+ // If there was no overflow, the source value is within the range of
+ // values that can be represented.
+ if (ConvertStatus & llvm::APFloat::opOverflow) {
+ ConstantType = Initializer->getType();
+ return NK_Constant_Narrowing;
+ }
}
} else {
return NK_Variable_Narrowing;
@@ -494,7 +516,30 @@ NarrowingKind StandardConversionSequence::getNarrowingKind(
}
return NK_Not_Narrowing;
}
+ case ICK_Complex_Real:
+ if (FromType->isComplexType() && !ToType->isComplexType())
+ return NK_Type_Narrowing;
+ return NK_Not_Narrowing;
+ case ICK_Floating_Promotion:
+ if (Ctx.getLangOpts().C23) {
+ const Expr *Initializer = IgnoreNarrowingConversion(Ctx, Converted);
+ Expr::EvalResult R;
+ if (Initializer->EvaluateAsRValue(R, Ctx)) {
+ ConstantValue = R.Val;
+ assert(ConstantValue.isFloat());
+ llvm::APFloat FloatVal = ConstantValue.getFloat();
+ // C23 6.7.3p6 If the initializer has real type and a signaling NaN
+ // value, the unqualified versions of the type of the initializer and
+ // the corresponding real type of the object declared shall be
+ // compatible.
+ if (FloatVal.isNaN() && FloatVal.isSignaling()) {
+ ConstantType = Initializer->getType();
+ return NK_Constant_Narrowing;
+ }
+ }
+ }
+ return NK_Not_Narrowing;
default:
// Other kinds of conversions are not narrowings.
return NK_Not_Narrowing;
diff --git a/clang/test/C/C2x/n3018.c b/clang/test/C/C2x/n3018.c
new file mode 100644
index 0000000..0d54d53
--- /dev/null
+++ b/clang/test/C/C2x/n3018.c
@@ -0,0 +1,87 @@
+// RUN: %clang_cc1 -std=c23 -verify -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion %s
+
+/* WG14 N3018: Full
+ * The constexpr specifier for object definitions
+ */
+
+#define ULLONG_MAX (__LONG_LONG_MAX__*2ULL+1ULL)
+#define UINT_MAX (__INT_MAX__ *2U +1U)
+
+void Example0() {
+ constexpr unsigned int minusOne = -1;
+ // expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned int'}}
+ constexpr unsigned int uint_max = -1U;
+ constexpr double onethird = 1.0/3.0;
+ constexpr double onethirdtrunc = (double)(1.0/3.0);
+
+ constexpr char string[] = { "\xFF", };
+ constexpr unsigned char ucstring[] = { "\xFF", };
+ // expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
+ constexpr char string1[] = { -1, 0, };
+ constexpr unsigned char ucstring1[] = { -1, 0, };
+ // expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
+
+ // TODO: Make sure these work correctly once char8_t and _Decimal are supported
+ // constexpr char8_t u8string[] = { 255, 0, }; // ok
+ // constexpr char8_t u8string[] = { u8"\xFF", }; // ok
+ // constexpr _Decimal32 small = DEC64_TRUE_MIN * 0;// constraint violation
+}
+
+void Example1() {
+ constexpr int K = 47;
+ enum {
+ A = K,
+ };
+ constexpr int L = K;
+ static int b = K + 1;
+ int array[K];
+ _Static_assert(K == 47);
+}
+
+constexpr int K = 47;
+static const int b = K + 1;
+
+void Example2() {
+ constexpr int A = 42LL;
+ constexpr signed short B = ULLONG_MAX;
+ // expected-error@-1 {{constexpr initializer evaluates to 18446744073709551615 which is not exactly representable in type 'const short'}}
+ constexpr float C = 47u;
+
+ constexpr float D = 432000000;
+ constexpr float E = 1.0 / 3.0;
+ // expected-error@-1 {{constexpr initializer evaluates to 3.333333e-01 which is not exactly representable in type 'const float'}}
+ constexpr float F = 1.0f / 3.0f;
+}
+
+
+void Example3() {
+ constexpr static unsigned short array[] = {
+ 3000,
+ 300000,
+ // expected-error@-1 {{constexpr initializer evaluates to 300000 which is not exactly representable in type 'const unsigned short'}}
+ -1
+ // expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned short'}}
+ };
+
+ constexpr static unsigned short array1[] = {
+ 3000,
+ 3000,
+ -1
+ // expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned short'}}
+ };
+
+ struct S {
+ int x, y;
+ };
+ constexpr struct S s = {
+ .x = __INT_MAX__,
+ .y = UINT_MAX,
+ // expected-error@-1 {{constexpr initializer evaluates to 4294967295 which is not exactly representable in type 'int'}}
+ };
+}
+
+void Example4() {
+ struct s { void *p; };
+ constexpr struct s A = { nullptr };
+ constexpr struct s B = A;
+}
diff --git a/clang/test/Parser/c23-constexpr.c b/clang/test/Parser/c23-constexpr.c
new file mode 100644
index 0000000..156128fa
--- /dev/null
+++ b/clang/test/Parser/c23-constexpr.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=c23 -std=c23 %s -Wpre-c2x-compat
+// RUN: %clang_cc1 -fsyntax-only -verify=c17 -std=c17 %s
+
+constexpr int a = 0; // c17-error {{unknown type name 'constexpr'}} \
+ c23-warning {{'constexpr' is incompatible with C standards before C23}}
+
+void func(int array[constexpr]); // c23-error {{expected expression}} \
+ // c17-error {{use of undeclared}}
+
+_Atomic constexpr int b = 0; // c23-error {{constexpr variable cannot have type 'const _Atomic(int)'}} \
+ // c23-warning {{'constexpr' is incompatible with C standards before C23}} \
+ // c17-error {{unknown type name 'constexpr'}}
+
+int static constexpr c = 1; // c17-error {{expected ';' after top level declarator}} \
+ // c23-warning {{'constexpr' is incompatible with C standards before C23}}
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
new file mode 100644
index 0000000..8286cd2
--- /dev/null
+++ b/clang/test/Sema/constexpr.c
@@ -0,0 +1,359 @@
+// RUN: %clang_cc1 -std=c23 -verify -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion -Wno-div-by-zero %s
+
+// Check that constexpr only applies to variables.
+constexpr void f0() {} // expected-error {{'constexpr' can only be used in variable declarations}}
+constexpr const int f1() { return 0; } // expected-error {{'constexpr' can only be used in variable declarations}}
+
+constexpr struct S1 { int f; }; //expected-error {{struct cannot be marked constexpr}}
+constexpr struct S2 ; // expected-error {{struct cannot be marked constexpr}}
+constexpr union U1; // expected-error {{union cannot be marked constexpr}}
+constexpr union U2 {int a; float b;}; // expected-error {{union cannot be marked constexpr}}
+constexpr enum E1 {A = 1, B = 2} ; // expected-error {{enum cannot be marked constexpr}}
+struct S3 {
+ static constexpr int f = 0; // expected-error {{type name does not allow storage class}}
+ // expected-error@-1 {{type name does not allow constexpr}}
+ // expected-error@-2 {{expected ';' at end}}
+ constexpr int f1 = 0;
+ // expected-error@-1 {{type name does not allow constexpr}}
+ // expected-error@-2 {{expected ';' at end}}
+};
+
+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}}
+
+void f2() {
+ constexpr int a = 0;
+ constexpr float b = 1.7f;
+}
+
+// Check how constexpr works with other storage-class specifiers.
+constexpr auto V4 = 1;
+constexpr static auto V5 = 1;
+constexpr static const auto V6 = 1;
+constexpr static const int V7 = 1;
+constexpr static int V8 = 1;
+constexpr auto Ulong = 1L;
+constexpr auto CompoundLiteral = (int){13};
+constexpr auto DoubleCast = (double)(1 / 3);
+constexpr auto String = "this is a string"; // expected-error {{constexpr pointer initializer is not null}}
+constexpr signed auto Long = 1L; // expected-error {{'auto' cannot be signed or unsigned}}
+_Static_assert(_Generic(Ulong, long : 1));
+_Static_assert(_Generic(CompoundLiteral, int : 1));
+_Static_assert(_Generic(DoubleCast, double : 1));
+_Static_assert(_Generic(String, char* : 1));
+
+typedef constexpr int Foo; // expected-error {{typedef cannot be constexpr}}
+constexpr typedef int Bar; // expected-error {{typedef cannot be constexpr}}
+
+void f3(constexpr register int P1) { // expected-error {{function parameter cannot be constexpr}}
+ constexpr register int V9 = 0;
+ constexpr register auto V10 = 0.0;
+}
+
+constexpr thread_local int V11 = 38; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
+constexpr static thread_local double V12 = 38; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
+constexpr extern thread_local char V13; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
+// expected-error@-1 {{cannot combine with previous 'extern' declaration specifier}}
+// expected-error@-2 {{constexpr variable declaration must be a definition}}
+constexpr thread_local short V14 = 38; // expected-error {{cannot combine with previous '_Thread_local' declaration specifier}}
+
+// Check how constexpr works with qualifiers.
+constexpr _Atomic int V15 = 0; // expected-error {{constexpr variable cannot have type 'const _Atomic(int)'}}
+constexpr _Atomic(int) V16 = 0; // expected-error {{constexpr variable cannot have type 'const _Atomic(int)'}}
+
+constexpr volatile int V17 = 0; // expected-error {{constexpr variable cannot have type 'const volatile int'}}
+
+constexpr int * restrict V18 = 0; // expected-error {{constexpr variable cannot have type 'int *const restrict'}}
+
+constexpr extern char Oops = 1; // expected-error {{cannot combine with previous 'extern' declaration specifier}} \
+ // expected-warning {{'extern' variable has an initializer}}
+
+constexpr int * restrict * Oops1 = 0;
+
+typedef _Atomic(int) TheA;
+typedef volatile short TheV;
+typedef float * restrict TheR;
+
+constexpr TheA V19[3] = {};
+// expected-error@-1 {{constexpr variable cannot have type 'const TheA[3]' (aka 'const _Atomic(int)[3]')}}
+constexpr TheV V20[3] = {};
+// expected-error@-1 {{constexpr variable cannot have type 'const TheV[3]' (aka 'const volatile short[3]')}}
+constexpr TheR V21[3] = {};
+// expected-error@-1 {{constexpr variable cannot have type 'const TheR[3]' (aka 'float *restrict const[3]')}}
+
+struct HasA {
+ TheA f;
+ int b;
+};
+
+struct HasV {
+ float b;
+ TheV f;
+};
+
+struct HasR {
+ short b;
+ int a;
+ TheR f;
+};
+
+constexpr struct HasA V22[2] = {};
+// expected-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
+constexpr struct HasV V23[2] = {};
+// expected-error@-1 {{constexpr variable cannot have type 'TheV' (aka 'volatile short')}}
+constexpr struct HasR V24[2] = {};
+// expected-error@-1 {{constexpr variable cannot have type 'TheR' (aka 'float *restrict')}}
+
+union U3 {
+ float a;
+ union {
+ struct HasA f;
+ struct HasR f1;
+ };
+};
+
+constexpr union U3 V25 = {};
+// expected-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
+constexpr union U3 V26[8] = {};
+// expected-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
+
+struct S4 {
+ union U3 f[3];
+};
+
+constexpr struct S4 V27 = {};
+// expected-error@-1 {{constexpr variable cannot have type 'TheA' (aka '_Atomic(int)')}}
+constexpr const int V28 = 28;
+
+struct S {
+ union {
+ volatile int i;
+ };
+ int j;
+};
+
+constexpr struct S s = {}; // expected-error {{constexpr variable cannot have type 'volatile int'}}
+
+// Check that constexpr variable must have a valid initializer which is a
+// constant expression.
+constexpr int V29;
+// expected-error@-1 {{constexpr variable 'V29' must be initialized by a constant expression}}
+
+struct S5 {
+ int f;
+};
+
+constexpr struct S5 V30;
+// expected-error@-1 {{constexpr variable 'V30' must be initialized by a constant expression}}
+constexpr struct S5 V31 = {};
+
+int randomFoo() { return 7; }
+
+constexpr float V32 = randomFoo();
+// expected-error@-1 {{constexpr variable 'V32' must be initialized by a constant expression}}
+
+const int V33 = 4;
+const int V34 = 0;
+const int V35 = 2;
+
+constexpr int V36 = V33 / V34;
+// expected-error@-1 {{constexpr variable 'V36' must be initialized by a constant expression}}
+constexpr int V37 = V33 / V35;
+// expected-error@-1 {{constexpr variable 'V37' must be initialized by a constant expression}}
+constexpr int V38 = 3;
+constexpr int V39 = V38 / V38;
+constexpr int V40 = V38 / 2;
+constexpr int V41 = V38 / 0;
+// expected-error@-1 {{constexpr variable 'V41' must be initialized by a constant expression}}
+// expected-note@-2 {{division by zero}}
+constexpr int V42 = V38 & 0;
+
+constexpr struct S5 V43 = { randomFoo() };
+// expected-error@-1 {{constexpr variable 'V43' must be initialized by a constant expression}}
+constexpr struct S5 V44 = { 0 };
+constexpr struct S5 V45 = { V38 / 0 };
+// expected-error@-1 {{constexpr variable 'V45' must be initialized by a constant expression}}
+// expected-note@-2 {{division by zero}}
+
+constexpr float V46[3] = {randomFoo() };
+// expected-error@-1 {{constexpr variable 'V46' must be initialized by a constant expression}}
+constexpr struct S5 V47[3] = {randomFoo() };
+// expected-error@-1 {{constexpr variable 'V47' must be initialized by a constant expression}}
+
+const static int V48 = V38;
+constexpr static int V49 = V48;
+// expected-error@-1 {{constexpr variable 'V49' must be initialized by a constant expression}}
+
+void f4(const int P1) {
+ constexpr int V = P1;
+// expected-error@-1 {{constexpr variable 'V' must be initialized by a constant expression}}
+
+ constexpr int V1 = 12;
+ constexpr const int *V2 = &V1;
+// expected-error@-1 {{constexpr variable 'V2' must be initialized by a constant expression}}
+}
+
+// Check that initializer for constexpr variable should match the type of the
+// variable and is exactly representable int the variable's type.
+
+struct S6 {
+ unsigned char a;
+};
+
+struct S7 {
+ union {
+ float a;
+ };
+ unsigned int b;
+};
+
+struct S8 {
+ unsigned char a[3];
+ unsigned int b[3];
+};
+
+constexpr struct S8 DesigInit = {.b = {299, 7, 8}, .a = {-1, 7, 8}};
+// expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned char'}}
+
+void f5() {
+ constexpr char V50 = 300;
+ // expected-error@-1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'const char'}}
+ constexpr float V51 = 1.0 / 3.0;
+ // expected-error@-1 {{constexpr initializer evaluates to 3.333333e-01 which is not exactly representable in type 'const float'}}
+ constexpr float V52 = 0.7;
+ // expected-error@-1 {{constexpr initializer evaluates to 7.000000e-01 which is not exactly representable in type 'const float'}}
+ constexpr float V53 = 1.0f / 3.0f;
+ constexpr float V54 = 432000000000;
+ // expected-error@-1 {{constexpr initializer evaluates to 432000000000 which is not exactly representable in type 'const float'}}
+ constexpr unsigned char V55[] = {
+ "\xAF",
+ // expected-error@-1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}}
+ };
+
+ constexpr unsigned char V56[] = {
+ u8"\xAF",
+ };
+ constexpr struct S6 V57 = {299};
+ // expected-error@-1 {{constexpr initializer evaluates to 299 which is not exactly representable in type 'unsigned char'}}
+ constexpr struct S6 V58 = {-299};
+ // expected-error@-1 {{constexpr initializer evaluates to -299 which is not exactly representable in type 'unsigned char'}}
+ constexpr double V59 = 0.5;
+ constexpr double V60 = 1.0;
+ constexpr float V61 = V59 / V60;
+ constexpr double V62 = 1.7;
+ constexpr float V63 = V59 / V62;
+ // expected-error@-1 {{constexpr initializer evaluates to 2.941176e-01 which is not exactly representable in type 'const float'}}
+
+ constexpr unsigned char V64 = '\xAF';
+ // expected-error@-1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}}
+ constexpr unsigned char V65 = u8'\xAF';
+
+ constexpr char V66[3] = {300};
+ // expected-error@-1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'const char'}}
+ constexpr struct S6 V67[3] = {300};
+ // expected-error@-1 {{constexpr initializer evaluates to 300 which is not exactly representable in type 'unsigned char'}}
+
+ constexpr struct S7 V68 = {0.3, -1 };
+ // expected-error@-1 {{constexpr initializer evaluates to 3.000000e-01 which is not exactly representable in type 'float'}}
+ // expected-error@-2 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned int'}}
+ constexpr struct S7 V69 = {0.5, -1 };
+ // expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'unsigned int'}}
+ constexpr struct S7 V70[3] = {{123456789}};
+ // expected-error@-1 {{constexpr initializer evaluates to 123456789 which is not exactly representable in type 'float'}}
+
+ constexpr int V71 = 0.3;
+ // expected-error@-1 {{constexpr initializer for type 'const int' is of type 'double'}}
+ constexpr int V72 = V59;
+ // expected-error@-1 {{constexpr initializer for type 'const int' is of type 'const double'}}
+ constexpr struct S6 V73 = {V59};
+ // expected-error@-1 {{constexpr initializer for type 'unsigned char' is of type 'const double'}}
+
+ constexpr float V74 = 1;
+ constexpr float V75 = V59;
+ constexpr unsigned int V76[3] = {0.5};
+ // expected-error@-1 {{constexpr initializer for type 'const unsigned int' is of type 'double'}}
+
+ constexpr _Complex float V77 = 0;
+ constexpr float V78 = V77;
+ // expected-error@-1 {{constexpr initializer for type 'const float' is of type 'const _Complex float'}}
+ constexpr int V79 = V77;
+ // expected-error@-1 {{constexpr initializer for type 'const int' is of type 'const _Complex float'}}
+
+}
+
+constexpr char string[] = "test""ing this out\xFF";
+constexpr unsigned char ustring[] = "test""ing this out\xFF";
+// expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
+constexpr char u8string[] = u8"test"u8"ing this out\xFF";
+// expected-error@-1 {{constexpr initializer evaluates to 255 which is not exactly representable in type 'const char'}}
+constexpr unsigned char u8ustring[] = u8"test"u8"ing this out\xFF";
+constexpr unsigned short uustring[] = u"test"u"ing this out\xFF";
+constexpr unsigned int Ustring[] = U"test"U"ing this out\xFF";
+constexpr unsigned char Arr2[6][6] = {
+ {"ek\xFF"}, {"ek\xFF"}
+// expected-error@-1 2{{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}}
+};
+
+constexpr int i = (12);
+constexpr int j = (i);
+constexpr unsigned jneg = (-i);
+// expected-error@-1 {{constexpr initializer evaluates to -12 which is not exactly representable in type 'const unsigned int'}}
+
+// Check that initializer for pointer constexpr variable should be null.
+constexpr int V80 = 3;
+constexpr const int *V81 = &V80;
+// expected-error@-1 {{constexpr pointer initializer is not null}}
+constexpr int *V82 = 0;
+constexpr int *V83 = V82;
+constexpr int *V84 = 42;
+// expected-error@-1 {{constexpr variable 'V84' must be initialized by a constant expression}}
+// expected-note@-2 {{this conversion is not allowed in a constant expression}}
+// expected-error@-3 {{constexpr pointer initializer is not null}}
+constexpr int *V85 = nullptr;
+
+// Check that constexpr variables should not be VLAs.
+void f6(const int P1) {
+ constexpr int V86[P1] = {};
+// expected-error@-1 {{constexpr variable cannot have type 'const int[P1]'}}
+ const int V87 = 3;
+ constexpr int V88[V87] = {};
+// expected-warning@-1 {{variable length array folded to constant array as an extension}}
+ int V89 = 7;
+ constexpr int V90[V89] = {};
+// expected-error@-1 {{constexpr variable cannot have type 'const int[V89]'}}
+}
+
+void f7(int n, int array[n]) {
+ constexpr typeof(array) foo = 0; // Accepted because array is a pointer type, not a VLA type
+ int (*(*fp)(int n))[n];
+ constexpr typeof(fp) bar = 0; // expected-error {{constexpr variable cannot have type 'const typeof (fp)' (aka 'int (*(*const)(int))[n]')}}
+}
+
+// Check how constexpr works with NaNs and infinities.
+#define FLT_NAN __builtin_nanf("1")
+#define DBL_NAN __builtin_nan("1")
+#define LD_NAN __builtin_nanf("1")
+#define FLT_SNAN __builtin_nansf("1")
+#define DBL_SNAN __builtin_nans("1")
+#define LD_SNAN __builtin_nansl("1")
+#define INF __builtin_inf()
+void infsNaNs() {
+ // Inf and quiet NaN is always fine, signaling NaN must have the same type.
+ constexpr float fl0 = INF;
+ constexpr float fl1 = (long double)INF;
+ constexpr float fl2 = (long double)FLT_NAN;
+ constexpr float fl3 = FLT_NAN;
+ constexpr float fl5 = DBL_NAN;
+ constexpr float fl6 = LD_NAN;
+ constexpr float fl7 = DBL_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const float'}}
+ constexpr float fl8 = LD_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const float'}}
+
+ constexpr double db0 = FLT_NAN;
+ constexpr double db2 = DBL_NAN;
+ constexpr double db3 = DBL_SNAN;
+ constexpr double db4 = FLT_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const double'}}
+ constexpr double db5 = LD_SNAN; // expected-error {{constexpr initializer evaluates to nan which is not exactly representable in type 'const double'}}
+ constexpr double db6 = INF;
+}
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index 3955a1d..7bf5e29 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -1201,7 +1201,7 @@ conformance.</p>
<tr>
<td>constexpr for object definitions</td>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3018.htm">N3018</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 19</td>
</tr>
<tr>
<td>Introduce storage class specifiers for compound literals</td>