https://github.com/Fznamznon updated https://github.com/llvm/llvm-project/pull/73099
>From 1d70b7726e7d1f11622a6d5c8246b0737e024c8d Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Tue, 19 Sep 2023 08:37:18 -0700 Subject: [PATCH 1/8] [C23] Implement N3018: The constexpr specifier for object definitions The implementation mostly reuses C++ code paths where possible, including narrowing check in order to provide diagnostic messages in case initializer for constexpr variable is not exactly representable in target type. The following won't work due to lack of support for other features: - Diagnosing of underspecified declarations involving constexpr - Constexpr attached to compound literals Also due to lack of support for char8_t some of examples with utf-8 strings don't work properly. --- clang/docs/ReleaseNotes.rst | 1 + .../clang/Basic/DiagnosticSemaKinds.td | 16 + clang/include/clang/Basic/TokenKinds.def | 2 +- clang/lib/AST/Decl.cpp | 16 +- clang/lib/AST/ExprConstant.cpp | 17 +- clang/lib/Parse/ParseDecl.cpp | 2 + clang/lib/Sema/SemaDecl.cpp | 204 +++++++++++-- clang/lib/Sema/SemaOverload.cpp | 36 ++- clang/test/C/C2x/n3018.c | 86 ++++++ clang/test/Parser/c23-constexpr.c | 6 + clang/test/Sema/constexpr.c | 275 ++++++++++++++++++ clang/www/c_status.html | 2 +- 12 files changed, 627 insertions(+), 36 deletions(-) create mode 100644 clang/test/C/C2x/n3018.c create mode 100644 clang/test/Parser/c23-constexpr.c create mode 100644 clang/test/Sema/constexpr.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b65106b9106d4..cae1707f3e30f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -208,6 +208,7 @@ C23 Feature Support - Clang now supports ``<stdckdint.h>`` which defines several macros for performing checked integer arithmetic. It is also exposed in pre-C23 modes. +- Clang now supports ``N3018 The constexpr specifier for object definitions``. - Completed the implementation of `N2508 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2508.pdf>`_. We diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 990692c06d7d3..11f24583dc55a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2932,6 +2932,22 @@ def warn_private_extern : Warning< def note_private_extern : Note< "use __attribute__((visibility(\"hidden\"))) attribute instead">; +// C23 constexpr +def err_c23_thread_local_constexpr : Error< + "thread-local storage is not allowed with constexpr">; +def err_c23_extern_constexpr : Error< + "extern specifier is not allowed with constexpr">; +def err_c23_constexpr_not_variable : Error< + "constexpr is only allowed 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 3ab420821d82b..e9e8f59247662 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 c5c2edf1bfe3a..678a366ed29ad 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2461,7 +2461,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. @@ -2485,12 +2485,12 @@ bool VarDecl::mightBeUsableInConstantExpressions(const ASTContext &C) const { // In C++, const, non-volatile variables of integral or enumeration types // can be used in constant expressions. - if (getType()->isIntegralOrEnumerationType()) + if (getType()->isIntegralOrEnumerationType() && !Lang.C23) return true; // 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 { @@ -2568,11 +2568,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, @@ -2630,7 +2630,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 3a41e9718bb58..fdd4bbafbb73c 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2231,6 +2231,13 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, return false; } + if (Info.getLangOpts().C23) { + auto *VarD = dyn_cast_or_null<VarDecl>(BaseVD); + if (VarD && VarD->isConstexpr() && !LVal.isNullPointer()) { + Info.report(Loc, diag::err_c23_constexpr_pointer_not_null); + } + } + // Check that the object is a global. Note that the fake 'this' object we // manufacture when checking potential constant expressions is conservatively // assumed to be global here. @@ -4110,6 +4117,10 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, } bool IsConstant = BaseType.isConstant(Info.Ctx); + bool ConstexprVar = false; + if (const auto *VD = dyn_cast_or_null<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. @@ -4129,6 +4140,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) @@ -15704,7 +15718,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 8cb5b09fd3b0f..7059631515c87 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4168,6 +4168,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/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4e1857b931cc8..1f74d937bf97a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5152,13 +5152,17 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, // and definitions of functions and variables. // C++2a [dcl.constexpr]p1: The consteval specifier shall be applied only to // the declaration of a function or function template - if (Tag) + if (Tag) { Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_tag) << GetDiagnosticTypeSpecifierID(DS) << static_cast<int>(DS.getConstexprSpecifier()); - else - Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_wrong_decl_kind) - << 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()); + } // Don't emit warnings after this error. return TagD; } @@ -7894,6 +7898,17 @@ NamedDecl *Sema::ActOnVariableDeclarator( (getLangOpts().CPlusPlus17 || Context.getTargetInfo().getCXXABI().isMicrosoft())) NewVD->setImplicitlyInline(); + + if (getLangOpts().C23) { + DeclSpec::TSCS TSC = D.getDeclSpec().getThreadStorageClassSpec(); + if (TSC != TSCS_unspecified) { + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_c23_thread_local_constexpr); + } + if (NewVD->hasExternalStorage()) + Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), + diag::err_c23_extern_constexpr); + } break; case ConstexprSpecKind::Constinit: @@ -8605,6 +8620,27 @@ static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND, return false; } +static bool CheckC23ConstexprVarTypeQualifiers(Sema &SemaRef, + SourceLocation VarLoc, QualType T) { + if (const auto *A = SemaRef.Context.getAsArrayType(T)) { + T = A->getElementType(); + } + + if (T->isAtomicType() || T.isVolatileQualified() || T.isRestrictQualified()) { + SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T; + return true; + } + + if (T->isRecordType()) { + RecordDecl *RD = T->getAsRecordDecl(); + for (const auto &F : RD->fields()) + if (CheckC23ConstexprVarTypeQualifiers(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()) @@ -8857,7 +8893,9 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) { if (NewVD->isConstexpr() && !T->isDependentType() && RequireLiteralType(NewVD->getLocation(), T, - diag::err_constexpr_var_non_literal)) { + getLangOpts().C23 + ? diag::err_c23_constexpr_invalid_type + : diag::err_constexpr_var_non_literal)) { NewVD->setInvalidDecl(); return; } @@ -8870,6 +8908,12 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) { return; } + if (getLangOpts().C23 && NewVD->isConstexpr() && + CheckC23ConstexprVarTypeQualifiers(*this, NewVD->getLocation(), T)) { + NewVD->setInvalidDecl(); + return; + } + // Check that SVE types are only used in functions with SVE available. if (T->isSVESizelessBuiltinType() && isa<FunctionDecl>(CurContext)) { const FunctionDecl *FD = cast<FunctionDecl>(CurContext); @@ -9237,6 +9281,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: @@ -9270,15 +9330,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); @@ -13786,7 +13837,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 double diagnosing for constexpr variables. + if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl() && + !VDecl->isConstexpr()) CheckForConstantInitializer(Init, DclT); } @@ -14240,6 +14293,115 @@ StmtResult Sema::ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc, : IdentLoc); } +static ImplicitConversionKind getConversionKind(QualType FromType, + QualType ToType) { + if (ToType->isIntegerType()) { + if (FromType->isComplexType()) + return ICK_Complex_Real; + if (FromType->isFloatingType()) + return ICK_Floating_Integral; + if (FromType->isIntegerType()) + return ICK_Integral_Conversion; + } + + if (ToType->isFloatingType()) { + if (FromType->isComplexType()) + return ICK_Complex_Real; + if (FromType->isFloatingType()) + return ICK_Floating_Conversion; + if (FromType->isIntegerType()) + return ICK_Floating_Integral; + } + + return ICK_Identity; +} + +static bool checkC23ConstexprInitConversion(Sema &S, const Expr *Init) { + assert(S.getLangOpts().C23); + const Expr *InitNoCast = Init->IgnoreImpCasts(); + StandardConversionSequence SCS; + SCS.setAsIdentityConversion(); + auto FromType = InitNoCast->getType(); + auto ToType = Init->getType(); + SCS.setToType(0, FromType); + SCS.setToType(1, ToType); + SCS.Second = getConversionKind(FromType, ToType); + + APValue Value; + QualType PreNarrowingType; + // Reuse C++ narrowing check. + switch (SCS.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 true; + + // Conversion to a narrower type. + case NK_Type_Narrowing: + S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_type_mismatch) + << ToType << FromType; + return true; + + // 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 false; + + } + llvm_unreachable("unhandled case in switch"); +} + +static bool checkC23ConstexprInitStringLiteral(const StringLiteral *SE, + Sema &SemaRef, + SourceLocation Loc) { + assert(SemaRef.getLangOpts().C23); + // String literals have the target type attached but underneath may contain + // values that don't really fit into the target type. Check that every + // character fits. + const ConstantArrayType *CAT = + SemaRef.Context.getAsConstantArrayType(SE->getType()); + QualType CharType = CAT->getElementType(); + uint32_t BitWidth = SemaRef.Context.getTypeSize(CharType); + bool isUnsigned = CharType->isUnsignedIntegerType(); + llvm::APSInt Value(BitWidth, isUnsigned); + const StringRef S = SE->getBytes(); + for (unsigned I = 0, N = SE->getLength(); I != N; ++I) { + Value = S[I]; + if (Value != S[I]) { + SemaRef.Diag(Loc, diag::err_c23_constexpr_init_not_representable) + << S[I] << CharType; + return true; + } + } + return false; +} + +static bool checkC23ConstexprInitializer(Sema &S, const Expr *Init) { + const Expr *InitNoCast = Init->IgnoreImpCasts(); + if (Init->getType() != InitNoCast->getType()) + if (checkC23ConstexprInitConversion(S, Init)) + return true; + + if (auto *SE = dyn_cast<StringLiteral>(Init)) + if (checkC23ConstexprInitStringLiteral(SE, S, Init->getBeginLoc())) + return true; + + for (const Stmt *SubStmt : Init->children()) { + const Expr *ChildExpr = dyn_cast_or_null<const Expr>(SubStmt); + if (!ChildExpr) + continue; + + if (checkC23ConstexprInitializer(S, ChildExpr)) { + return true; + } + } + return false; +} + void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { if (var->isInvalidDecl()) return; @@ -14397,9 +14559,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) && + !type->isDependentType() && Init && !Init->isValueDependent() && (GlobalStorage || var->isConstexpr() || var->mightBeUsableInConstantExpressions(Context))) { // If this variable might have a constant initializer or might be usable in @@ -14407,7 +14573,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. @@ -14432,6 +14598,8 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { if (HasConstInit) { // FIXME: Consider replacing the initializer with a ConstantExpr. + if (getLangOpts().C23 && var->isConstexpr()) + checkC23ConstexprInitializer(*this, Init); } else if (var->isConstexpr()) { SourceLocation DiagLoc = var->getLocation(); // If the note doesn't add any useful information other than a source diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 64607e28b8b35..dfb1dfc00c1bc 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -328,7 +328,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 ... @@ -410,20 +411,35 @@ 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( + llvm::APFloat Converted = FloatVal; + llvm::APFloat::opStatus ConvertStatus = Converted.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; + Converted.convert(Ctx.getFloatTypeSemantics(FromType), + llvm::APFloat::rmNearestTiesToEven, &ignored); + if (Ctx.getLangOpts().C23) { + 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; @@ -490,6 +506,10 @@ NarrowingKind StandardConversionSequence::getNarrowingKind( } return NK_Not_Narrowing; } + case ICK_Complex_Real: + if (FromType->isComplexType() && !ToType->isComplexType()) + return NK_Type_Narrowing; + return NK_Not_Narrowing; default: // Other kinds of conversions are not narrowings. diff --git a/clang/test/C/C2x/n3018.c b/clang/test/C/C2x/n3018.c new file mode 100644 index 0000000000000..ccc0b708a67e0 --- /dev/null +++ b/clang/test/C/C2x/n3018.c @@ -0,0 +1,86 @@ +// RUN: %clang_cc1 -std=c2x -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 '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 '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 '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 '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 'unsigned short'}} + -1 // constraint violation, target type is unsigned + }; + + constexpr static unsigned short array1[] = { + 3000, + 3000, + -1 + // expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type '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 0000000000000..5eddf66a66e74 --- /dev/null +++ b/clang/test/Parser/c23-constexpr.c @@ -0,0 +1,6 @@ +// 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}} diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c new file mode 100644 index 0000000000000..650716640e2f4 --- /dev/null +++ b/clang/test/Sema/constexpr.c @@ -0,0 +1,275 @@ +// RUN: %clang_cc1 -std=c2x -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 is only allowed in variable declarations}} +constexpr const int f1() { return 0; } // expected-error {{constexpr is only allowed 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 is only allowed 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; + +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 {{thread-local storage is not allowed with constexpr}} +constexpr static thread_local double V12 = 38; // expected-error {{thread-local storage is not allowed with constexpr}} +constexpr extern thread_local char V13; // expected-error {{extern specifier is not allowed with constexpr}} +// expected-error@-1 {{thread-local storage is not allowed with constexpr}} +// expected-error@-2 {{constexpr variable declaration must be a definition}} +constexpr thread_local short V14 = 38; // expected-error {{thread-local storage is not allowed with constexpr}} + +// 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'}} + +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' (aka 'const _Atomic(int)')}} +constexpr TheV V20[3] = {}; +// expected-error@-1 {{constexpr variable cannot have type 'const TheV' (aka 'const volatile short')}} +constexpr TheR V21[3] = {}; +// expected-error@-1 {{constexpr variable cannot have type 'const TheR' (aka 'float *const restrict')}} + +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; + +// 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}} +// expected-error@-2 {{constexpr pointer initializer is not null}} +} + +// 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; +}; + +void f5() { + constexpr char V50 = 300; + // expected-error@-1 {{constexpr initializer evaluates to 300 which is not exactly representable in type '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 'float'}} + constexpr float V52 = 0.7; + // expected-error@-1 {{constexpr initializer evaluates to 7.000000e-01 which is not exactly representable in type '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 'float'}} + constexpr unsigned char V55[] = { + "\xAF", + // expected-error@-1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}} + }; + + // FIXME Shouldn't be diagnosed if char_8t is supported. + constexpr unsigned char V56[] = { + u8"\xAF", + // expected-error@-1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}} + }; + 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 'float'}} + + constexpr unsigned char V64 = '\xAF'; + // expected-error@-1 {{constexpr initializer evaluates to -81 which is not exactly representable in type '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 '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'}} + 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 'int' is of type 'double'}} + constexpr int V72 = V59; + // expected-error@-1 {{constexpr initializer for type '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 'unsigned int' is of type 'double'}} + + constexpr _Complex float V77 = 0; + constexpr float V78 = V77; + // expected-error@-1 {{constexpr initializer for type 'float' is of type 'const _Complex float'}} + constexpr int V79 = V77; + // expected-error@-1 {{constexpr initializer for type 'int' is of type 'const _Complex float'}} + +} + +// 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}} +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]'}} +} diff --git a/clang/www/c_status.html b/clang/www/c_status.html index 91cae138074b3..4644d18b49617 100644 --- a/clang/www/c_status.html +++ b/clang/www/c_status.html @@ -1201,7 +1201,7 @@ <h2 id="c2x">C23 implementation status</h2> <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 18</td> </tr> <tr> <td>Introduce storage class specifiers for compound literals</td> >From bbb801dde58ecd336369985d146b2592d5748593 Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Thu, 23 Nov 2023 06:44:12 -0800 Subject: [PATCH 2/8] Fix format, apply comments --- clang/lib/AST/ExprConstant.cpp | 7 +++---- clang/lib/Sema/SemaDecl.cpp | 18 +++++++++--------- clang/lib/Sema/SemaOverload.cpp | 9 ++++----- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index fdd4bbafbb73c..97827e31d8f4a 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2232,10 +2232,9 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, } if (Info.getLangOpts().C23) { - auto *VarD = dyn_cast_or_null<VarDecl>(BaseVD); - if (VarD && VarD->isConstexpr() && !LVal.isNullPointer()) { + if (const auto *VarD = dyn_cast_if_present<VarDecl>(BaseVD); + VarD && VarD->isConstexpr() && !LVal.isNullPointer()) Info.report(Loc, diag::err_c23_constexpr_pointer_not_null); - } } // Check that the object is a global. Note that the fake 'this' object we @@ -4118,7 +4117,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, bool IsConstant = BaseType.isConstant(Info.Ctx); bool ConstexprVar = false; - if (const auto *VD = dyn_cast_or_null<VarDecl>( + if (const auto *VD = dyn_cast_if_present<VarDecl>( Info.EvaluatingDecl.dyn_cast<const ValueDecl *>())) ConstexprVar = VD->isConstexpr(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 1f74d937bf97a..1ffc9971ae698 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -8621,7 +8621,8 @@ static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND, } static bool CheckC23ConstexprVarTypeQualifiers(Sema &SemaRef, - SourceLocation VarLoc, QualType T) { + SourceLocation VarLoc, + QualType T) { if (const auto *A = SemaRef.Context.getAsArrayType(T)) { T = A->getElementType(); } @@ -8632,7 +8633,7 @@ static bool CheckC23ConstexprVarTypeQualifiers(Sema &SemaRef, } if (T->isRecordType()) { - RecordDecl *RD = T->getAsRecordDecl(); + const RecordDecl *RD = T->getAsRecordDecl(); for (const auto &F : RD->fields()) if (CheckC23ConstexprVarTypeQualifiers(SemaRef, VarLoc, F->getType())) return true; @@ -14297,20 +14298,20 @@ static ImplicitConversionKind getConversionKind(QualType FromType, QualType ToType) { if (ToType->isIntegerType()) { if (FromType->isComplexType()) - return ICK_Complex_Real; + return ICK_Complex_Real; if (FromType->isFloatingType()) - return ICK_Floating_Integral; + return ICK_Floating_Integral; if (FromType->isIntegerType()) - return ICK_Integral_Conversion; + return ICK_Integral_Conversion; } if (ToType->isFloatingType()) { if (FromType->isComplexType()) - return ICK_Complex_Real; + return ICK_Complex_Real; if (FromType->isFloatingType()) - return ICK_Floating_Conversion; + return ICK_Floating_Conversion; if (FromType->isIntegerType()) - return ICK_Floating_Integral; + return ICK_Floating_Integral; } return ICK_Identity; @@ -14350,7 +14351,6 @@ static bool checkC23ConstexprInitConversion(Sema &S, const Expr *Init) { case NK_Variable_Narrowing: case NK_Not_Narrowing: return false; - } llvm_unreachable("unhandled case in switch"); } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index dfb1dfc00c1bc..0d3a9701b9501 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -412,8 +412,7 @@ NarrowingKind StandardConversionSequence::getNarrowingKind( return NK_Dependent_Narrowing; Expr::EvalResult R; - if ((Ctx.getLangOpts().C23 && - Initializer->EvaluateAsRValue(R, Ctx)) || + if ((Ctx.getLangOpts().C23 && Initializer->EvaluateAsRValue(R, Ctx)) || Initializer->isCXX11ConstantExpr(Ctx, &ConstantValue)) { // Constant! if (Ctx.getLangOpts().C23) @@ -423,9 +422,9 @@ NarrowingKind StandardConversionSequence::getNarrowingKind( // Convert the source value into the target type. bool ignored; llvm::APFloat Converted = FloatVal; - llvm::APFloat::opStatus ConvertStatus = Converted.convert( - Ctx.getFloatTypeSemantics(ToType), - llvm::APFloat::rmNearestTiesToEven, &ignored); + 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) { >From 9b4a5ea626138cc110c09ff41fb0793d9fb2a02b Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Wed, 29 Nov 2023 09:18:51 -0800 Subject: [PATCH 3/8] Apply some of the feedback: - Minor changes - Don't reuse requireLiteralType - Diagnose interaction with extern and thread_local from DeclSpec::Finish() - Add a couple of test cases --- .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/AST/ExprConstant.cpp | 4 + clang/lib/Sema/DeclSpec.cpp | 14 +++ clang/lib/Sema/SemaDecl.cpp | 93 +++++++++---------- clang/test/Sema/constexpr.c | 26 ++++-- 5 files changed, 80 insertions(+), 59 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1841f0f44fb0a..016a099f01293 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2938,7 +2938,7 @@ def err_c23_thread_local_constexpr : Error< def err_c23_extern_constexpr : Error< "extern specifier is not allowed with constexpr">; def err_c23_constexpr_not_variable : Error< - "constexpr is only allowed in variable declarations">; + "'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< diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 06f1635b5bd0f..bfea4414bc544 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -2231,6 +2231,10 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, return false; } + // 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. if (Info.getLangOpts().C23) { if (const auto *VarD = dyn_cast_if_present<VarDecl>(BaseVD); VarD && VarD->isConstexpr() && !LVal.isNullPointer()) diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 781f24cb71ae9..a3cf215d8a64a 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1361,6 +1361,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 6aff091a1df3f..9ee69880d1e36 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5172,17 +5172,15 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, // and definitions of functions and variables. // C++2a [dcl.constexpr]p1: The consteval specifier shall be applied only to // the declaration of a function or function template - if (Tag) { + if (Tag) 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()); - } + 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()); // Don't emit warnings after this error. return TagD; } @@ -7918,17 +7916,6 @@ NamedDecl *Sema::ActOnVariableDeclarator( (getLangOpts().CPlusPlus17 || Context.getTargetInfo().getCXXABI().isMicrosoft())) NewVD->setImplicitlyInline(); - - if (getLangOpts().C23) { - DeclSpec::TSCS TSC = D.getDeclSpec().getThreadStorageClassSpec(); - if (TSC != TSCS_unspecified) { - Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), - diag::err_c23_thread_local_constexpr); - } - if (NewVD->hasExternalStorage()) - Diag(D.getDeclSpec().getThreadStorageClassSpecLoc(), - diag::err_c23_extern_constexpr); - } break; case ConstexprSpecKind::Constinit: @@ -8640,13 +8627,21 @@ static bool checkForConflictWithNonVisibleExternC(Sema &S, const T *ND, return false; } -static bool CheckC23ConstexprVarTypeQualifiers(Sema &SemaRef, - SourceLocation VarLoc, - QualType T) { - if (const auto *A = SemaRef.Context.getAsArrayType(T)) { - T = A->getElementType(); +static bool CheckC23ConstexprVarType(Sema &SemaRef, SourceLocation VarLoc, + QualType T) { + + if (T->isVariableArrayType()) { + 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). + T = SemaRef.Context.getBaseElementType(T); + + // C23 6.7.1p4: 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 (T->isAtomicType() || T.isVolatileQualified() || T.isRestrictQualified()) { SemaRef.Diag(VarLoc, diag::err_c23_constexpr_invalid_type) << T; return true; @@ -8654,9 +8649,10 @@ static bool CheckC23ConstexprVarTypeQualifiers(Sema &SemaRef, if (T->isRecordType()) { const RecordDecl *RD = T->getAsRecordDecl(); - for (const auto &F : RD->fields()) - if (CheckC23ConstexprVarTypeQualifiers(SemaRef, VarLoc, F->getType())) - return true; + if (llvm::any_of(RD->fields(), [&SemaRef, VarLoc](const FieldDecl *F) { + return CheckC23ConstexprVarType(SemaRef, VarLoc, F->getType()); + })) + return true; } return false; @@ -8912,11 +8908,15 @@ 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, - getLangOpts().C23 - ? diag::err_c23_constexpr_invalid_type - : diag::err_constexpr_var_non_literal)) { + diag::err_constexpr_var_non_literal)) { NewVD->setInvalidDecl(); return; } @@ -8929,12 +8929,6 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) { return; } - if (getLangOpts().C23 && NewVD->isConstexpr() && - CheckC23ConstexprVarTypeQualifiers(*this, NewVD->getLocation(), T)) { - NewVD->setInvalidDecl(); - return; - } - // Check that SVE types are only used in functions with SVE available. if (T->isSVESizelessBuiltinType() && isa<FunctionDecl>(CurContext)) { const FunctionDecl *FD = cast<FunctionDecl>(CurContext); @@ -14314,7 +14308,7 @@ StmtResult Sema::ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc, : IdentLoc); } -static ImplicitConversionKind getConversionKind(QualType FromType, +static ImplicitConversionKind GetConversionKind(QualType FromType, QualType ToType) { if (ToType->isIntegerType()) { if (FromType->isComplexType()) @@ -14337,16 +14331,16 @@ static ImplicitConversionKind getConversionKind(QualType FromType, return ICK_Identity; } -static bool checkC23ConstexprInitConversion(Sema &S, const Expr *Init) { +static bool CheckC23ConstexprInitConversion(Sema &S, const Expr *Init) { assert(S.getLangOpts().C23); - const Expr *InitNoCast = Init->IgnoreImpCasts(); + const Expr *InitNoCast = Init->IgnoreParenImpCasts(); StandardConversionSequence SCS; SCS.setAsIdentityConversion(); auto FromType = InitNoCast->getType(); auto ToType = Init->getType(); SCS.setToType(0, FromType); SCS.setToType(1, ToType); - SCS.Second = getConversionKind(FromType, ToType); + SCS.Second = GetConversionKind(FromType, ToType); APValue Value; QualType PreNarrowingType; @@ -14375,7 +14369,7 @@ static bool checkC23ConstexprInitConversion(Sema &S, const Expr *Init) { llvm_unreachable("unhandled case in switch"); } -static bool checkC23ConstexprInitStringLiteral(const StringLiteral *SE, +static bool CheckC23ConstexprInitStringLiteral(const StringLiteral *SE, Sema &SemaRef, SourceLocation Loc) { assert(SemaRef.getLangOpts().C23); @@ -14400,24 +14394,23 @@ static bool checkC23ConstexprInitStringLiteral(const StringLiteral *SE, return false; } -static bool checkC23ConstexprInitializer(Sema &S, const Expr *Init) { - const Expr *InitNoCast = Init->IgnoreImpCasts(); +static bool CheckC23ConstexprInitializer(Sema &S, const Expr *Init) { + const Expr *InitNoCast = Init->IgnoreParenImpCasts(); if (Init->getType() != InitNoCast->getType()) - if (checkC23ConstexprInitConversion(S, Init)) + if (CheckC23ConstexprInitConversion(S, Init)) return true; - if (auto *SE = dyn_cast<StringLiteral>(Init)) - if (checkC23ConstexprInitStringLiteral(SE, S, Init->getBeginLoc())) + if (const auto *SE = dyn_cast<StringLiteral>(Init)) + if (CheckC23ConstexprInitStringLiteral(SE, S, Init->getBeginLoc())) return true; for (const Stmt *SubStmt : Init->children()) { - const Expr *ChildExpr = dyn_cast_or_null<const Expr>(SubStmt); + const Expr *ChildExpr = dyn_cast_or_null<Expr>(SubStmt); if (!ChildExpr) continue; - if (checkC23ConstexprInitializer(S, ChildExpr)) { + if (CheckC23ConstexprInitializer(S, ChildExpr)) return true; - } } return false; } @@ -14619,7 +14612,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { if (HasConstInit) { // FIXME: Consider replacing the initializer with a ConstantExpr. if (getLangOpts().C23 && var->isConstexpr()) - checkC23ConstexprInitializer(*this, Init); + CheckC23ConstexprInitializer(*this, Init); } else if (var->isConstexpr()) { SourceLocation DiagLoc = var->getLocation(); // If the note doesn't add any useful information other than a source diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c index 650716640e2f4..d8e68b56f918c 100644 --- a/clang/test/Sema/constexpr.c +++ b/clang/test/Sema/constexpr.c @@ -1,8 +1,8 @@ // RUN: %clang_cc1 -std=c2x -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 is only allowed in variable declarations}} -constexpr const int f1() { return 0; } // expected-error {{constexpr is only allowed in variable declarations}} +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}} @@ -18,7 +18,7 @@ struct S3 { // expected-error@-2 {{expected ';' at end}} }; -constexpr; // expected-error {{constexpr is only allowed in variable declarations}} +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}} @@ -40,12 +40,12 @@ void f3(constexpr register int P1) { // expected-error {{function parameter cann constexpr register auto V10 = 0.0; } -constexpr thread_local int V11 = 38; // expected-error {{thread-local storage is not allowed with constexpr}} -constexpr static thread_local double V12 = 38; // expected-error {{thread-local storage is not allowed with constexpr}} -constexpr extern thread_local char V13; // expected-error {{extern specifier is not allowed with constexpr}} -// expected-error@-1 {{thread-local storage is not allowed with constexpr}} +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 {{thread-local storage is not allowed with constexpr}} +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)'}} @@ -251,6 +251,16 @@ void f5() { } +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 wstring[] = u8"test"u8"ing this out\xFF"; +constexpr unsigned char wustring[] = u8"test"u8"ing this out\xFF"; +// expected-error@-1 {{constexpr initializer evaluates to -1 which is not exactly representable in type 'const unsigned char'}} + +constexpr int i = (12); +constexpr int j = (i); + // Check that initializer for pointer constexpr variable should be null. constexpr int V80 = 3; constexpr const int *V81 = &V80; >From d14018a89eee8cf549b4e2530b9ce4d2f51b27c5 Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Wed, 29 Nov 2023 09:23:19 -0800 Subject: [PATCH 4/8] Update a release note --- clang/docs/ReleaseNotes.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 55822d55b5b4f..a2c377d5f0a5d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -208,7 +208,8 @@ C23 Feature Support - Clang now supports ``<stdckdint.h>`` which defines several macros for performing checked integer arithmetic. It is also exposed in pre-C23 modes. -- Clang now supports ``N3018 The constexpr specifier for object definitions``. +- Clang now supports `N3018 The constexpr specifier for object definitions` + <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3018.htm>`_. - Completed the implementation of `N2508 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2508.pdf>`_. We >From 85ad720fb8bd78d0e72625557e2a26d9647e74eb Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Wed, 29 Nov 2023 09:28:05 -0800 Subject: [PATCH 5/8] Remove unused messages --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 016a099f01293..dc5e58fc4683a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2933,10 +2933,6 @@ def note_private_extern : Note< "use __attribute__((visibility(\"hidden\"))) attribute instead">; // C23 constexpr -def err_c23_thread_local_constexpr : Error< - "thread-local storage is not allowed with constexpr">; -def err_c23_extern_constexpr : Error< - "extern specifier is not allowed with constexpr">; def err_c23_constexpr_not_variable : Error< "'constexpr' can only be used in variable declarations">; def err_c23_constexpr_invalid_type : Error< >From 83d96777a93c38a91fef189b31269f5f6674d671 Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Thu, 30 Nov 2023 03:07:49 -0800 Subject: [PATCH 6/8] Update comments --- clang/lib/AST/Decl.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 678a366ed29ad..ce91f396081b1 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2483,11 +2483,16 @@ 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. + // 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 || Lang.C23) && isConstexpr(); >From 0d24ada6f485727f4c311d094381c7a405fd3cbb Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Mon, 4 Dec 2023 05:44:15 -0800 Subject: [PATCH 7/8] Use getCodeUnit --- clang/include/clang/AST/Expr.h | 11 +++++++++++ clang/lib/Sema/SemaDecl.cpp | 8 ++++---- clang/test/Sema/constexpr.c | 12 +++++++----- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index a9c4c67a60e8e..885df074f9be2 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1914,6 +1914,17 @@ class StringLiteral final llvm_unreachable("Unsupported character width!"); } + // Get code unit but preserve sign info. + int64_t getCodeUnitS(size_t I, uint64_t ByteWidth) const { + int64_t V = getCodeUnit(I); + if (isOrdinary() || isWide()) { + unsigned Width = getCharByteWidth() * ByteWidth; + 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/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 744e18747dbae..1811e4b8c6ce7 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14392,12 +14392,12 @@ static bool CheckC23ConstexprInitStringLiteral(const StringLiteral *SE, uint32_t BitWidth = SemaRef.Context.getTypeSize(CharType); bool isUnsigned = CharType->isUnsignedIntegerType(); llvm::APSInt Value(BitWidth, isUnsigned); - const StringRef S = SE->getBytes(); for (unsigned I = 0, N = SE->getLength(); I != N; ++I) { - Value = S[I]; - if (Value != S[I]) { + int64_t C = SE->getCodeUnitS(I, SemaRef.Context.getCharWidth()); + Value = C; + if (Value != C) { SemaRef.Diag(Loc, diag::err_c23_constexpr_init_not_representable) - << S[I] << CharType; + << C << CharType; return true; } } diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c index d8e68b56f918c..ba5168b8e2a77 100644 --- a/clang/test/Sema/constexpr.c +++ b/clang/test/Sema/constexpr.c @@ -199,10 +199,8 @@ void f5() { // expected-error@-1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}} }; - // FIXME Shouldn't be diagnosed if char_8t is supported. constexpr unsigned char V56[] = { u8"\xAF", - // expected-error@-1 {{constexpr initializer evaluates to -81 which is not exactly representable in type 'const unsigned char'}} }; constexpr struct S6 V57 = {299}; // expected-error@-1 {{constexpr initializer evaluates to 299 which is not exactly representable in type 'unsigned char'}} @@ -254,12 +252,16 @@ void f5() { 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 wstring[] = u8"test"u8"ing this out\xFF"; -constexpr unsigned char wustring[] = u8"test"u8"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 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 'unsigned int'}} // Check that initializer for pointer constexpr variable should be null. constexpr int V80 = 3; >From 6b740ae324730e014246a4a330c493e05517c3d4 Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Wed, 6 Dec 2023 03:32:55 -0800 Subject: [PATCH 8/8] Apply suggestions --- clang/include/clang/AST/Expr.h | 4 ++-- clang/lib/Sema/SemaDecl.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 885df074f9be2..ddef339054c44 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1915,10 +1915,10 @@ class StringLiteral final } // Get code unit but preserve sign info. - int64_t getCodeUnitS(size_t I, uint64_t ByteWidth) const { + int64_t getCodeUnitS(size_t I, uint64_t BitWidth) const { int64_t V = getCodeUnit(I); if (isOrdinary() || isWide()) { - unsigned Width = getCharByteWidth() * ByteWidth; + unsigned Width = getCharByteWidth() * BitWidth; llvm::APInt AInt(Width, (uint64_t)V); V = AInt.getSExtValue(); } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a90b0ecd015b4..f84f10a63027d 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13863,7 +13863,7 @@ 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. - // Avoid double diagnosing for constexpr variables. + // Avoid double diagnoses for constexpr variables. if (!getLangOpts().CPlusPlus && !VDecl->isInvalidDecl() && !VDecl->isConstexpr()) CheckForConstantInitializer(Init, DclT); @@ -14381,8 +14381,7 @@ static bool CheckC23ConstexprInitConversion(Sema &S, const Expr *Init) { } static bool CheckC23ConstexprInitStringLiteral(const StringLiteral *SE, - Sema &SemaRef, - SourceLocation Loc) { + Sema &SemaRef) { assert(SemaRef.getLangOpts().C23); // String literals have the target type attached but underneath may contain // values that don't really fit into the target type. Check that every @@ -14397,7 +14396,8 @@ static bool CheckC23ConstexprInitStringLiteral(const StringLiteral *SE, int64_t C = SE->getCodeUnitS(I, SemaRef.Context.getCharWidth()); Value = C; if (Value != C) { - SemaRef.Diag(Loc, diag::err_c23_constexpr_init_not_representable) + SemaRef.Diag(SemaRef.getLocationOfStringLiteralByte(SE, I), + diag::err_c23_constexpr_init_not_representable) << C << CharType; return true; } @@ -14412,7 +14412,7 @@ static bool CheckC23ConstexprInitializer(Sema &S, const Expr *Init) { return true; if (const auto *SE = dyn_cast<StringLiteral>(Init)) - if (CheckC23ConstexprInitStringLiteral(SE, S, Init->getBeginLoc())) + if (CheckC23ConstexprInitStringLiteral(SE, S)) return true; for (const Stmt *SubStmt : Init->children()) { _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits