Revert "[clang][SemaCXX] Diagnose tautological uses of consteval if and is_constant_evaluated"

This reverts commit 491b2810fb7fe5f080fa9c4f5945ed0a6909dc92.

This change broke valid code and generated incorrect diagnostics, see
https://reviews.llvm.org/D155064
This commit is contained in:
Sam McCall 2023-09-27 18:41:11 +02:00
parent 6a34b12727
commit 880fa7faa9
40 changed files with 163 additions and 638 deletions

View File

@ -206,12 +206,6 @@ 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: <https://github.com/llvm/llvm-project/issues/54678>`_).
- Clang now diagnoses wider cases of tautological use of consteval if or
``std::is_constant_evaluated``. This also suppresses some false positives.
(`#43760: <https://github.com/llvm/llvm-project/issues/43760>`_)
(`#51567: <https://github.com/llvm/llvm-project/issues/51567>`_)
- Clang now diagnoses narrowing implicit conversions on variable initializers in immediate
function context and on constexpr variable template initializers.
- Clang now prints its 'note' diagnostic in cyan instead of black, to be more compatible
with terminals with dark background colors. This is also more consistent with GCC.

View File

@ -413,6 +413,10 @@ 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<DiagGroup<"constant-evaluated">>;
// inline asm related.
let CategoryName = "Inline Assembly Issue" in {
def err_asm_invalid_escape : Error<

View File

@ -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_tautological_consteval_if : Warning<
"consteval if is always %select{true|false}0 in this context">,
def warn_consteval_if_always_true : Warning<
"consteval if is always true in an %select{unevaluated|immediate}0 context">,
InGroup<DiagGroup<"redundant-consteval-if">>;
def ext_inline_variable : ExtWarn<
@ -8897,9 +8897,6 @@ 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<PotentiallyEvaluatedExpression>;
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<DiagGroup<"constant-evaluated">>;
def warn_unused_result : Warning<
"ignoring return value of function declared with %0 attribute">,
InGroup<UnusedResult>;

View File

@ -2003,10 +2003,12 @@ private:
//===--------------------------------------------------------------------===//
// 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, SourceLocation ConstexprLoc = {});
Sema::ConditionResult ParseCXXCondition(StmtResult *InitStmt,
SourceLocation Loc,
Sema::ConditionKind CK,
bool MissingOK,
ForRangeInfo *FRI = nullptr,
bool EnterForConditionScope = false);
DeclGroupPtrTy ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
ParsedAttributes &Attrs);
@ -2104,8 +2106,7 @@ private:
Sema::ConditionResult &CondResult,
SourceLocation Loc, Sema::ConditionKind CK,
SourceLocation &LParenLoc,
SourceLocation &RParenLoc,
SourceLocation ConstexprLoc = {});
SourceLocation &RParenLoc);
StmtResult ParseIfStatement(SourceLocation *TrailingElseLoc);
StmtResult ParseSwitchStatement(SourceLocation *TrailingElseLoc);
StmtResult ParseWhileStatement(SourceLocation *TrailingElseLoc);

View File

@ -1358,9 +1358,6 @@ public:
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;
@ -1390,8 +1387,7 @@ public:
NumCleanupObjects(NumCleanupObjects), NumTypos(0),
ManglingContextDecl(ManglingContextDecl), ExprContext(ExprContext),
InDiscardedStatement(false), InImmediateFunctionContext(false),
InImmediateEscalatingFunctionContext(false),
IsRuntimeEvaluated(false) {}
InImmediateEscalatingFunctionContext(false) {}
bool isUnevaluated() const {
return Context == ExpressionEvaluationContext::Unevaluated ||
@ -1430,10 +1426,6 @@ public:
/// A stack of expression evaluation contexts.
SmallVector<ExpressionEvaluationContextRecord, 8> 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<ConstantExpr *, 4> FailedImmediateInvocations;

View File

@ -12178,6 +12178,22 @@ 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);
}

View File

@ -2461,15 +2461,6 @@ 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<VarDecl>(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.
@ -2502,36 +2493,6 @@ 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;
@ -2631,7 +2592,6 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
<< getLangOpts().CPlusPlus20;
} else {
InitializerScopeRAII InitScope(*this, D, ThisDecl);
EnterInitializerExpressionEvaluationContext InitEC(Actions, D, ThisDecl);
if (Tok.is(tok::code_completion)) {
cutOffParsing();
@ -2679,7 +2639,6 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
ExprVector Exprs;
InitializerScopeRAII InitScope(*this, D, ThisDecl);
EnterInitializerExpressionEvaluationContext InitEC(Actions, D, ThisDecl);
auto ThisVarDecl = dyn_cast_or_null<VarDecl>(ThisDecl);
auto RunSignatureHelp = [&]() {
@ -2730,7 +2689,6 @@ 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());

View File

@ -3234,14 +3234,9 @@ ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction,
bool IsFieldInitialization = isa_and_present<FieldDecl>(D);
bool IsConstexpr = false;
if (const auto *VD = dyn_cast_if_present<VarDecl>(D))
IsConstexpr = VD->isConstexpr();
EnterExpressionEvaluationContext Context(
Actions,
IsConstexpr ? Sema::ExpressionEvaluationContext::ConstantEvaluated
: IsFieldInitialization
IsFieldInitialization
? Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed
: Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
D);

View File

@ -2093,13 +2093,6 @@ 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);
@ -2116,8 +2109,6 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
for (auto &E : ArgExprs)
Actions.CorrectDelayedTyposInExpr(E);
}
Actions.ExprEvalContexts.back().IsRuntimeEvaluated =
IsRuntimeEvaluated;
}
}

View File

@ -2041,8 +2041,7 @@ Parser::ParseAliasDeclarationInInitStatement(DeclaratorContext Context,
Sema::ConditionResult
Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
Sema::ConditionKind CK, bool MissingOK,
ForRangeInfo *FRI, bool EnterForConditionScope,
SourceLocation ConstexprLoc) {
ForRangeInfo *FRI, bool EnterForConditionScope) {
// Helper to ensure we always enter a continue/break scope if requested.
struct ForConditionScopeRAII {
Scope *S;
@ -2099,28 +2098,9 @@ 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);
}
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;
}
// Parse the expression.
ExprResult Expr = ParseExpression(); // expression
if (Expr.isInvalid())
return Sema::ConditionError();
@ -2208,21 +2188,6 @@ 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(),
@ -2249,7 +2214,6 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
Actions.ActOnInitializerError(DeclOut);
Actions.FinalizeDeclaration(DeclOut);
Actions.ConstexprIfLoc = OuterConstexprIfLoc;
return Actions.ActOnConditionVariable(DeclOut, Loc, CK);
}

View File

@ -1293,17 +1293,18 @@ 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, SourceLocation ConstexprLoc) {
bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt,
Sema::ConditionResult &Cond,
SourceLocation Loc,
Sema::ConditionKind CK,
SourceLocation &LParenLoc,
SourceLocation &RParenLoc) {
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
SourceLocation Start = Tok.getLocation();
if (getLangOpts().CPlusPlus) {
Cond = ParseCXXCondition(InitStmt, Loc, CK, false, nullptr, false,
ConstexprLoc);
Cond = ParseCXXCondition(InitStmt, Loc, CK, false);
} else {
ExprResult CondExpr = ParseExpression();
@ -1463,13 +1464,12 @@ 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;
ConstexprLoc = ConsumeToken();
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, ConstexprLoc))
LParen, RParen))
return StmtError();
if (IsConstexpr)
@ -1558,16 +1558,11 @@ 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);
}
@ -1608,16 +1603,11 @@ 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())

View File

@ -14836,8 +14836,7 @@ 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 IsConstexprInit = false) {
bool IsListInit = false) {
if (E->isTypeDependent() || E->isValueDependent()) return;
const Type *Source = S.Context.getCanonicalType(E->getType()).getTypePtr();
@ -15146,14 +15145,11 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
SmallString<32> PrettyTargetValue;
TargetFloatValue.toString(PrettyTargetValue, TargetPrecision);
PartialDiagnostic PD =
S.DiagRuntimeBehavior(
E->getExprLoc(), E,
S.PDiag(diag::warn_impcast_integer_float_precision_constant)
<< PrettySourceValue << PrettyTargetValue << E->getType() << T
<< E->getSourceRange() << clang::SourceRange(CC);
if (IsConstexprInit)
S.Diag(E->getExprLoc(), PD);
else
S.DiagRuntimeBehavior(E->getExprLoc(), E, PD);
<< PrettySourceValue << PrettyTargetValue << E->getType() << T
<< E->getSourceRange() << clang::SourceRange(CC));
}
} else {
// Otherwise, the implicit conversion may lose precision.
@ -15207,14 +15203,11 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
std::string PrettySourceValue = toString(Value, 10);
std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange);
PartialDiagnostic PD =
S.DiagRuntimeBehavior(
E->getExprLoc(), E,
S.PDiag(diag::warn_impcast_integer_precision_constant)
<< PrettySourceValue << PrettyTargetValue << E->getType() << T
<< E->getSourceRange() << SourceRange(CC);
if (IsConstexprInit)
S.Diag(E->getExprLoc(), PD);
else
S.DiagRuntimeBehavior(E->getExprLoc(), E, PD);
<< PrettySourceValue << PrettyTargetValue << E->getType() << T
<< E->getSourceRange() << SourceRange(CC));
return;
}
@ -15256,14 +15249,11 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
std::string PrettySourceValue = toString(Value, 10);
std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange);
PartialDiagnostic PD =
S.DiagRuntimeBehavior(
E->getExprLoc(), E,
S.PDiag(diag::warn_impcast_integer_precision_constant)
<< PrettySourceValue << PrettyTargetValue << E->getType() << T
<< E->getSourceRange() << SourceRange(CC);
if (IsConstexprInit)
S.Diag(E->getExprLoc(), PD);
else
S.DiagRuntimeBehavior(E->getExprLoc(), E, PD);
<< PrettySourceValue << PrettyTargetValue << E->getType() << T
<< E->getSourceRange() << SourceRange(CC));
return;
}
}
@ -15425,17 +15415,6 @@ static void AnalyzeImplicitConversions(
if (auto *Src = OVE->getSourceExpr())
SourceExpr = Src;
bool IsConstexprInit =
S.isConstantEvaluated() &&
isa_and_present<VarDecl>(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<UnaryOperator>(SourceExpr))
if (UO->getOpcode() == UO_Not &&
UO->getSubExpr()->isKnownToHaveBooleanValue())
@ -15471,7 +15450,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 (!IsConstexprInit && SourceExpr->getType() != T)
if (SourceExpr->getType() != T)
CheckImplicitConversion(S, SourceExpr, T, CC, nullptr, IsListInit);
// Now continue drilling into this expression.

View File

@ -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,9 +15451,6 @@ 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

View File

@ -18241,6 +18241,15 @@ 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<VarDecl>(D))
return Var->hasGlobalStorage();
return false;
}
/// Invoked when we are about to parse an initializer for the declaration
/// 'Dcl'.
///
@ -18263,6 +18272,9 @@ 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.
@ -18271,6 +18283,9 @@ void Sema::ActOnCXXExitDeclInitializer(Scope *S, Decl *D) {
if (!D || D->isInvalidDecl())
return;
if (isNonlocalVariable(D))
PopExpressionEvaluationContext();
if (S && D->isOutOfLine())
ExitDeclaratorContext(S);
}

View File

@ -7091,32 +7091,6 @@ 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) {
@ -7141,10 +7115,8 @@ 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<CallExpr>(Call.get())) {
if (const auto *CE = dyn_cast<CallExpr>(Call.get()))
DiagnosedUnqualifiedCallsToStdFunctions(*this, CE);
DiagnoseTautologicalCallToIsConstantEvaluated(*this, CE);
}
}
return Call;
}
@ -18636,8 +18608,6 @@ void Sema::PopExpressionEvaluationContext() {
} else
llvm_unreachable("Couldn't infer lambda error message.");
if (auto *VD = dyn_cast_if_present<VarDecl>(Rec.ManglingContextDecl))
VD->setInvalidDecl();
for (const auto *L : Rec.Lambdas)
Diag(L->getBeginLoc(), D);
}

View File

@ -1444,13 +1444,12 @@ 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() || InImmediateFunctionContext
LSI->CallOperator->isConsteval()
? ExpressionEvaluationContext::ImmediateFunctionContext
: ExpressionEvaluationContext::PotentiallyEvaluated);
ExprEvalContexts.back().InImmediateFunctionContext =
LSI->CallOperator->isConsteval() || InImmediateFunctionContext;
LSI->CallOperator->isConsteval();
ExprEvalContexts.back().InImmediateEscalatingFunctionContext =
getLangOpts().CPlusPlus20 && LSI->CallOperator->isImmediateEscalating();
}

View File

@ -933,14 +933,16 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc,
}
if (ConstevalOrNegatedConsteval) {
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);
bool Immediate = ExprEvalContexts.back().Context ==
ExpressionEvaluationContext::ImmediateFunctionContext;
if (CurContext->isFunctionOrMethod()) {
const auto *FD =
dyn_cast<FunctionDecl>(Decl::castFromDeclContext(CurContext));
if (FD && FD->isImmediateFunction())
Immediate = true;
}
if (isUnevaluatedContext() || Immediate)
Diags.Report(IfLoc, diag::warn_consteval_if_always_true) << Immediate;
}
return BuildIfStmt(IfLoc, StatementKind, LParenLoc, InitStmt, Cond, RParenLoc,

View File

@ -5378,11 +5378,8 @@ void Sema::InstantiateVariableInitializer(
Var->setImplicitlyInline();
if (OldVar->getInit()) {
Sema::ExpressionEvaluationContext InitEvalContext =
Var->isConstexpr()
? Sema::ExpressionEvaluationContext::ConstantEvaluated
: Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
EnterExpressionEvaluationContext Evaluated(*this, InitEvalContext, Var);
EnterExpressionEvaluationContext Evaluated(
*this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var);
// Instantiate the initializer.
ExprResult Init;

View File

@ -1,5 +1,5 @@
// 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 -fexperimental-new-constant-interpreter %s -verify
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -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

View File

@ -1,5 +1,5 @@
// 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
// 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
// expected-no-diagnostics
// ref-no-diagnostics

View File

@ -225,11 +225,15 @@ 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 2 {{not valid in a constant expression}}
return sizeof(int[n]); // ref-note 3{{not valid in a constant expression}} \
// expected-note {{not valid in a constant expression}}
}
constinit int var = foo(5); // ref-note {{in call to}} \
constinit int var = foo(5); // ref-error {{not a constant expression}} \
// ref-note 2{{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}} \

View File

@ -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 from 200 to -56}}
constexpr signed char c2 = '\x64' * '\2'; // also ok expected-warning {{changes value from 200 to -56}}
constexpr signed char c1 = 100 * 2; // ok expected-warning{{changes value}}
constexpr signed char c2 = '\x64' * '\2'; // also ok expected-warning{{changes value}}
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

View File

@ -43,11 +43,12 @@ 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 {{declared here}}
consteval int f() { return 42; } // expected-note 2 {{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 {{constexpr variable 'e' must be initialized by a constant expression}} \
expected-note {{pointer to a consteval declaration is not a constant expression}}
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}}
static_assert(r == 42);
} // namespace P1073R3

View File

@ -16,5 +16,4 @@ 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

View File

@ -38,8 +38,7 @@ 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-warning {{consteval if is always false}}
// expected-note 2{{annotating the 'if consteval' statement here}}
}
@ -50,8 +49,7 @@ void test_consteval() {
}
void test_consteval_jumps() {
if consteval { // expected-warning {{consteval if is always false}} \
// expected-note 4{{jump enters controlled statement of consteval if}}
if consteval { // 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:;
@ -67,16 +65,14 @@ void test_consteval_jumps() {
void test_consteval_switch() {
int x = 42;
switch (x) {
if consteval { // expected-warning {{consteval if is always false}} \
// expected-note 2{{jump enters controlled statement of consteval if}}
if consteval { // 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-warning {{consteval if is always false}} \
// expected-note 2{{jump enters controlled statement of consteval if}}
if consteval { // 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}}
@ -103,32 +99,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 this context}}
if consteval {} // expected-warning {{consteval if is always true in this context}}
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}}
}
}
constexpr void warn_in_consteval2() {
if consteval {
if consteval {} // expected-warning {{consteval if is always true in this context}}
if consteval {} // expected-warning {{consteval if is always true in an immediate context}}
}
}
auto y = []() consteval {
if consteval { // expected-warning {{consteval if is always true in this context}}
if consteval {} // expected-warning {{consteval if is always true in this context}}
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}}
}
};
namespace test_transform {
int f(auto n) {
if consteval { // expected-warning {{consteval if is always false}}
if consteval {
n.foo; //expected-error {{no member named}}
}
else {
}
if !consteval { // expected-warning {{consteval if is always true}}
if !consteval {
n.foo; //expected-error {{no member named}}
}
else {

View File

@ -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 from 'const int' to 'const float' changes value from 2147483632 to 2147483648}}
//expected-warning@+2{{implicit conversion}}
#endif
CONST float fbig = too_big; // inexact
#if !defined(CPP)

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify -triple x86_64-apple-darwin %s
// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-apple-darwin %s
// This file tests -Wconstant-conversion, a subcategory of -Wconversion
// which is on by default.
@ -31,59 +31,3 @@ 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 N>
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 <int a, int b>
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}}
}
}

View File

@ -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}}
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 m = m; // expected-error {{constant expression}} expected-note {{read of object outside its lifetime}}
}

View File

@ -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 {{declared here}}
consteval int f() const { return 12; } // expected-note 2{{declared here}}
};
constexpr test a;
@ -726,7 +726,8 @@ constexpr int s = a.operator[](1);
constexpr int t = a[1];
constexpr int u = a.operator->()->b;
constexpr int v = a->b;
constexpr int w = (a.*&test::f)();
// 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 x = a.f();
// Show that we reject when not in an immediate context.
@ -1072,17 +1073,18 @@ 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 5{{declared here}}
int bad = 10; // expected-note 6{{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::tester::tester' is not a constant expression}} \
tester paddedglob(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' 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 {{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}}
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}}
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}} \
@ -1092,12 +1094,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::tester::tester' is not a constant expression}} \
static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' 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::tester::tester' is not a constant expression}} \
static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \
// expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}}
}
}
@ -1131,7 +1133,7 @@ namespace GH65985 {
int consteval operator""_foo(unsigned long long V) {
return 0;
}
int consteval operator""_bar(unsigned long long V); // expected-note 2{{here}}
int consteval operator""_bar(unsigned long long V); // expected-note 3{{here}}
int consteval f() {
return 0;
@ -1147,7 +1149,10 @@ 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}}
static constexpr int d = 1_bar; // expected-error {{constexpr variable 'd' must be initialized by 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}} \
// expected-note {{undefined function 'operator""_bar' cannot be used in a constant expression}}
static const int e = f();
@ -1162,12 +1167,12 @@ namespace GH66562 {
namespace ns
{
consteval int foo(int x) { return 1; }
consteval int foo(int x) { return 1; } // expected-note {{declared here}}
}
template <class A>
struct T {
static constexpr auto xx = ns::foo(A{});
static constexpr auto xx = ns::foo(A{}); // expected-error {{cannot take address of consteval function 'foo' outside of an immediate invocation}}
};
}

View File

@ -18,7 +18,7 @@ constexpr auto h() {
constexpr auto i() {
if consteval {
if consteval { // expected-warning {{consteval if is always true in this context}}
if consteval { // expected-warning {{consteval if is always true in an immediate context}}
return 1;
}
return 2;

View File

@ -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(); // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to true}}
if (!__builtin_is_constant_evaluated()) side_effect();
return x;
}
struct S2 {
@ -332,14 +332,16 @@ S s(0); // expected-note {{in the default initializer of 'j'}}
}
namespace GH65985 {
consteval int invalid(); // expected-note {{declared here}}
consteval int invalid(); // expected-note 2{{declared here}}
constexpr int escalating(auto) {
return invalid();
// expected-note@-1 {{undefined function 'invalid' cannot be used in a constant expression}}
// expected-note@-1 {{'escalating<int>' 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}}
}
struct S {
static constexpr int a = escalating(0); // expected-note {{in call to}}
// expected-error@-1 {{constexpr variable 'a' must be initialized by a constant expression}}
static constexpr int a = escalating(0); // expected-note 2{{in call to}}
// expected-error@-1 {{call to immediate function 'GH65985::escalating<int>' is not a constant expression}}
// expected-error@-2 {{constexpr variable 'a' must be initialized by a constant expression}}
};
}

View File

@ -1,21 +0,0 @@
// 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]]:{{.*}}}:""
}

View File

@ -12,7 +12,7 @@ template<typename T> auto v1 = [](int a = T()) { return a; }();
struct S {
template<class T>
static constexpr T t = [](int f = T(7)){return f;}(); // expected-error{{a lambda expression may not appear inside of a constant expression}}
static constexpr T t = [](int f = T(7)){return f;}(); // expected-error{{constexpr variable 't<int>' must be initialized by a constant expression}} expected-note{{cannot be used in a constant expression}}
};
template <typename X>
@ -21,7 +21,7 @@ int foo2() {
fn1<char>(a);
(void)v1<int>;
(void)v1<int *>; // expected-note{{in instantiation of variable template specialization 'v1' requested here}}
(void)S::t<int>;
(void)S::t<int>; // expected-note{{in instantiation of static data member 'S::t<int>' requested here}}
return 0;
}

View File

@ -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 this context}}
if constexpr (std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}}
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 this context}}
if constexpr (!std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}}
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 this context}}
if constexpr (std::is_constant_evaluated() == false) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}}
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 this context}}
if constexpr (__builtin_is_constant_evaluated() == true) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}}
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 this context}}
if constexpr (__builtin_is_constant_evaluated()) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}}
return 0;
else
return 1;

View File

@ -1,262 +0,0 @@
// 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 <bool b>
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<std::is_constant_evaluated()>() { // 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<false>();
}
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 <bool c = std::is_constant_evaluated()> // expected-warning {{always evaluate to true}}
void vvv() {
return;
}
template<> void vvv<true>() {}
template<> void vvv<false>() {}
template<typename T> 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}}
}
}

View File

@ -135,21 +135,21 @@ namespace PackInTypeConstraint {
namespace BuiltinIsConstantEvaluated {
// Check that we do all satisfaction and diagnostic checks in a constant context.
template<typename T> concept C = __builtin_is_constant_evaluated(); // expected-warning {{always evaluate to true}}
template<typename T> concept C = __builtin_is_constant_evaluated(); // expected-warning {{always}}
static_assert(C<int>);
template<typename T> concept D = __builtin_is_constant_evaluated() == true; // expected-warning {{always evaluate to true}}
template<typename T> concept D = __builtin_is_constant_evaluated() == true; // expected-warning {{always}}
static_assert(D<int>);
template<typename T> concept E = __builtin_is_constant_evaluated() == true && // expected-warning {{always evaluate to true}}
template<typename T> concept E = __builtin_is_constant_evaluated() == true && // expected-warning {{always}}
false; // expected-note {{'false' evaluated to false}}
static_assert(E<int>); // expected-error {{failed}} expected-note {{because 'int' does not satisfy 'E'}}
template<typename T> concept F = __builtin_is_constant_evaluated() == false; // expected-warning {{always evaluate to true}}
template<typename T> concept F = __builtin_is_constant_evaluated() == false; // expected-warning {{always}}
// expected-note@-1 {{'__builtin_is_constant_evaluated() == false' (1 == 0)}}
static_assert(F<int>); // expected-error {{failed}} expected-note {{because 'int' does not satisfy 'F'}}
template<typename T> concept G = __builtin_is_constant_evaluated() && // expected-warning {{always evaluate to true}}
template<typename T> concept G = __builtin_is_constant_evaluated() && // expected-warning {{always}}
false; // expected-note {{'false' evaluated to false}}
static_assert(G<int>); // expected-error {{failed}} expected-note {{because 'int' does not satisfy 'G'}}
}

View File

@ -188,6 +188,7 @@ Frontend
| EvaluateAsBooleanCondition (<test.cc:8:21, col:25>)
| | EvaluateAsRValue (<test.cc:8:21, col:25>)
| EvaluateAsInitializer (slow_value)
| EvaluateAsConstantExpr (<test.cc:17:33, col:59>)
| EvaluateAsConstantExpr (<test.cc:18:11, col:37>)
| EvaluateAsRValue (<test.cc:22:14, line:23:58>)
| EvaluateAsInitializer (slow_init_list)

View File

@ -24,14 +24,7 @@ _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

View File

@ -93,10 +93,12 @@ constexpr void test_join_view() {
}
int main(int, char**) {
test_containers<std::deque<int>, std::deque<int>>();
test_containers<std::deque<int>, std::vector<int>>();
test_containers<std::vector<int>, std::deque<int>>();
test_containers<std::vector<int>, std::vector<int>>();
if (!std::is_constant_evaluated()) {
test_containers<std::deque<int>, std::deque<int>>();
test_containers<std::deque<int>, std::vector<int>>();
test_containers<std::vector<int>, std::deque<int>>();
test_containers<std::vector<int>, std::vector<int>>();
}
types::for_each(types::forward_iterator_list<int*>{}, []<class Iter> {
test_join_view<Iter, Iter>();

View File

@ -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-re@-1 0-1 {{'std::is_constant_evaluated' will always evaluate to {{('true' in a manifestly constant-evaluated expression|true in this context)}}}}
// expected-warning@-1 0-1 {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}}
#endif
return 0;
}