diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 8a136aae5489a..a17efab57bcdf 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -206,6 +206,12 @@ Improvements to Clang's diagnostics - Clang no longer emits irrelevant notes about unsatisfied constraint expressions on the left-hand side of ``||`` when the right-hand side constraint is satisfied. (`#54678: `_). +- Clang now diagnoses wider cases of tautological use of consteval if or + ``std::is_constant_evaluated``. This also suppresses some false positives. + (`#43760: `_) + (`#51567: `_) +- Clang now diagnoses narrowing implicit conversions on variable initializers in immediate + function context and on constexpr variable template initializers. Bug Fixes in This Version ------------------------- diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index d2656310e79c9..1abb0b89af9f1 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -413,10 +413,6 @@ def warn_constexpr_unscoped_enum_out_of_range : Warning< def note_unimplemented_constexpr_lambda_feature_ast : Note< "unimplemented constexpr lambda feature: %0 (coming soon!)">; -def warn_is_constant_evaluated_always_true_constexpr : Warning< - "'%0' will always evaluate to 'true' in a manifestly constant-evaluated expression">, - InGroup>; - // inline asm related. let CategoryName = "Inline Assembly Issue" in { def err_asm_invalid_escape : Error< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f4eb02fd9570c..3f30681a378e2 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1591,8 +1591,8 @@ def err_static_assert_message_constexpr : Error< "the message in a static assertion must be produced by a " "constant expression">; -def warn_consteval_if_always_true : Warning< - "consteval if is always true in an %select{unevaluated|immediate}0 context">, +def warn_tautological_consteval_if : Warning< + "consteval if is always %select{true|false}0 in this context">, InGroup>; def ext_inline_variable : ExtWarn< @@ -8897,6 +8897,9 @@ def warn_side_effects_unevaluated_context : Warning< def warn_side_effects_typeid : Warning< "expression with side effects will be evaluated despite being used as an " "operand to 'typeid'">, InGroup; +def warn_tautological_is_constant_evaluated : Warning< + "'%select{std::is_constant_evaluated|__builtin_is_constant_evaluated}0' will always evaluate to %select{false|true}1 in this context">, + InGroup>; def warn_unused_result : Warning< "ignoring return value of function declared with %0 attribute">, InGroup; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index f599b8b98d031..ae3c265f40a2c 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2003,12 +2003,10 @@ class Parser : public CodeCompletionHandler { //===--------------------------------------------------------------------===// // C++ if/switch/while/for condition expression. struct ForRangeInfo; - Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt, - SourceLocation Loc, - Sema::ConditionKind CK, - bool MissingOK, - ForRangeInfo *FRI = nullptr, - bool EnterForConditionScope = false); + Sema::ConditionResult ParseCXXCondition( + StmtResult *InitStmt, SourceLocation Loc, Sema::ConditionKind CK, + bool MissingOK, ForRangeInfo *FRI = nullptr, + bool EnterForConditionScope = false, SourceLocation ConstexprLoc = {}); DeclGroupPtrTy ParseAliasDeclarationInInitStatement(DeclaratorContext Context, ParsedAttributes &Attrs); @@ -2106,7 +2104,8 @@ class Parser : public CodeCompletionHandler { Sema::ConditionResult &CondResult, SourceLocation Loc, Sema::ConditionKind CK, SourceLocation &LParenLoc, - SourceLocation &RParenLoc); + SourceLocation &RParenLoc, + SourceLocation ConstexprLoc = {}); StmtResult ParseIfStatement(SourceLocation *TrailingElseLoc); StmtResult ParseSwitchStatement(SourceLocation *TrailingElseLoc); StmtResult ParseWhileStatement(SourceLocation *TrailingElseLoc); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 712db0a3dd895..e4366170005a0 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1358,6 +1358,9 @@ class Sema final { bool InDiscardedStatement; bool InImmediateFunctionContext; bool InImmediateEscalatingFunctionContext; + // The immediate occurances of consteval if or std::is_constant_evaluated() + // are tautologically false + bool IsRuntimeEvaluated; bool IsCurrentlyCheckingDefaultArgumentOrInitializer = false; @@ -1387,7 +1390,8 @@ class Sema final { NumCleanupObjects(NumCleanupObjects), NumTypos(0), ManglingContextDecl(ManglingContextDecl), ExprContext(ExprContext), InDiscardedStatement(false), InImmediateFunctionContext(false), - InImmediateEscalatingFunctionContext(false) {} + InImmediateEscalatingFunctionContext(false), + IsRuntimeEvaluated(false) {} bool isUnevaluated() const { return Context == ExpressionEvaluationContext::Unevaluated || @@ -1426,6 +1430,10 @@ class Sema final { /// A stack of expression evaluation contexts. SmallVector ExprEvalContexts; + /// Source location of the start of `constexpr` in constexpr-if + /// used for diagnostics + SourceLocation ConstexprIfLoc; + // Set of failed immediate invocations to avoid double diagnosing. llvm::SmallPtrSet FailedImmediateInvocations; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index fea06b97259fe..310debe327017 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -12178,22 +12178,6 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, } case Builtin::BI__builtin_is_constant_evaluated: { - const auto *Callee = Info.CurrentCall->getCallee(); - if (Info.InConstantContext && !Info.CheckingPotentialConstantExpression && - (Info.CallStackDepth == 1 || - (Info.CallStackDepth == 2 && Callee->isInStdNamespace() && - Callee->getIdentifier() && - Callee->getIdentifier()->isStr("is_constant_evaluated")))) { - // FIXME: Find a better way to avoid duplicated diagnostics. - if (Info.EvalStatus.Diag) - Info.report((Info.CallStackDepth == 1) - ? E->getExprLoc() - : Info.CurrentCall->getCallRange().getBegin(), - diag::warn_is_constant_evaluated_always_true_constexpr) - << (Info.CallStackDepth == 1 ? "__builtin_is_constant_evaluated" - : "std::is_constant_evaluated"); - } - return Success(Info.InConstantContext, E); } diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 748b9d53c9f5b..f5b4107cc32c1 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2461,6 +2461,15 @@ Decl *Parser::ParseDeclarationAfterDeclarator( return ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo); } +/// Determine whether the given declaration is a global variable or +/// static data member. +static bool isNonlocalVariable(const Decl *D) { + if (const VarDecl *Var = dyn_cast_or_null(D)) + return Var->hasGlobalStorage(); + + return false; +} + Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( Declarator &D, const ParsedTemplateInfo &TemplateInfo, ForRangeInit *FRI) { // RAII type used to track whether we're inside an initializer. @@ -2493,6 +2502,36 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( ThisDecl = nullptr; } }; + struct EnterInitializerExpressionEvaluationContext { + Sema &S; + bool Entered; + + EnterInitializerExpressionEvaluationContext(Sema &S, Declarator &D, + Decl *ThisDecl) + : S(S), Entered(false) { + if (ThisDecl && S.getLangOpts().CPlusPlus && !ThisDecl->isInvalidDecl()) { + Entered = true; + bool RuntimeEvaluated = S.ExprEvalContexts.back().IsRuntimeEvaluated; + Sema::ExpressionEvaluationContext NewEEC = + S.ExprEvalContexts.back().Context; + if ((D.getDeclSpec().getTypeQualifiers() == DeclSpec::TQ_const || + isNonlocalVariable(ThisDecl)) && + S.ExprEvalContexts.back().IsRuntimeEvaluated) { + RuntimeEvaluated = false; + } + if (D.getDeclSpec().hasConstexprSpecifier()) { + NewEEC = Sema::ExpressionEvaluationContext::ConstantEvaluated; + RuntimeEvaluated = false; + } + S.PushExpressionEvaluationContext(NewEEC, ThisDecl); + S.ExprEvalContexts.back().IsRuntimeEvaluated = RuntimeEvaluated; + } + } + ~EnterInitializerExpressionEvaluationContext() { + if (Entered) + S.PopExpressionEvaluationContext(); + } + }; enum class InitKind { Uninitialized, Equal, CXXDirect, CXXBraced }; InitKind TheInitKind; @@ -2592,6 +2631,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( << getLangOpts().CPlusPlus20; } else { InitializerScopeRAII InitScope(*this, D, ThisDecl); + EnterInitializerExpressionEvaluationContext InitEC(Actions, D, ThisDecl); if (Tok.is(tok::code_completion)) { cutOffParsing(); @@ -2639,6 +2679,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( ExprVector Exprs; InitializerScopeRAII InitScope(*this, D, ThisDecl); + EnterInitializerExpressionEvaluationContext InitEC(Actions, D, ThisDecl); auto ThisVarDecl = dyn_cast_or_null(ThisDecl); auto RunSignatureHelp = [&]() { @@ -2689,6 +2730,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); InitializerScopeRAII InitScope(*this, D, ThisDecl); + EnterInitializerExpressionEvaluationContext InitEC(Actions, D, ThisDecl); PreferredType.enterVariableInit(Tok.getLocation(), ThisDecl); ExprResult Init(ParseBraceInitializer()); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 5a6b5efbf6c12..e5a278c598cfb 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -3234,9 +3234,14 @@ ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction, bool IsFieldInitialization = isa_and_present(D); + bool IsConstexpr = false; + if (const auto *VD = dyn_cast_if_present(D)) + IsConstexpr = VD->isConstexpr(); + EnterExpressionEvaluationContext Context( Actions, - IsFieldInitialization + IsConstexpr ? Sema::ExpressionEvaluationContext::ConstantEvaluated + : IsFieldInitialization ? Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed : Sema::ExpressionEvaluationContext::PotentiallyEvaluated, D); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 74664c34abdbd..0e69be6bcb3ce 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -2093,6 +2093,13 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { }; if (OpKind == tok::l_paren || !LHS.isInvalid()) { if (Tok.isNot(tok::r_paren)) { + // FIXME: arguments in consteval functions are constant expression + // regardless of the evaluation context of callsite. However, we + // cannot know whether the called function is constevasl before the + // declaration is resolved. + bool IsRuntimeEvaluated = + Actions.ExprEvalContexts.back().IsRuntimeEvaluated; + Actions.ExprEvalContexts.back().IsRuntimeEvaluated = false; if (ParseExpressionList(ArgExprs, [&] { PreferredType.enterFunctionArgument(Tok.getLocation(), RunSignatureHelp); @@ -2109,6 +2116,8 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { for (auto &E : ArgExprs) Actions.CorrectDelayedTyposInExpr(E); } + Actions.ExprEvalContexts.back().IsRuntimeEvaluated = + IsRuntimeEvaluated; } } diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 99b4931004546..d20d400e3076e 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -2041,7 +2041,8 @@ Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context, Sema::ConditionResult Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc, Sema::ConditionKind CK, bool MissingOK, - ForRangeInfo *FRI, bool EnterForConditionScope) { + ForRangeInfo *FRI, bool EnterForConditionScope, + SourceLocation ConstexprLoc) { // Helper to ensure we always enter a continue/break scope if requested. struct ForConditionScopeRAII { Scope *S; @@ -2098,9 +2099,28 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc, *InitStmt = Actions.ActOnNullStmt(SemiLoc); return ParseCXXCondition(nullptr, Loc, CK, MissingOK); } + bool InitStmtIsExprStmt = false; + if (InitStmt) { + RevertingTentativeParsingAction PA(*this); + SkipUntil(tok::r_paren, tok::semi, StopBeforeMatch); + InitStmtIsExprStmt = Tok.is(tok::semi); + } - // Parse the expression. - ExprResult Expr = ParseExpression(); // expression + ExprResult Expr; // expression + { + EnterExpressionEvaluationContext Consteval( + Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated, + /*LambdaContextDecl=*/nullptr, + Sema::ExpressionEvaluationContextRecord::EK_Other, + /*ShouldEnter=*/CK == Sema::ConditionKind::ConstexprIf && + !InitStmtIsExprStmt); + SourceLocation OuterConstexprIfLoc = Actions.ConstexprIfLoc; + Actions.ConstexprIfLoc = ConstexprLoc; + + // Parse the expression. + Expr = ParseExpression(); // expression + Actions.ConstexprIfLoc = OuterConstexprIfLoc; + } if (Expr.isInvalid()) return Sema::ConditionError(); @@ -2188,6 +2208,21 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc, if (CopyInitialization) ConsumeToken(); + Sema::ExpressionEvaluationContext NewEEC = + Actions.ExprEvalContexts.back().Context; + bool RuntimeEvaluated = Actions.ExprEvalContexts.back().IsRuntimeEvaluated; + if (DS.getTypeQualifiers() == DeclSpec::TQ_const) + RuntimeEvaluated = false; + + if (CK == Sema::ConditionKind::ConstexprIf || DS.hasConstexprSpecifier()) { + RuntimeEvaluated = false; + NewEEC = Sema::ExpressionEvaluationContext::ConstantEvaluated; + } + EnterExpressionEvaluationContext Initializer(Actions, NewEEC, DeclOut); + Actions.ExprEvalContexts.back().IsRuntimeEvaluated = RuntimeEvaluated; + SourceLocation OuterConstexprIfLoc = Actions.ConstexprIfLoc; + Actions.ConstexprIfLoc = ConstexprLoc; + ExprResult InitExpr = ExprError(); if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { Diag(Tok.getLocation(), @@ -2214,6 +2249,7 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc, Actions.ActOnInitializerError(DeclOut); Actions.FinalizeDeclaration(DeclOut); + Actions.ConstexprIfLoc = OuterConstexprIfLoc; return Actions.ActOnConditionVariable(DeclOut, Loc, CK); } diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 2531147c23196..75c689f21efa2 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -1293,18 +1293,17 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { /// errors in the condition. /// Additionally, it will assign the location of the outer-most '(' and ')', /// to LParenLoc and RParenLoc, respectively. -bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt, - Sema::ConditionResult &Cond, - SourceLocation Loc, - Sema::ConditionKind CK, - SourceLocation &LParenLoc, - SourceLocation &RParenLoc) { +bool Parser::ParseParenExprOrCondition( + StmtResult *InitStmt, Sema::ConditionResult &Cond, SourceLocation Loc, + Sema::ConditionKind CK, SourceLocation &LParenLoc, + SourceLocation &RParenLoc, SourceLocation ConstexprLoc) { BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); SourceLocation Start = Tok.getLocation(); if (getLangOpts().CPlusPlus) { - Cond = ParseCXXCondition(InitStmt, Loc, CK, false); + Cond = ParseCXXCondition(InitStmt, Loc, CK, false, nullptr, false, + ConstexprLoc); } else { ExprResult CondExpr = ParseExpression(); @@ -1464,12 +1463,13 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { bool IsConsteval = false; SourceLocation NotLocation; SourceLocation ConstevalLoc; + SourceLocation ConstexprLoc; if (Tok.is(tok::kw_constexpr)) { Diag(Tok, getLangOpts().CPlusPlus17 ? diag::warn_cxx14_compat_constexpr_if : diag::ext_constexpr_if); IsConstexpr = true; - ConsumeToken(); + ConstexprLoc = ConsumeToken(); } else { if (Tok.is(tok::exclaim)) { NotLocation = ConsumeToken(); @@ -1515,7 +1515,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { if (ParseParenExprOrCondition(&InitStmt, Cond, IfLoc, IsConstexpr ? Sema::ConditionKind::ConstexprIf : Sema::ConditionKind::Boolean, - LParen, RParen)) + LParen, RParen, ConstexprLoc)) return StmtError(); if (IsConstexpr) @@ -1558,11 +1558,16 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { if (NotLocation.isInvalid() && IsConsteval) { Context = Sema::ExpressionEvaluationContext::ImmediateFunctionContext; ShouldEnter = true; + } else if (NotLocation.isValid() && IsConsteval) { + Context = Actions.ExprEvalContexts.back().Context; + ShouldEnter = true; } EnterExpressionEvaluationContext PotentiallyDiscarded( Actions, Context, nullptr, Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter); + if (NotLocation.isValid() && IsConsteval) + Actions.ExprEvalContexts.back().IsRuntimeEvaluated = true; ThenStmt = ParseStatement(&InnerStatementTrailingElseLoc); } @@ -1603,11 +1608,16 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { if (NotLocation.isValid() && IsConsteval) { Context = Sema::ExpressionEvaluationContext::ImmediateFunctionContext; ShouldEnter = true; + } else if (NotLocation.isInvalid() && IsConsteval) { + Context = Actions.ExprEvalContexts.back().Context; + ShouldEnter = true; } EnterExpressionEvaluationContext PotentiallyDiscarded( Actions, Context, nullptr, Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter); + if (NotLocation.isInvalid() && IsConsteval) + Actions.ExprEvalContexts.back().IsRuntimeEvaluated = true; ElseStmt = ParseStatement(); if (ElseStmt.isUsable()) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 1d56c495e8997..5fe8c1afec76f 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -14832,7 +14832,8 @@ static void DiagnoseIntInBoolContext(Sema &S, Expr *E) { static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, SourceLocation CC, bool *ICContext = nullptr, - bool IsListInit = false) { + bool IsListInit = false, + bool IsConstexprInit = false) { if (E->isTypeDependent() || E->isValueDependent()) return; const Type *Source = S.Context.getCanonicalType(E->getType()).getTypePtr(); @@ -15141,11 +15142,14 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, SmallString<32> PrettyTargetValue; TargetFloatValue.toString(PrettyTargetValue, TargetPrecision); - S.DiagRuntimeBehavior( - E->getExprLoc(), E, + PartialDiagnostic PD = S.PDiag(diag::warn_impcast_integer_float_precision_constant) - << PrettySourceValue << PrettyTargetValue << E->getType() << T - << E->getSourceRange() << clang::SourceRange(CC)); + << PrettySourceValue << PrettyTargetValue << E->getType() << T + << E->getSourceRange() << clang::SourceRange(CC); + if (IsConstexprInit) + S.Diag(E->getExprLoc(), PD); + else + S.DiagRuntimeBehavior(E->getExprLoc(), E, PD); } } else { // Otherwise, the implicit conversion may lose precision. @@ -15199,11 +15203,14 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, std::string PrettySourceValue = toString(Value, 10); std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange); - S.DiagRuntimeBehavior( - E->getExprLoc(), E, + PartialDiagnostic PD = S.PDiag(diag::warn_impcast_integer_precision_constant) - << PrettySourceValue << PrettyTargetValue << E->getType() << T - << E->getSourceRange() << SourceRange(CC)); + << PrettySourceValue << PrettyTargetValue << E->getType() << T + << E->getSourceRange() << SourceRange(CC); + if (IsConstexprInit) + S.Diag(E->getExprLoc(), PD); + else + S.DiagRuntimeBehavior(E->getExprLoc(), E, PD); return; } @@ -15245,11 +15252,14 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, std::string PrettySourceValue = toString(Value, 10); std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange); - S.DiagRuntimeBehavior( - E->getExprLoc(), E, + PartialDiagnostic PD = S.PDiag(diag::warn_impcast_integer_precision_constant) - << PrettySourceValue << PrettyTargetValue << E->getType() << T - << E->getSourceRange() << SourceRange(CC)); + << PrettySourceValue << PrettyTargetValue << E->getType() << T + << E->getSourceRange() << SourceRange(CC); + if (IsConstexprInit) + S.Diag(E->getExprLoc(), PD); + else + S.DiagRuntimeBehavior(E->getExprLoc(), E, PD); return; } } @@ -15411,6 +15421,17 @@ static void AnalyzeImplicitConversions( if (auto *Src = OVE->getSourceExpr()) SourceExpr = Src; + bool IsConstexprInit = + S.isConstantEvaluated() && + isa_and_present(S.ExprEvalContexts.back().ManglingContextDecl); + // Constant-evaluated initializers are not diagnosed by DiagRuntimeBehavior, + // but narrowings from the evaluated result to the variable type should be + // diagnosed. + if (IsConstexprInit && SourceExpr->getType() != T) { + CheckImplicitConversion(S, SourceExpr, T, CC, nullptr, IsListInit, + /*IsConstexprInit=*/true); + } + if (const auto *UO = dyn_cast(SourceExpr)) if (UO->getOpcode() == UO_Not && UO->getSubExpr()->isKnownToHaveBooleanValue()) @@ -15446,7 +15467,7 @@ static void AnalyzeImplicitConversions( // Go ahead and check any implicit conversions we might have skipped. // The non-canonical typecheck is just an optimization; // CheckImplicitConversion will filter out dead implicit conversions. - if (SourceExpr->getType() != T) + if (!IsConstexprInit && SourceExpr->getType() != T) CheckImplicitConversion(S, SourceExpr, T, CC, nullptr, IsListInit); // Now continue drilling into this expression. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 7d92e93188610..30e4af16944ab 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15443,7 +15443,7 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, // Do not push if it is a lambda because one is already pushed when building // the lambda in ActOnStartOfLambdaDefinition(). - if (!isLambdaCallOperator(FD)) + if (!isLambdaCallOperator(FD)) { // [expr.const]/p14.1 // An expression or conversion is in an immediate function context if it is // potentially evaluated and either: its innermost enclosing non-block scope @@ -15451,6 +15451,9 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, PushExpressionEvaluationContext( FD->isConsteval() ? ExpressionEvaluationContext::ImmediateFunctionContext : ExprEvalContexts.back().Context); + if (!FD->isConsteval() && !FD->isConstexpr()) + ExprEvalContexts.back().IsRuntimeEvaluated = true; + } // Each ExpressionEvaluationContextRecord also keeps track of whether the // context is nested in an immediate function context, so smaller contexts diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 0091e0ecf6f39..3d5ecfc2eb256 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18241,15 +18241,6 @@ void Sema::ActOnPureSpecifier(Decl *D, SourceLocation ZeroLoc) { Diag(D->getLocation(), diag::err_illegal_initializer); } -/// Determine whether the given declaration is a global variable or -/// static data member. -static bool isNonlocalVariable(const Decl *D) { - if (const VarDecl *Var = dyn_cast_or_null(D)) - return Var->hasGlobalStorage(); - - return false; -} - /// Invoked when we are about to parse an initializer for the declaration /// 'Dcl'. /// @@ -18272,9 +18263,6 @@ void Sema::ActOnCXXEnterDeclInitializer(Scope *S, Decl *D) { // If we are parsing the initializer for a static data member, push a // new expression evaluation context that is associated with this static // data member. - if (isNonlocalVariable(D)) - PushExpressionEvaluationContext( - ExpressionEvaluationContext::PotentiallyEvaluated, D); } /// Invoked after we are finished parsing an initializer for the declaration D. @@ -18283,9 +18271,6 @@ void Sema::ActOnCXXExitDeclInitializer(Scope *S, Decl *D) { if (!D || D->isInvalidDecl()) return; - if (isNonlocalVariable(D)) - PopExpressionEvaluationContext(); - if (S && D->isOutOfLine()) ExitDeclaratorContext(S); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 92496b03ecabe..305b437437747 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7091,6 +7091,32 @@ static void DiagnosedUnqualifiedCallsToStdFunctions(Sema &S, << FixItHint::CreateInsertion(DRE->getLocation(), "std::"); } +// Diagnose uses of std::is_constant_evaluated or +// __builtin_is_constant_evaluated in contexts where the result is known at +// compile time. +static void DiagnoseTautologicalCallToIsConstantEvaluated(Sema &S, + const CallExpr *CE) { + if (S.inTemplateInstantiation() || CE->getBeginLoc().isMacroID()) + return; + if (const FunctionDecl *FD = CE->getDirectCallee()) { + bool IsBuiltin = + FD->getBuiltinID() == Builtin::BI__builtin_is_constant_evaluated; + SourceLocation ConstexprIfLoc = S.ConstexprIfLoc; + + if ((FD->isInStdNamespace() && + FD->getNameAsString() == "is_constant_evaluated") || + IsBuiltin) { + bool AlwaysTrue = S.ExprEvalContexts.back().isConstantEvaluated() || + S.ExprEvalContexts.back().isUnevaluated(); + bool AlwaysFalse = S.ExprEvalContexts.back().IsRuntimeEvaluated; + if (AlwaysTrue || AlwaysFalse) + S.Diag(CE->getBeginLoc(), diag::warn_tautological_is_constant_evaluated) + << IsBuiltin << AlwaysTrue + << FixItHint::CreateRemoval(ConstexprIfLoc); + } + } +} + ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig) { @@ -7115,8 +7141,10 @@ ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, Call = ActOnOpenMPCall(Call, Scope, LParenLoc, ArgExprs, RParenLoc, ExecConfig); if (LangOpts.CPlusPlus) { - if (const auto *CE = dyn_cast(Call.get())) + if (const auto *CE = dyn_cast(Call.get())) { DiagnosedUnqualifiedCallsToStdFunctions(*this, CE); + DiagnoseTautologicalCallToIsConstantEvaluated(*this, CE); + } } return Call; } @@ -18608,6 +18636,8 @@ void Sema::PopExpressionEvaluationContext() { } else llvm_unreachable("Couldn't infer lambda error message."); + if (auto *VD = dyn_cast_if_present(Rec.ManglingContextDecl)) + VD->setInvalidDecl(); for (const auto *L : Rec.Lambdas) Diag(L->getBeginLoc(), D); } diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 1702ddb3ee0fb..bd2c3c2f45d4c 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -1444,12 +1444,13 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro, // Enter a new evaluation context to insulate the lambda from any // cleanups from the enclosing full-expression. + bool InImmediateFunctionContext = isImmediateFunctionContext(); PushExpressionEvaluationContext( - LSI->CallOperator->isConsteval() + LSI->CallOperator->isConsteval() || InImmediateFunctionContext ? ExpressionEvaluationContext::ImmediateFunctionContext : ExpressionEvaluationContext::PotentiallyEvaluated); ExprEvalContexts.back().InImmediateFunctionContext = - LSI->CallOperator->isConsteval(); + LSI->CallOperator->isConsteval() || InImmediateFunctionContext; ExprEvalContexts.back().InImmediateEscalatingFunctionContext = getLangOpts().CPlusPlus20 && LSI->CallOperator->isImmediateEscalating(); } diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 10adfbc406dfb..c20d0c50b09e1 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -933,16 +933,14 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, } if (ConstevalOrNegatedConsteval) { - bool Immediate = ExprEvalContexts.back().Context == - ExpressionEvaluationContext::ImmediateFunctionContext; - if (CurContext->isFunctionOrMethod()) { - const auto *FD = - dyn_cast(Decl::castFromDeclContext(CurContext)); - if (FD && FD->isImmediateFunction()) - Immediate = true; - } - if (isUnevaluatedContext() || Immediate) - Diags.Report(IfLoc, diag::warn_consteval_if_always_true) << Immediate; + bool AlwaysTrue = ExprEvalContexts.back().isConstantEvaluated() || + ExprEvalContexts.back().isUnevaluated(); + bool AlwaysFalse = ExprEvalContexts.back().IsRuntimeEvaluated; + if (AlwaysTrue || AlwaysFalse) + Diags.Report(IfLoc, diag::warn_tautological_consteval_if) + << (AlwaysTrue + ? StatementKind == IfStatementKind::ConstevalNegated + : StatementKind == IfStatementKind::ConstevalNonNegated); } return BuildIfStmt(IfLoc, StatementKind, LParenLoc, InitStmt, Cond, RParenLoc, diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index fa839e9b71a3c..c4f1c4e06ac83 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5378,8 +5378,11 @@ void Sema::InstantiateVariableInitializer( Var->setImplicitlyInline(); if (OldVar->getInit()) { - EnterExpressionEvaluationContext Evaluated( - *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var); + Sema::ExpressionEvaluationContext InitEvalContext = + Var->isConstexpr() + ? Sema::ExpressionEvaluationContext::ConstantEvaluated + : Sema::ExpressionEvaluationContext::PotentiallyEvaluated; + EnterExpressionEvaluationContext Evaluated(*this, InitEvalContext, Var); // Instantiate the initializer. ExprResult Init; diff --git a/clang/test/AST/Interp/builtins.cpp b/clang/test/AST/Interp/builtins.cpp index 5e2ffe50f3740..90bd290f1d6a4 100644 --- a/clang/test/AST/Interp/builtins.cpp +++ b/clang/test/AST/Interp/builtins.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -verify -// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -S -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -Wno-constant-evaluated -verify +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -Wno-constant-evaluated -S -emit-llvm -o - | FileCheck %s // RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated // RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated %s -S -emit-llvm -o - | FileCheck %s diff --git a/clang/test/AST/Interp/if.cpp b/clang/test/AST/Interp/if.cpp index 86ae8de6f73eb..3d1ed7b7ffe76 100644 --- a/clang/test/AST/Interp/if.cpp +++ b/clang/test/AST/Interp/if.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fexperimental-new-constant-interpreter %s -verify -// RUN: %clang_cc1 -std=c++23 -fsyntax-only %s -verify=ref +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fexperimental-new-constant-interpreter -Wno-redundant-consteval-if %s -verify +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -Wno-redundant-consteval-if %s -verify=ref // expected-no-diagnostics // ref-no-diagnostics diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp index aabc909b3328e..67de4ef6dae2a 100644 --- a/clang/test/AST/Interp/literals.cpp +++ b/clang/test/AST/Interp/literals.cpp @@ -225,15 +225,11 @@ namespace SizeOf { #if __cplusplus >= 202002L /// FIXME: The following code should be accepted. consteval int foo(int n) { // ref-error {{consteval function never produces a constant expression}} - return sizeof(int[n]); // ref-note 3{{not valid in a constant expression}} \ - // expected-note {{not valid in a constant expression}} + return sizeof(int[n]); // ref-note 2 {{not valid in a constant expression}} } - constinit int var = foo(5); // ref-error {{not a constant expression}} \ - // ref-note 2{{in call to}} \ + constinit int var = foo(5); // ref-note {{in call to}} \ // ref-error {{does not have a constant initializer}} \ // ref-note {{required by 'constinit' specifier}} \ - // expected-error {{is not a constant expression}} \ - // expected-note {{in call to}} \ // expected-error {{does not have a constant initializer}} \ // expected-note {{required by 'constinit' specifier}} \ diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp index e3cd057baba75..596df90243b1e 100644 --- a/clang/test/CXX/expr/expr.const/p2-0x.cpp +++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp @@ -244,8 +244,8 @@ namespace UndefinedBehavior { constexpr int n13 = n5 + n5; // expected-error {{constant expression}} expected-note {{value -4294967296 is outside the range of }} constexpr int n14 = n3 - n5; // expected-error {{constant expression}} expected-note {{value 4294967295 is outside the range of }} constexpr int n15 = n5 * n5; // expected-error {{constant expression}} expected-note {{value 4611686018427387904 is outside the range of }} - constexpr signed char c1 = 100 * 2; // ok expected-warning{{changes value}} - constexpr signed char c2 = '\x64' * '\2'; // also ok expected-warning{{changes value}} + constexpr signed char c1 = 100 * 2; // ok expected-warning {{changes value from 200 to -56}} + constexpr signed char c2 = '\x64' * '\2'; // also ok expected-warning {{changes value from 200 to -56}} constexpr long long ll1 = 0x7fffffffffffffff; // ok constexpr long long ll2 = ll1 + 1; // expected-error {{constant}} expected-note {{ 9223372036854775808 }} constexpr long long ll3 = -ll1 - 1; // ok diff --git a/clang/test/CXX/expr/expr.const/p6-2a.cpp b/clang/test/CXX/expr/expr.const/p6-2a.cpp index a937474d53b22..7bcf6fdc3d997 100644 --- a/clang/test/CXX/expr/expr.const/p6-2a.cpp +++ b/clang/test/CXX/expr/expr.const/p6-2a.cpp @@ -43,12 +43,11 @@ struct Temporary { constexpr Temporary t = {3}; // expected-error {{must have constant destruction}} expected-note {{created here}} expected-note {{in call}} namespace P1073R3 { -consteval int f() { return 42; } // expected-note 2 {{declared here}} +consteval int f() { return 42; } // expected-note {{declared here}} consteval auto g() { return f; } consteval int h(int (*p)() = g()) { return p(); } constexpr int r = h(); -constexpr auto e = g(); // expected-error {{call to consteval function 'P1073R3::g' is not a constant expression}} \ - expected-error {{constexpr variable 'e' must be initialized by a constant expression}} \ - expected-note 2 {{pointer to a consteval declaration is not a constant expression}} +constexpr auto e = g(); // expected-error {{constexpr variable 'e' must be initialized by a constant expression}} \ + expected-note {{pointer to a consteval declaration is not a constant expression}} static_assert(r == 42); } // namespace P1073R3 diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p3.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p3.cpp index db40bd5d1420e..8b30b5eefd85f 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p3.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p3.cpp @@ -16,4 +16,5 @@ constexpr auto literal = []{}; #if __cplusplus < 201703L // expected-error@-2 {{constexpr variable cannot have non-literal type}} // expected-note@-3 {{lambda closure types are non-literal types before C++17}} +// expected-error@-4 {{a lambda expression may not appear inside of a constant expression}} #endif diff --git a/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p4.cpp b/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p4.cpp index 8d43be6fc9047..fd2deee249016 100644 --- a/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p4.cpp +++ b/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p4.cpp @@ -38,7 +38,8 @@ void test_consteval() { }() == 1); if consteval [[likely]] { // expected-warning {{attribute 'likely' has no effect when annotating an 'if consteval' statement}}\ - // expected-note 2{{annotating the 'if consteval' statement here}} + // expected-note 2{{annotating the 'if consteval' statement here}} \ + // expected-warning {{consteval if is always false}} } @@ -49,7 +50,8 @@ void test_consteval() { } void test_consteval_jumps() { - if consteval { // expected-note 4{{jump enters controlled statement of consteval if}} + if consteval { // expected-warning {{consteval if is always false}} \ + // expected-note 4{{jump enters controlled statement of consteval if}} goto a; goto b; // expected-error {{cannot jump from this goto statement to its label}} a:; @@ -65,14 +67,16 @@ void test_consteval_jumps() { void test_consteval_switch() { int x = 42; switch (x) { - if consteval { // expected-note 2{{jump enters controlled statement of consteval if}} + if consteval { // expected-warning {{consteval if is always false}} \ + // expected-note 2{{jump enters controlled statement of consteval if}} case 1:; // expected-error {{cannot jump from switch statement to this case label}} default:; // expected-error {{cannot jump from switch statement to this case label}} } else { } } switch (x) { - if consteval { // expected-note 2{{jump enters controlled statement of consteval if}} + if consteval { // expected-warning {{consteval if is always false}} \ + // expected-note 2{{jump enters controlled statement of consteval if}} } else { case 2:; // expected-error {{cannot jump from switch statement to this case label}} default:; // expected-error {{cannot jump from switch statement to this case label}} @@ -99,32 +103,32 @@ constexpr int h(int i) { // expected-note {{declared here}} } consteval void warn_in_consteval() { - if consteval { // expected-warning {{consteval if is always true in an immediate context}} - if consteval {} // expected-warning {{consteval if is always true in an immediate context}} + if consteval { // expected-warning {{consteval if is always true in this context}} + if consteval {} // expected-warning {{consteval if is always true in this context}} } } constexpr void warn_in_consteval2() { if consteval { - if consteval {} // expected-warning {{consteval if is always true in an immediate context}} + if consteval {} // expected-warning {{consteval if is always true in this context}} } } auto y = []() consteval { - if consteval { // expected-warning {{consteval if is always true in an immediate context}} - if consteval {} // expected-warning {{consteval if is always true in an immediate context}} + if consteval { // expected-warning {{consteval if is always true in this context}} + if consteval {} // expected-warning {{consteval if is always true in this context}} } }; namespace test_transform { int f(auto n) { - if consteval { + if consteval { // expected-warning {{consteval if is always false}} n.foo; //expected-error {{no member named}} } else { } - if !consteval { + if !consteval { // expected-warning {{consteval if is always true}} n.foo; //expected-error {{no member named}} } else { diff --git a/clang/test/Parser/pragma-fenv_access.c b/clang/test/Parser/pragma-fenv_access.c index 76256cff1b49b..a626453344ee6 100644 --- a/clang/test/Parser/pragma-fenv_access.c +++ b/clang/test/Parser/pragma-fenv_access.c @@ -33,7 +33,7 @@ int main(void) { CONST float fnot_too_big = not_too_big; CONST int too_big = 0x7ffffff0; #if defined(CPP) -//expected-warning@+2{{implicit conversion}} +//expected-warning@+2{{implicit conversion from 'const int' to 'const float' changes value from 2147483632 to 2147483648}} #endif CONST float fbig = too_big; // inexact #if !defined(CPP) diff --git a/clang/test/SemaCXX/constant-conversion.cpp b/clang/test/SemaCXX/constant-conversion.cpp index 9be8b139e79e2..884166c419764 100644 --- a/clang/test/SemaCXX/constant-conversion.cpp +++ b/clang/test/SemaCXX/constant-conversion.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-apple-darwin %s +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify -triple x86_64-apple-darwin %s // This file tests -Wconstant-conversion, a subcategory of -Wconversion // which is on by default. @@ -31,3 +31,59 @@ void test_bitfield() { s.one_bit = 1; // expected-warning {{implicit truncation from 'int' to a one-bit wide bit-field changes value from 1 to -1}} s.one_bit = true; // no-warning } + +namespace Initializers { +constexpr char ok = true ? 0 : 200; +constexpr char a = 200; // expected-warning {{implicit conversion from 'int' to 'const char' changes value from 200 to -56}} +char b = 200; // expected-warning {{implicit conversion from 'int' to 'char' changes value from 200 to -56}} +const char c = 200; // expected-warning {{implicit conversion from 'int' to 'const char' changes value from 200 to -56}} + +void f() { + constexpr char a = 200; // expected-warning {{implicit conversion from 'int' to 'const char' changes value from 200 to -56}} + char b = 200; // expected-warning {{implicit conversion from 'int' to 'char' changes value from 200 to -56}} + const char c = 200; // expected-warning {{implicit conversion from 'int' to 'const char' changes value from 200 to -56}} + static char d = 2 * 100; // expected-warning {{implicit conversion from 'int' to 'char' changes value from 200 to -56}} +} + +constexpr void g() { + constexpr char a = 2 * 100; // expected-warning {{implicit conversion from 'int' to 'const char' changes value from 200 to -56}} + char b = 2 * 100; // expected-warning {{implicit conversion from 'int' to 'char' changes value from 200 to -56}} + const char c = 2 * 100; // expected-warning {{implicit conversion from 'int' to 'const char' changes value from 200 to -56}} +} + +consteval void h() { + char ok = true ? 0 : 200; + constexpr char a = 200; // expected-warning {{implicit conversion from 'int' to 'const char' changes value from 200 to -56}} + char b = 200; // expected-warning {{implicit conversion from 'int' to 'char' changes value from 200 to -56}} + const char c = 200; // expected-warning {{implicit conversion from 'int' to 'const char' changes value from 200 to -56}} +} + +template +int templ() { + constexpr char a = false ? 129 : N; // expected-warning {{implicit conversion from 'int' to 'const char' changes value from 200 to -56}} \ + // expected-warning {{implicit conversion from 'int' to 'const char' changes value from 345 to 89}} + return 3; +} + +void call_templ() { + int ok = templ<127>(); + int l = templ<3>(); + int m = templ<200>(); // expected-note {{in instantiation of}} + int n = templ<345>(); // expected-note {{in instantiation of}} +} + +template +constexpr signed char diff = a > b ? a - b : b - a; // expected-warning{{changes value from 201 to -55}} \ + // expected-warning{{changes value from 199 to -57}} \ + // expected-warning{{changes value from 299 to 43}} \ + // expected-warning{{changes value from 301 to 45}} + +void test_diff() { + char ok1 = diff<201, 100>; + char ok2 = diff<101, 200>; + char s1 = diff<301, 100>; // expected-note {{in instantiation of}} + char s2 = diff<101, 300>; // expected-note {{in instantiation of}} + char w1 = diff<101, 400>; // expected-note {{in instantiation of}} + char w2 = diff<401, 100>; // expected-note {{in instantiation of}} +} +} diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index 89d1b3ea6de05..8e6af7811a30b 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -1961,7 +1961,7 @@ namespace ConstexprConstructorRecovery { namespace Lifetime { void f() { - constexpr int &n = n; // expected-error {{constant expression}} expected-note {{use of reference outside its lifetime}} expected-warning {{not yet bound to a value}} + constexpr int &n = n; // expected-error {{constant expression}} expected-note {{use of reference outside its lifetime}} constexpr int m = m; // expected-error {{constant expression}} expected-note {{read of object outside its lifetime}} } diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp index a091fadfa3094..823778f25e167 100644 --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -713,7 +713,7 @@ constexpr derp d; struct test { consteval int operator[](int i) const { return {}; } consteval const derp * operator->() const { return &d; } - consteval int f() const { return 12; } // expected-note 2{{declared here}} + consteval int f() const { return 12; } // expected-note {{declared here}} }; constexpr test a; @@ -726,8 +726,7 @@ constexpr int s = a.operator[](1); constexpr int t = a[1]; constexpr int u = a.operator->()->b; constexpr int v = a->b; -// FIXME: I believe this case should work, but we currently reject. -constexpr int w = (a.*&test::f)(); // expected-error {{cannot take address of consteval function 'f' outside of an immediate invocation}} +constexpr int w = (a.*&test::f)(); constexpr int x = a.f(); // Show that we reject when not in an immediate context. @@ -1073,18 +1072,17 @@ struct tester { consteval const char* make_name(const char* name) { return name;} consteval const char* pad(int P) { return "thestring"; } -int bad = 10; // expected-note 6{{declared here}} +int bad = 10; // expected-note 5{{declared here}} tester glob1(make_name("glob1")); tester glob2(make_name("glob2")); constexpr tester cglob(make_name("cglob")); -tester paddedglob(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ +tester paddedglob(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::tester::tester' is not a constant expression}} \ // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} constexpr tester glob3 = { make_name("glob3") }; -constexpr tester glob4 = { make_name(pad(bad)) }; // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ - // expected-error {{constexpr variable 'glob4' must be initialized by a constant expression}} \ - // expected-note 2{{read of non-const variable 'bad' is not allowed in a constant expression}} +constexpr tester glob4 = { make_name(pad(bad)) }; // expected-error {{constexpr variable 'glob4' must be initialized by a constant expression}} \ + // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} auto V = make_name(pad(3)); auto V1 = make_name(pad(bad)); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ @@ -1094,12 +1092,12 @@ auto V1 = make_name(pad(bad)); // expected-error {{call to consteval function 'G void foo() { static tester loc1(make_name("loc1")); static constexpr tester loc2(make_name("loc2")); - static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::tester::tester' is not a constant expression}} \ // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} } void bar() { - static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::tester::tester' is not a constant expression}} \ // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} } } @@ -1133,7 +1131,7 @@ namespace GH65985 { int consteval operator""_foo(unsigned long long V) { return 0; } -int consteval operator""_bar(unsigned long long V); // expected-note 3{{here}} +int consteval operator""_bar(unsigned long long V); // expected-note 2{{here}} int consteval f() { return 0; @@ -1149,10 +1147,7 @@ struct C { // expected-note {{undefined function 'operator""_bar' cannot be used in a constant expression}} \ // expected-error {{in-class initializer for static data member is not a constant expression}} - // FIXME: remove duplicate diagnostics - static constexpr int d = 1_bar; // expected-error {{call to consteval function 'GH65985::operator""_bar' is not a constant expression}} \ - // expected-note {{undefined function 'operator""_bar' cannot be used in a constant expression}} \ - // expected-error {{constexpr variable 'd' must be initialized by a constant expression}} \ + static constexpr int d = 1_bar; // expected-error {{constexpr variable 'd' must be initialized by a constant expression}} \ // expected-note {{undefined function 'operator""_bar' cannot be used in a constant expression}} static const int e = f(); @@ -1167,12 +1162,12 @@ namespace GH66562 { namespace ns { - consteval int foo(int x) { return 1; } // expected-note {{declared here}} + consteval int foo(int x) { return 1; } } template struct T { - static constexpr auto xx = ns::foo(A{}); // expected-error {{cannot take address of consteval function 'foo' outside of an immediate invocation}} + static constexpr auto xx = ns::foo(A{}); }; } diff --git a/clang/test/SemaCXX/cxx2b-consteval-if.cpp b/clang/test/SemaCXX/cxx2b-consteval-if.cpp index 72ba58b676247..21413e58169b6 100644 --- a/clang/test/SemaCXX/cxx2b-consteval-if.cpp +++ b/clang/test/SemaCXX/cxx2b-consteval-if.cpp @@ -18,7 +18,7 @@ constexpr auto h() { constexpr auto i() { if consteval { - if consteval { // expected-warning {{consteval if is always true in an immediate context}} + if consteval { // expected-warning {{consteval if is always true in this context}} return 1; } return 2; diff --git a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp index 531a626228733..2daab2c75322c 100644 --- a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp +++ b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp @@ -209,7 +209,7 @@ struct SS { SS::SS(){} // expected-note {{in the default initializer of 'x'}} consteval int f2(int x) { - if (!__builtin_is_constant_evaluated()) side_effect(); + if (!__builtin_is_constant_evaluated()) side_effect(); // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to true}} return x; } struct S2 { @@ -332,16 +332,14 @@ S s(0); // expected-note {{in the default initializer of 'j'}} } namespace GH65985 { -consteval int invalid(); // expected-note 2{{declared here}} +consteval int invalid(); // expected-note {{declared here}} constexpr int escalating(auto) { return invalid(); - // expected-note@-1 {{'escalating' is an immediate function because its body contains a call to a consteval function 'invalid' and that call is not a constant expression}} - // expected-note@-2 2{{undefined function 'invalid' cannot be used in a constant expression}} + // expected-note@-1 {{undefined function 'invalid' cannot be used in a constant expression}} } struct S { - static constexpr int a = escalating(0); // expected-note 2{{in call to}} - // expected-error@-1 {{call to immediate function 'GH65985::escalating' is not a constant expression}} - // expected-error@-2 {{constexpr variable 'a' must be initialized by a constant expression}} + static constexpr int a = escalating(0); // expected-note {{in call to}} + // expected-error@-1 {{constexpr variable 'a' must be initialized by a constant expression}} }; } diff --git a/clang/test/SemaCXX/fixit-tautological-meta-constant.cpp b/clang/test/SemaCXX/fixit-tautological-meta-constant.cpp new file mode 100644 index 0000000000000..104dd2e75d25a --- /dev/null +++ b/clang/test/SemaCXX/fixit-tautological-meta-constant.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -std=c++2b -Wno-unused-value -fdiagnostics-parseable-fixits -fsyntax-only %s 2>&1 | FileCheck %s +namespace std { +constexpr inline bool + is_constant_evaluated() noexcept { + if consteval { return true; } else { return false; } + } +} // namespace std + +constexpr void cexpr() { + if constexpr (std::is_constant_evaluated()) {} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:6-[[@LINE-1]]:16}:"" + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:6-[[@LINE-2]]:16}:"" + constexpr int a = std::is_constant_evaluated(); + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:{{.*}}-[[@LINE-1]]:{{.*}}}:"" + + if constexpr (const int ce = __builtin_is_constant_evaluated()) {} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:6-[[@LINE-1]]:16}:"" + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-2]]:6-[[@LINE-2]]:16}:"" + constexpr int b = std::is_constant_evaluated(); + // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:{{.*}}-[[@LINE-1]]:{{.*}}}:"" +} diff --git a/clang/test/SemaCXX/vartemplate-lambda.cpp b/clang/test/SemaCXX/vartemplate-lambda.cpp index d2b53b53dcd49..ab187b2c2e7a2 100644 --- a/clang/test/SemaCXX/vartemplate-lambda.cpp +++ b/clang/test/SemaCXX/vartemplate-lambda.cpp @@ -12,7 +12,7 @@ template auto v1 = [](int a = T()) { return a; }(); struct S { template - static constexpr T t = [](int f = T(7)){return f;}(); // expected-error{{constexpr variable 't' must be initialized by a constant expression}} expected-note{{cannot be used in a constant expression}} + static constexpr T t = [](int f = T(7)){return f;}(); // expected-error{{a lambda expression may not appear inside of a constant expression}} }; template @@ -21,7 +21,7 @@ int foo2() { fn1(a); (void)v1; (void)v1; // expected-note{{in instantiation of variable template specialization 'v1' requested here}} - (void)S::t; // expected-note{{in instantiation of static data member 'S::t' requested here}} + (void)S::t; return 0; } diff --git a/clang/test/SemaCXX/warn-constant-evaluated-constexpr.cpp b/clang/test/SemaCXX/warn-constant-evaluated-constexpr.cpp index 35dc69cccb3a2..6ee5f86998eb6 100644 --- a/clang/test/SemaCXX/warn-constant-evaluated-constexpr.cpp +++ b/clang/test/SemaCXX/warn-constant-evaluated-constexpr.cpp @@ -7,35 +7,35 @@ constexpr bool is_constant_evaluated() noexcept { } // namespace std constexpr int fn1() { - if constexpr (std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + if constexpr (std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to true in this context}} return 0; else return 1; } constexpr int fn2() { - if constexpr (!std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + if constexpr (!std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to true in this context}} return 0; else return 1; } constexpr int fn3() { - if constexpr (std::is_constant_evaluated() == false) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + if constexpr (std::is_constant_evaluated() == false) // expected-warning {{'std::is_constant_evaluated' will always evaluate to true in this context}} return 0; else return 1; } constexpr int fn4() { - if constexpr (__builtin_is_constant_evaluated() == true) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + if constexpr (__builtin_is_constant_evaluated() == true) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to true in this context}} return 0; else return 1; } constexpr int fn5() { - if constexpr (__builtin_is_constant_evaluated()) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + if constexpr (__builtin_is_constant_evaluated()) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to true in this context}} return 0; else return 1; diff --git a/clang/test/SemaCXX/warn-tautological-meta-constant.cpp b/clang/test/SemaCXX/warn-tautological-meta-constant.cpp new file mode 100644 index 0000000000000..eef944effd8eb --- /dev/null +++ b/clang/test/SemaCXX/warn-tautological-meta-constant.cpp @@ -0,0 +1,262 @@ +// RUN: %clang_cc1 -std=c++2b -Wno-unused-value -fsyntax-only -verify %s + +namespace std { +constexpr inline bool + is_constant_evaluated() noexcept { + if consteval { return true; } else { return false; } + } +} // namespace std + +namespace P1938 { + constexpr int f1() { + if constexpr (!std::is_constant_evaluated() && sizeof(int) == 4) { // expected-warning {{always evaluate to true}} + return 0; + } + if (std::is_constant_evaluated()) { + return 42; + } else { + if constexpr (std::is_constant_evaluated()) { // expected-warning {{always evaluate to true}} + return 0; + } + } + return 7; +} + + +consteval int f2() { + if (std::is_constant_evaluated() && f1()) { // expected-warning {{always evaluate to true}} + return 42; + } + return 7; +} + + +int f3() { + if (std::is_constant_evaluated() && f1()) { // expected-warning {{always evaluate to false}} + return 42; + } + return 7; +} +} + +void non_qual() { + int ff = std::is_constant_evaluated(); // expected-warning {{always evaluate to false}} + const int aa = std::is_constant_evaluated(); + constexpr int tt = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + static int bb = std::is_constant_evaluated(); + constexpr int cc = [](){ + if consteval {return 8;} + }(); + auto lamda = []() { + if consteval {return 8;} + else {return 4;} + }; + constexpr auto cexpr_lambda = []() { + if consteval {} + return __builtin_is_constant_evaluated(); + }; + auto lamda_const = []() consteval { + if consteval {return 8;} // expected-warning {{always true}} + else {return 4;} + }; + if consteval { // expected-warning {{always false}} + int b = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + } +} + +constexpr void in_constexpr() { + int aa = std::is_constant_evaluated(); + constexpr int bb = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const int cc = std::is_constant_evaluated(); + if consteval { + int dd = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + constexpr int ee = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const int ff = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + } else { + int dd = std::is_constant_evaluated(); // expected-warning {{always evaluate to false}} + constexpr int ee = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const int ff = std::is_constant_evaluated(); + const int qq = std::is_constant_evaluated() ? dd : 3; + } + + if consteval { + if consteval {} // expected-warning {{always true}} + if !consteval {} // expected-warning {{always false}} + } else { + if consteval {} // expected-warning {{always false}} + if !consteval {} // expected-warning {{always true}} + } + if !consteval { + if consteval {} // expected-warning {{always false}} + if !consteval {} // expected-warning {{always true}} + } else { + if consteval {} // expected-warning {{always true}} + if !consteval {} // expected-warning {{always false}} + } +} + +consteval void in_consteval() { + int aa = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + constexpr int bb = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const int cc = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + auto lambda = []() { + int a(std::is_constant_evaluated()); // expected-warning {{always evaluate to true}} + constexpr int b = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const int c = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + }; + if !consteval {} // expected-warning {{always false}} +} + +static_assert(std::is_constant_evaluated()); // expected-warning {{always evaluate to true}} +static_assert(__builtin_is_constant_evaluated()); // expected-warning {{always evaluate to true}} + +template +void templ() { + if constexpr(std::is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + constexpr bool c = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + if consteval {} // expected-warning {{always false}} +} + +template <> void templ() { // expected-warning {{always evaluate to true}} + if constexpr(std::is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + constexpr bool c = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + if consteval {} // expected-warning {{always false}} + templ(); +} + +static_assert([] { + if consteval { + return 0; + } else { + return 1; + } + }() == 0); +constexpr bool b = __builtin_is_constant_evaluated(); // expected-warning {{always evaluate to true}} +constexpr bool c = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} +constinit bool d = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} +int p = __builtin_is_constant_evaluated(); +const int q = __builtin_is_constant_evaluated(); + +template // expected-warning {{always evaluate to true}} +void vvv() { + return; +} + +template<> void vvv() {} +template<> void vvv() {} + +template concept C = __builtin_is_constant_evaluated();// expected-warning {{always evaluate to true}} + +struct Foo { + static constexpr bool ce = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const static bool nonce = std::is_constant_evaluated(); + bool b = std::is_constant_evaluated(); + + Foo() { + if constexpr(std::is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + bool aa = std::is_constant_evaluated(); // expected-warning {{always evaluate to false}} + static bool bb = std::is_constant_evaluated(); + constexpr bool cc = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + if consteval {} // expected-warning {{always false}} + } + constexpr Foo(int) { + if constexpr(std::is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + bool aa = std::is_constant_evaluated(); + static bool bb = std::is_constant_evaluated(); + constexpr bool cc = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + } + consteval Foo(int *) { + if constexpr(std::is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + bool aa = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + static bool bb = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + constexpr bool cc = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + } +}; + +namespace condition { +void f() { + if constexpr (int a = __builtin_is_constant_evaluated(); // expected-warning {{always evaluate to false}} + true) {} + if constexpr (const int a = __builtin_is_constant_evaluated(); + true) {} + if constexpr (constexpr int a = __builtin_is_constant_evaluated(); // expected-warning {{always evaluate to true}} + true) {} + if constexpr (;const int b = __builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + if constexpr (;constexpr int b = __builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + + if (int a = __builtin_is_constant_evaluated(); // expected-warning {{always evaluate to false}} + true) {} + if (const int a = __builtin_is_constant_evaluated(); + true) {} + if (constexpr int a = __builtin_is_constant_evaluated(); // expected-warning {{always evaluate to true}} + true) {} + if (;int b = __builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to false}} + if (;const int b = __builtin_is_constant_evaluated()) {} + if (;constexpr int b = __builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + + if constexpr (__builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + if (__builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to false}} + + if constexpr (__builtin_is_constant_evaluated(); true) {} // expected-warning {{always evaluate to false}} + // False + if constexpr (({__builtin_is_constant_evaluated();2;3;}); true) {} + + if (__builtin_is_constant_evaluated(); true) {} // expected-warning {{always evaluate to false}} + if constexpr (;__builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + if (;__builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to false}} +} + +constexpr void g() { + if constexpr (int a = __builtin_is_constant_evaluated(); + true) {} + if constexpr (const int a = __builtin_is_constant_evaluated(); + true) {} + if constexpr (constexpr int a = __builtin_is_constant_evaluated(); // expected-warning {{always evaluate to true}} + true) {} + if constexpr (;const int b = __builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + if constexpr (;constexpr int b = __builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + + if (int a = __builtin_is_constant_evaluated(); + true) {} + if (const int a = __builtin_is_constant_evaluated(); + true) {} + if (constexpr int a = __builtin_is_constant_evaluated(); // expected-warning {{always evaluate to true}} + true) {} + if (;int b = __builtin_is_constant_evaluated()) {} + if (;const int b = __builtin_is_constant_evaluated()) {} + if (;constexpr int b = __builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + + if constexpr (__builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + if (__builtin_is_constant_evaluated()) {} + + if constexpr (__builtin_is_constant_evaluated(); true) {} + if constexpr (({__builtin_is_constant_evaluated();2;3;}); true) {} + + if (__builtin_is_constant_evaluated(); true) {} + if constexpr (;__builtin_is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + if (;__builtin_is_constant_evaluated()) {} +} +} + +namespace Arguments { + int nonc(int n) { return n;} + constexpr int cexpr(int n) { return n;} + consteval int ceval(int n) { return n; } + void f() { + // FIXME: These are tauologically-false; + int a1 = nonc(__builtin_is_constant_evaluated()); + const int b1 = nonc(__builtin_is_constant_evaluated()); + int a2 = cexpr(__builtin_is_constant_evaluated()); + + // ok + const int b2 = cexpr(__builtin_is_constant_evaluated()); + constexpr int c2 = cexpr(__builtin_is_constant_evaluated()); // expected-warning {{always evaluate to true}} + + // FIXME: These are tautologically-true; + int a3 = ceval(__builtin_is_constant_evaluated()); + const int b3 = ceval(__builtin_is_constant_evaluated()); + + // ok + constexpr int c3 = ceval(__builtin_is_constant_evaluated()); // expected-warning {{always evaluate to true}} + } +} diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index e98ebcc9203a4..c90339e778bc1 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -135,21 +135,21 @@ namespace PackInTypeConstraint { namespace BuiltinIsConstantEvaluated { // Check that we do all satisfaction and diagnostic checks in a constant context. - template concept C = __builtin_is_constant_evaluated(); // expected-warning {{always}} + template concept C = __builtin_is_constant_evaluated(); // expected-warning {{always evaluate to true}} static_assert(C); - template concept D = __builtin_is_constant_evaluated() == true; // expected-warning {{always}} + template concept D = __builtin_is_constant_evaluated() == true; // expected-warning {{always evaluate to true}} static_assert(D); - template concept E = __builtin_is_constant_evaluated() == true && // expected-warning {{always}} + template concept E = __builtin_is_constant_evaluated() == true && // expected-warning {{always evaluate to true}} false; // expected-note {{'false' evaluated to false}} static_assert(E); // expected-error {{failed}} expected-note {{because 'int' does not satisfy 'E'}} - template concept F = __builtin_is_constant_evaluated() == false; // expected-warning {{always}} + template concept F = __builtin_is_constant_evaluated() == false; // expected-warning {{always evaluate to true}} // expected-note@-1 {{'__builtin_is_constant_evaluated() == false' (1 == 0)}} static_assert(F); // expected-error {{failed}} expected-note {{because 'int' does not satisfy 'F'}} - template concept G = __builtin_is_constant_evaluated() && // expected-warning {{always}} + template concept G = __builtin_is_constant_evaluated() && // expected-warning {{always evaluate to true}} false; // expected-note {{'false' evaluated to false}} static_assert(G); // expected-error {{failed}} expected-note {{because 'int' does not satisfy 'G'}} } diff --git a/clang/unittests/Support/TimeProfilerTest.cpp b/clang/unittests/Support/TimeProfilerTest.cpp index fdfbbfe4e3a9d..85173b23fc649 100644 --- a/clang/unittests/Support/TimeProfilerTest.cpp +++ b/clang/unittests/Support/TimeProfilerTest.cpp @@ -188,7 +188,6 @@ Frontend | EvaluateAsBooleanCondition () | | EvaluateAsRValue () | EvaluateAsInitializer (slow_value) -| EvaluateAsConstantExpr () | EvaluateAsConstantExpr () | EvaluateAsRValue () | EvaluateAsInitializer (slow_init_list) diff --git a/libcxx/include/__type_traits/is_constant_evaluated.h b/libcxx/include/__type_traits/is_constant_evaluated.h index d7af462486e13..1649762ed6dc5 100644 --- a/libcxx/include/__type_traits/is_constant_evaluated.h +++ b/libcxx/include/__type_traits/is_constant_evaluated.h @@ -24,7 +24,14 @@ _LIBCPP_INLINE_VISIBILITY inline constexpr bool is_constant_evaluated() noexcept #endif _LIBCPP_HIDE_FROM_ABI inline _LIBCPP_CONSTEXPR bool __libcpp_is_constant_evaluated() _NOEXCEPT { +// __builtin_is_constant_evaluated() in this function always evaluates to false in pre-C++11 mode +// because this function is not constexpr-qualified. +// The following macro use clarifies this and avoids warnings from compilers. +#ifndef _LIBCPP_CXX03_LANG return __builtin_is_constant_evaluated(); +#else + return false; +#endif } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp index 9291c0aa1f434..abb9157df9abb 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp @@ -93,12 +93,10 @@ constexpr void test_join_view() { } int main(int, char**) { - if (!std::is_constant_evaluated()) { - test_containers, std::deque>(); - test_containers, std::vector>(); - test_containers, std::deque>(); - test_containers, std::vector>(); - } + test_containers, std::deque>(); + test_containers, std::vector>(); + test_containers, std::deque>(); + test_containers, std::vector>(); types::for_each(types::forward_iterator_list{}, [] { test_join_view(); diff --git a/libcxx/test/std/utilities/meta/meta.const.eval/is_constant_evaluated.verify.cpp b/libcxx/test/std/utilities/meta/meta.const.eval/is_constant_evaluated.verify.cpp index f4865ff368079..ef6e6f18ba6b5 100644 --- a/libcxx/test/std/utilities/meta/meta.const.eval/is_constant_evaluated.verify.cpp +++ b/libcxx/test/std/utilities/meta/meta.const.eval/is_constant_evaluated.verify.cpp @@ -24,7 +24,7 @@ int main(int, char**) #else // expected-error-re@+1 {{{{(static_assert|static assertion)}} failed}} static_assert(!std::is_constant_evaluated(), ""); - // expected-warning@-1 0-1 {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + // expected-warning-re@-1 0-1 {{'std::is_constant_evaluated' will always evaluate to {{('true' in a manifestly constant-evaluated expression|true in this context)}}}} #endif return 0; }