Implement p0292r2 (constexpr if), a likely C++1z feature.
llvm-svn: 273602
diff --git a/clang/lib/Sema/JumpDiagnostics.cpp b/clang/lib/Sema/JumpDiagnostics.cpp
index 500e761..bd54139 100644
--- a/clang/lib/Sema/JumpDiagnostics.cpp
+++ b/clang/lib/Sema/JumpDiagnostics.cpp
@@ -319,6 +319,37 @@
Jumps.push_back(S);
break;
+ case Stmt::IfStmtClass: {
+ IfStmt *IS = cast<IfStmt>(S);
+ if (!IS->isConstexpr())
+ break;
+
+ if (VarDecl *Var = IS->getConditionVariable())
+ BuildScopeInformation(Var, ParentScope);
+
+ // Cannot jump into the middle of the condition.
+ unsigned NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_constexpr_if, 0,
+ IS->getLocStart()));
+ BuildScopeInformation(IS->getCond(), NewParentScope);
+
+ // Jumps into either arm of an 'if constexpr' are not allowed.
+ NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_constexpr_if, 0,
+ IS->getLocStart()));
+ BuildScopeInformation(IS->getThen(), NewParentScope);
+ if (Stmt *Else = IS->getElse()) {
+ NewParentScope = Scopes.size();
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_constexpr_if, 0,
+ IS->getLocStart()));
+ BuildScopeInformation(Else, NewParentScope);
+ }
+ return;
+ }
+
case Stmt::CXXTryStmtClass: {
CXXTryStmt *TS = cast<CXXTryStmt>(S);
{
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index f975b81..ab1180e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -12903,6 +12903,11 @@
// definition of a null pointer constant is completely crazy.)
return false;
+ case Sema::DiscardedStatement:
+ // These are technically a potentially evaluated but they have the effect
+ // of suppressing use marking.
+ return false;
+
case Sema::ConstantEvaluated:
case Sema::PotentiallyEvaluated:
// We are in a potentially evaluated expression (or a constant-expression
@@ -14192,6 +14197,7 @@
switch (ExprEvalContexts.back().Context) {
case Unevaluated:
case UnevaluatedAbstract:
+ case DiscardedStatement:
// The argument will never be evaluated, so don't complain.
break;
@@ -14341,7 +14347,8 @@
}
}
-ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E) {
+ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E,
+ bool IsConstexpr) {
DiagnoseAssignmentAsCondition(E);
if (ParenExpr *parenE = dyn_cast<ParenExpr>(E))
DiagnoseEqualityWithExtraParens(parenE);
@@ -14352,7 +14359,7 @@
if (!E->isTypeDependent()) {
if (getLangOpts().CPlusPlus)
- return CheckCXXBooleanCondition(E); // C++ 6.4p4
+ return CheckCXXBooleanCondition(E, IsConstexpr); // C++ 6.4p4
ExprResult ERes = DefaultFunctionArrayLvalueConversion(E);
if (ERes.isInvalid())
@@ -14383,6 +14390,10 @@
Cond = CheckBooleanCondition(Loc, SubExpr);
break;
+ case ConditionKind::ConstexprIf:
+ Cond = CheckBooleanCondition(Loc, SubExpr, true);
+ break;
+
case ConditionKind::Switch:
Cond = CheckSwitchCondition(Loc, SubExpr);
break;
@@ -14390,7 +14401,8 @@
if (Cond.isInvalid())
return ConditionError();
- return ConditionResult(nullptr, MakeFullExpr(Cond.get(), Loc));
+ return ConditionResult(*this, nullptr, MakeFullExpr(Cond.get(), Loc),
+ CK == ConditionKind::ConstexprIf);
}
namespace {
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f97b9c9..ac87ae5 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -3061,7 +3061,8 @@
CheckConditionVariable(cast<VarDecl>(ConditionVar), StmtLoc, CK);
if (E.isInvalid())
return ConditionError();
- return ConditionResult(ConditionVar, MakeFullExpr(E.get(), StmtLoc));
+ return ConditionResult(*this, ConditionVar, MakeFullExpr(E.get(), StmtLoc),
+ CK == ConditionKind::ConstexprIf);
}
/// \brief Check the use of the given variable as a C++ condition in an if,
@@ -3096,6 +3097,9 @@
case ConditionKind::Boolean:
return CheckBooleanCondition(StmtLoc, Condition.get());
+ case ConditionKind::ConstexprIf:
+ return CheckBooleanCondition(StmtLoc, Condition.get(), true);
+
case ConditionKind::Switch:
return CheckSwitchCondition(StmtLoc, Condition.get());
}
@@ -3104,7 +3108,7 @@
}
/// CheckCXXBooleanCondition - Returns true if a conversion to bool is invalid.
-ExprResult Sema::CheckCXXBooleanCondition(Expr *CondExpr) {
+ExprResult Sema::CheckCXXBooleanCondition(Expr *CondExpr, bool IsConstexpr) {
// C++ 6.4p4:
// The value of a condition that is an initialized declaration in a statement
// other than a switch statement is the value of the declared variable
@@ -3113,7 +3117,12 @@
// The value of a condition that is an expression is the value of the
// expression, implicitly converted to bool.
//
- return PerformContextuallyConvertToBool(CondExpr);
+ // FIXME: Return this value to the caller so they don't need to recompute it.
+ llvm::APSInt Value(/*BitWidth*/1);
+ return (IsConstexpr && !CondExpr->isValueDependent())
+ ? CheckConvertedConstantExpression(CondExpr, Context.BoolTy, Value,
+ CCEK_ConstexprIf)
+ : PerformContextuallyConvertToBool(CondExpr);
}
/// Helper function to determine whether this is the (deprecated) C++
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index c9231ed..28362188 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -142,6 +142,7 @@
AbstractInstanceResult = IMA_Abstract;
break;
+ case Sema::DiscardedStatement:
case Sema::ConstantEvaluated:
case Sema::PotentiallyEvaluated:
case Sema::PotentiallyEvaluatedIfUsed:
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index ae768b6..1f5f63a 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1635,6 +1635,7 @@
ExprEvalContexts.back().Lambdas.push_back(Lambda);
break;
+ case DiscardedStatement:
case PotentiallyEvaluated:
case PotentiallyEvaluatedIfUsed:
break;
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 8d5bec8..5432a70 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -504,31 +504,43 @@
}
StmtResult
-Sema::ActOnIfStmt(SourceLocation IfLoc, ConditionResult Cond,
+Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, ConditionResult Cond,
Stmt *thenStmt, SourceLocation ElseLoc,
Stmt *elseStmt) {
- auto CondVal = Cond.get();
- if (Cond.isInvalid()) {
- CondVal.first = nullptr;
- CondVal.second = new (Context)
- OpaqueValueExpr(SourceLocation(), Context.BoolTy, VK_RValue);
- }
+ if (Cond.isInvalid())
+ Cond = ConditionResult(
+ *this, nullptr,
+ MakeFullExpr(new (Context) OpaqueValueExpr(SourceLocation(),
+ Context.BoolTy, VK_RValue),
+ IfLoc),
+ false);
+ Expr *CondExpr = Cond.get().second;
if (!Diags.isIgnored(diag::warn_comma_operator,
- CondVal.second->getExprLoc()))
- CommaVisitor(*this).Visit(CondVal.second);
+ CondExpr->getExprLoc()))
+ CommaVisitor(*this).Visit(CondExpr);
+
+ if (!elseStmt)
+ DiagnoseEmptyStmtBody(CondExpr->getLocEnd(), thenStmt,
+ diag::warn_empty_if_body);
+
+ return BuildIfStmt(IfLoc, IsConstexpr, Cond, thenStmt, ElseLoc, elseStmt);
+}
+
+StmtResult Sema::BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
+ ConditionResult Cond, Stmt *thenStmt,
+ SourceLocation ElseLoc, Stmt *elseStmt) {
+ if (Cond.isInvalid())
+ return StmtError();
+
+ if (IsConstexpr)
+ getCurFunction()->setHasBranchProtectedScope();
DiagnoseUnusedExprResult(thenStmt);
-
- if (!elseStmt) {
- DiagnoseEmptyStmtBody(CondVal.second->getLocEnd(), thenStmt,
- diag::warn_empty_if_body);
- }
-
DiagnoseUnusedExprResult(elseStmt);
- return new (Context) IfStmt(Context, IfLoc, CondVal.first, CondVal.second,
- thenStmt, ElseLoc, elseStmt);
+ return new (Context) IfStmt(Context, IfLoc, IsConstexpr, Cond.get().first,
+ Cond.get().second, thenStmt, ElseLoc, elseStmt);
}
namespace {
@@ -2836,8 +2848,21 @@
CapturingScopeInfo *CurCap = cast<CapturingScopeInfo>(getCurFunction());
QualType FnRetType = CurCap->ReturnType;
LambdaScopeInfo *CurLambda = dyn_cast<LambdaScopeInfo>(CurCap);
+ bool HasDeducedReturnType =
+ CurLambda && hasDeducedReturnType(CurLambda->CallOperator);
- if (CurLambda && hasDeducedReturnType(CurLambda->CallOperator)) {
+ if (ExprEvalContexts.back().Context == DiscardedStatement &&
+ (HasDeducedReturnType || CurCap->HasImplicitReturnType)) {
+ if (RetValExp) {
+ ExprResult ER = ActOnFinishFullExpr(RetValExp, ReturnLoc);
+ if (ER.isInvalid())
+ return StmtError();
+ RetValExp = ER.get();
+ }
+ return new (Context) ReturnStmt(ReturnLoc, RetValExp, nullptr);
+ }
+
+ if (HasDeducedReturnType) {
// In C++1y, the return type may involve 'auto'.
// FIXME: Blocks might have a return type of 'auto' explicitly specified.
FunctionDecl *FD = CurLambda->CallOperator;
@@ -3118,9 +3143,8 @@
Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
Scope *CurScope) {
StmtResult R = BuildReturnStmt(ReturnLoc, RetValExp);
- if (R.isInvalid()) {
+ if (R.isInvalid() || ExprEvalContexts.back().Context == DiscardedStatement)
return R;
- }
if (VarDecl *VD =
const_cast<VarDecl*>(cast<ReturnStmt>(R.get())->getNRVOCandidate())) {
@@ -3169,6 +3193,19 @@
} else // If we don't have a function/method context, bail.
return StmtError();
+ // C++1z: discarded return statements are not considered when deducing a
+ // return type.
+ if (ExprEvalContexts.back().Context == DiscardedStatement &&
+ FnRetType->getContainedAutoType()) {
+ if (RetValExp) {
+ ExprResult ER = ActOnFinishFullExpr(RetValExp, ReturnLoc);
+ if (ER.isInvalid())
+ return StmtError();
+ RetValExp = ER.get();
+ }
+ return new (Context) ReturnStmt(ReturnLoc, RetValExp, nullptr);
+ }
+
// FIXME: Add a flag to the ScopeInfo to indicate whether we're performing
// deduction.
if (getLangOpts().CPlusPlus14) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 5814197..a7b994d 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -1174,9 +1174,10 @@
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
- StmtResult RebuildIfStmt(SourceLocation IfLoc, Sema::ConditionResult Cond,
- Stmt *Then, SourceLocation ElseLoc, Stmt *Else) {
- return getSema().ActOnIfStmt(IfLoc, Cond, Then, ElseLoc, Else);
+ StmtResult RebuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
+ Sema::ConditionResult Cond, Stmt *Then,
+ SourceLocation ElseLoc, Stmt *Else) {
+ return getSema().ActOnIfStmt(IfLoc, IsConstexpr, Cond, Then, ElseLoc, Else);
}
/// \brief Start building a new switch statement.
@@ -6228,19 +6229,33 @@
// Transform the condition
Sema::ConditionResult Cond = getDerived().TransformCondition(
S->getIfLoc(), S->getConditionVariable(), S->getCond(),
- Sema::ConditionKind::Boolean);
+ S->isConstexpr() ? Sema::ConditionKind::ConstexprIf
+ : Sema::ConditionKind::Boolean);
if (Cond.isInvalid())
return StmtError();
+ // If this is a constexpr if, determine which arm we should instantiate.
+ llvm::Optional<bool> ConstexprConditionValue;
+ if (S->isConstexpr())
+ ConstexprConditionValue = Cond.getKnownValue();
+
// Transform the "then" branch.
- StmtResult Then = getDerived().TransformStmt(S->getThen());
- if (Then.isInvalid())
- return StmtError();
+ StmtResult Then;
+ if (!ConstexprConditionValue || *ConstexprConditionValue) {
+ Then = getDerived().TransformStmt(S->getThen());
+ if (Then.isInvalid())
+ return StmtError();
+ } else {
+ Then = new (getSema().Context) NullStmt(S->getThen()->getLocStart());
+ }
// Transform the "else" branch.
- StmtResult Else = getDerived().TransformStmt(S->getElse());
- if (Else.isInvalid())
- return StmtError();
+ StmtResult Else;
+ if (!ConstexprConditionValue || !*ConstexprConditionValue) {
+ Else = getDerived().TransformStmt(S->getElse());
+ if (Else.isInvalid())
+ return StmtError();
+ }
if (!getDerived().AlwaysRebuild() &&
Cond.get() == std::make_pair(S->getConditionVariable(), S->getCond()) &&
@@ -6248,8 +6263,8 @@
Else.get() == S->getElse())
return S;
- return getDerived().RebuildIfStmt(S->getIfLoc(), Cond, Then.get(),
- S->getElseLoc(), Else.get());
+ return getDerived().RebuildIfStmt(S->getIfLoc(), S->isConstexpr(), Cond,
+ Then.get(), S->getElseLoc(), Else.get());
}
template<typename Derived>