diff options
-rw-r--r-- | clang/docs/ReleaseNotes.rst | 3 | ||||
-rw-r--r-- | clang/include/clang/AST/Expr.h | 11 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 12 | ||||
-rw-r--r-- | clang/include/clang/Basic/TokenKinds.def | 2 | ||||
-rw-r--r-- | clang/lib/AST/Decl.cpp | 25 | ||||
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 10 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/DeclSpec.cpp | 14 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 79 | ||||
-rw-r--r-- | clang/lib/Sema/SemaInit.cpp | 119 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 65 | ||||
-rw-r--r-- | clang/test/C/C2x/n3018.c | 87 | ||||
-rw-r--r-- | clang/test/Parser/c23-constexpr.c | 15 | ||||
-rw-r--r-- | clang/test/Sema/constexpr.c | 359 | ||||
-rw-r--r-- | clang/www/c_status.html | 2 |
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> |