Implement p0292r2 (constexpr if), a likely C++1z feature.

llvm-svn: 273602
This commit is contained in:
Richard Smith 2016-06-23 19:16:49 +00:00
parent fe1397b977
commit b130fe7d31
24 changed files with 477 additions and 77 deletions

View File

@ -93,6 +93,13 @@ protected:
unsigned NumStmts : 32 - NumStmtBits; unsigned NumStmts : 32 - NumStmtBits;
}; };
class IfStmtBitfields {
friend class IfStmt;
unsigned : NumStmtBits;
unsigned IsConstexpr : 1;
};
class ExprBitfields { class ExprBitfields {
friend class Expr; friend class Expr;
friend class DeclRefExpr; // computeDependence friend class DeclRefExpr; // computeDependence
@ -248,6 +255,7 @@ protected:
union { union {
StmtBitfields StmtBits; StmtBitfields StmtBits;
CompoundStmtBitfields CompoundStmtBits; CompoundStmtBitfields CompoundStmtBits;
IfStmtBitfields IfStmtBits;
ExprBitfields ExprBits; ExprBitfields ExprBits;
CharacterLiteralBitfields CharacterLiteralBits; CharacterLiteralBitfields CharacterLiteralBits;
FloatingLiteralBitfields FloatingLiteralBits; FloatingLiteralBitfields FloatingLiteralBits;
@ -878,7 +886,8 @@ class IfStmt : public Stmt {
SourceLocation ElseLoc; SourceLocation ElseLoc;
public: public:
IfStmt(const ASTContext &C, SourceLocation IL, VarDecl *var, Expr *cond, IfStmt(const ASTContext &C, SourceLocation IL,
bool IsConstexpr, VarDecl *var, Expr *cond,
Stmt *then, SourceLocation EL = SourceLocation(), Stmt *then, SourceLocation EL = SourceLocation(),
Stmt *elsev = nullptr); Stmt *elsev = nullptr);
@ -918,6 +927,9 @@ public:
SourceLocation getElseLoc() const { return ElseLoc; } SourceLocation getElseLoc() const { return ElseLoc; }
void setElseLoc(SourceLocation L) { ElseLoc = L; } void setElseLoc(SourceLocation L) { ElseLoc = L; }
bool isConstexpr() const { return IfStmtBits.IsConstexpr; }
void setConstexpr(bool C) { IfStmtBits.IsConstexpr = C; }
SourceLocation getLocStart() const LLVM_READONLY { return IfLoc; } SourceLocation getLocStart() const LLVM_READONLY { return IfLoc; }
SourceLocation getLocEnd() const LLVM_READONLY { SourceLocation getLocEnd() const LLVM_READONLY {
if (SubExprs[ELSE]) if (SubExprs[ELSE])

View File

@ -512,6 +512,11 @@ def err_function_is_not_record : Error<
"unexpected %0 in function call; perhaps remove the %0?">; "unexpected %0 in function call; perhaps remove the %0?">;
def err_super_in_using_declaration : Error< def err_super_in_using_declaration : Error<
"'__super' cannot be used with a using declaration">; "'__super' cannot be used with a using declaration">;
def ext_constexpr_if : ExtWarn<
"constexpr if is a C++1z extension">, InGroup<CXX1z>;
def warn_cxx14_compat_constexpr_if : Warning<
"constexpr if is incompatible with C++ standards before C++1z">,
DefaultIgnore, InGroup<CXXPre1zCompat>;
// C++ derived classes // C++ derived classes
def err_dup_virtual : Error<"duplicate 'virtual' in base specifier">; def err_dup_virtual : Error<"duplicate 'virtual' in base specifier">;

View File

@ -75,10 +75,12 @@ def err_typecheck_converted_constant_expression_indirect : Error<
"conversion from %0 to %1 in converted constant expression would " "conversion from %0 to %1 in converted constant expression would "
"bind reference to a temporary">; "bind reference to a temporary">;
def err_expr_not_cce : Error< def err_expr_not_cce : Error<
"%select{case value|enumerator value|non-type template argument|array size}0 " "%select{case value|enumerator value|non-type template argument|"
"array size|constexpr if condition}0 "
"is not a constant expression">; "is not a constant expression">;
def ext_cce_narrowing : ExtWarn< def ext_cce_narrowing : ExtWarn<
"%select{case value|enumerator value|non-type template argument|array size}0 " "%select{case value|enumerator value|non-type template argument|"
"array size|constexpr if condition}0 "
"%select{cannot be narrowed from type %2 to %3|" "%select{cannot be narrowed from type %2 to %3|"
"evaluates to %2, which cannot be narrowed to type %3}1">, "evaluates to %2, which cannot be narrowed to type %3}1">,
InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure; InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure;
@ -4641,6 +4643,8 @@ def note_protected_by_vla_typedef : Note<
"jump bypasses initialization of VLA typedef">; "jump bypasses initialization of VLA typedef">;
def note_protected_by_vla_type_alias : Note< def note_protected_by_vla_type_alias : Note<
"jump bypasses initialization of VLA type alias">; "jump bypasses initialization of VLA type alias">;
def note_protected_by_constexpr_if : Note<
"jump enters controlled statement of constexpr if">;
def note_protected_by_vla : Note< def note_protected_by_vla : Note<
"jump bypasses initialization of variable length array">; "jump bypasses initialization of variable length array">;
def note_protected_by_objc_try : Note< def note_protected_by_objc_try : Note<

View File

@ -797,6 +797,11 @@ public:
/// run time. /// run time.
Unevaluated, Unevaluated,
/// \brief The current expression occurs within a discarded statement.
/// This behaves largely similarly to an unevaluated operand in preventing
/// definitions from being required, but not in other ways.
DiscardedStatement,
/// \brief The current expression occurs within an unevaluated /// \brief The current expression occurs within an unevaluated
/// operand that unconditionally permits abstract references to /// operand that unconditionally permits abstract references to
/// fields, such as a SIZE operator in MS-style inline assembly. /// fields, such as a SIZE operator in MS-style inline assembly.
@ -2329,7 +2334,8 @@ public:
CCEK_CaseValue, ///< Expression in a case label. CCEK_CaseValue, ///< Expression in a case label.
CCEK_Enumerator, ///< Enumerator value with fixed underlying type. CCEK_Enumerator, ///< Enumerator value with fixed underlying type.
CCEK_TemplateArg, ///< Value of a non-type template parameter. CCEK_TemplateArg, ///< Value of a non-type template parameter.
CCEK_NewExpr ///< Constant expression in a noptr-new-declarator. CCEK_NewExpr, ///< Constant expression in a noptr-new-declarator.
CCEK_ConstexprIf ///< Condition in a constexpr if statement.
}; };
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T, ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
llvm::APSInt &Value, CCEKind CCE); llvm::APSInt &Value, CCEKind CCE);
@ -3393,8 +3399,12 @@ public:
Stmt *SubStmt); Stmt *SubStmt);
class ConditionResult; class ConditionResult;
StmtResult ActOnIfStmt(SourceLocation IfLoc, ConditionResult Cond, StmtResult ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr,
Stmt *ThenVal, SourceLocation ElseLoc, Stmt *ElseVal); ConditionResult Cond, Stmt *ThenVal,
SourceLocation ElseLoc, Stmt *ElseVal);
StmtResult BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
ConditionResult Cond, Stmt *ThenVal,
SourceLocation ElseLoc, Stmt *ElseVal);
StmtResult ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, StmtResult ActOnStartOfSwitchStmt(SourceLocation SwitchLoc,
ConditionResult Cond); ConditionResult Cond);
StmtResult ActOnFinishSwitchStmt(SourceLocation SwitchLoc, StmtResult ActOnFinishSwitchStmt(SourceLocation SwitchLoc,
@ -8919,12 +8929,20 @@ public:
Decl *ConditionVar; Decl *ConditionVar;
FullExprArg Condition; FullExprArg Condition;
bool Invalid; bool Invalid;
bool HasKnownValue;
bool KnownValue;
friend class Sema; friend class Sema;
ConditionResult(Decl *ConditionVar, FullExprArg Condition) ConditionResult(Sema &S, Decl *ConditionVar, FullExprArg Condition,
: ConditionVar(ConditionVar), Condition(Condition), Invalid(false) {} bool IsConstexpr)
: ConditionVar(ConditionVar), Condition(Condition), Invalid(false),
HasKnownValue(IsConstexpr && Condition.get() &&
!Condition.get()->isValueDependent()),
KnownValue(HasKnownValue &&
!!Condition.get()->EvaluateKnownConstInt(S.Context)) {}
explicit ConditionResult(bool Invalid) explicit ConditionResult(bool Invalid)
: ConditionVar(nullptr), Condition(nullptr), Invalid(Invalid) {} : ConditionVar(nullptr), Condition(nullptr), Invalid(Invalid),
HasKnownValue(false), KnownValue(false) {}
public: public:
ConditionResult() : ConditionResult(false) {} ConditionResult() : ConditionResult(false) {}
@ -8933,12 +8951,18 @@ public:
return std::make_pair(cast_or_null<VarDecl>(ConditionVar), return std::make_pair(cast_or_null<VarDecl>(ConditionVar),
Condition.get()); Condition.get());
} }
llvm::Optional<bool> getKnownValue() const {
if (!HasKnownValue)
return None;
return KnownValue;
}
}; };
static ConditionResult ConditionError() { return ConditionResult(true); } static ConditionResult ConditionError() { return ConditionResult(true); }
enum class ConditionKind { enum class ConditionKind {
Boolean, ///< A boolean condition, from 'if', 'while', 'for', or 'do'. Boolean, ///< A boolean condition, from 'if', 'while', 'for', or 'do'.
Switch ///< An integral condition for a 'switch' statement. ConstexprIf, ///< A constant boolean condition from 'if constexpr'.
Switch ///< An integral condition for a 'switch' statement.
}; };
ConditionResult ActOnCondition(Scope *S, SourceLocation Loc, ConditionResult ActOnCondition(Scope *S, SourceLocation Loc,
@ -8963,7 +8987,8 @@ public:
/// \param Loc - A location associated with the condition, e.g. the /// \param Loc - A location associated with the condition, e.g. the
/// 'if' keyword. /// 'if' keyword.
/// \return true iff there were any errors /// \return true iff there were any errors
ExprResult CheckBooleanCondition(SourceLocation Loc, Expr *E); ExprResult CheckBooleanCondition(SourceLocation Loc, Expr *E,
bool IsConstexpr = false);
/// DiagnoseAssignmentAsCondition - Given that an expression is /// DiagnoseAssignmentAsCondition - Given that an expression is
/// being used as a boolean condition, warn if it's an assignment. /// being used as a boolean condition, warn if it's an assignment.
@ -8974,7 +8999,7 @@ public:
void DiagnoseEqualityWithExtraParens(ParenExpr *ParenE); void DiagnoseEqualityWithExtraParens(ParenExpr *ParenE);
/// CheckCXXBooleanCondition - Returns true if conversion to bool is invalid. /// CheckCXXBooleanCondition - Returns true if conversion to bool is invalid.
ExprResult CheckCXXBooleanCondition(Expr *CondExpr); ExprResult CheckCXXBooleanCondition(Expr *CondExpr, bool IsConstexpr = false);
/// ConvertIntegerToTypeWarnOnOverflow - Convert the specified APInt to have /// ConvertIntegerToTypeWarnOnOverflow - Convert the specified APInt to have
/// the specified width and sign. If an overflow occurs, detect it and emit /// the specified width and sign. If an overflow occurs, detect it and emit
@ -9533,15 +9558,18 @@ public:
/// \brief RAII object that enters a new expression evaluation context. /// \brief RAII object that enters a new expression evaluation context.
class EnterExpressionEvaluationContext { class EnterExpressionEvaluationContext {
Sema &Actions; Sema &Actions;
bool Entered = true;
public: public:
EnterExpressionEvaluationContext(Sema &Actions, EnterExpressionEvaluationContext(Sema &Actions,
Sema::ExpressionEvaluationContext NewContext, Sema::ExpressionEvaluationContext NewContext,
Decl *LambdaContextDecl = nullptr, Decl *LambdaContextDecl = nullptr,
bool IsDecltype = false) bool IsDecltype = false,
: Actions(Actions) { bool ShouldEnter = true)
Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl, : Actions(Actions), Entered(ShouldEnter) {
IsDecltype); if (Entered)
Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl,
IsDecltype);
} }
EnterExpressionEvaluationContext(Sema &Actions, EnterExpressionEvaluationContext(Sema &Actions,
Sema::ExpressionEvaluationContext NewContext, Sema::ExpressionEvaluationContext NewContext,
@ -9554,7 +9582,8 @@ public:
} }
~EnterExpressionEvaluationContext() { ~EnterExpressionEvaluationContext() {
Actions.PopExpressionEvaluationContext(); if (Entered)
Actions.PopExpressionEvaluationContext();
} }
}; };

View File

@ -4981,7 +4981,8 @@ Stmt *ASTNodeImporter::VisitIfStmt(IfStmt *S) {
if (!ToElseStmt && S->getElse()) if (!ToElseStmt && S->getElse())
return nullptr; return nullptr;
return new (Importer.getToContext()) IfStmt(Importer.getToContext(), return new (Importer.getToContext()) IfStmt(Importer.getToContext(),
ToIfLoc, ToConditionVariable, ToIfLoc, S->isConstexpr(),
ToConditionVariable,
ToCondition, ToThenStmt, ToCondition, ToThenStmt,
ToElseLoc, ToElseStmt); ToElseLoc, ToElseStmt);
} }

View File

@ -763,10 +763,11 @@ void MSAsmStmt::initialize(const ASTContext &C, StringRef asmstr,
}); });
} }
IfStmt::IfStmt(const ASTContext &C, SourceLocation IL, VarDecl *var, Expr *cond, IfStmt::IfStmt(const ASTContext &C, SourceLocation IL, bool IsConstexpr,
Stmt *then, SourceLocation EL, Stmt *elsev) VarDecl *var, Expr *cond, Stmt *then, SourceLocation EL,
: Stmt(IfStmtClass), IfLoc(IL), ElseLoc(EL) Stmt *elsev)
{ : Stmt(IfStmtClass), IfLoc(IL), ElseLoc(EL) {
setConstexpr(IsConstexpr);
setConditionVariable(C, var); setConditionVariable(C, var);
SubExprs[COND] = cond; SubExprs[COND] = cond;
SubExprs[THEN] = then; SubExprs[THEN] = then;

View File

@ -239,7 +239,7 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
SourceLocation()); SourceLocation());
// (5) Create the 'if' statement. // (5) Create the 'if' statement.
IfStmt *If = new (C) IfStmt(C, SourceLocation(), nullptr, UO, CS); IfStmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr, UO, CS);
return If; return If;
} }
@ -343,7 +343,7 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D)
/// Construct the If. /// Construct the If.
Stmt *If = Stmt *If =
new (C) IfStmt(C, SourceLocation(), nullptr, Comparison, Body, new (C) IfStmt(C, SourceLocation(), false, nullptr, Comparison, Body,
SourceLocation(), Else); SourceLocation(), Else);
return If; return If;

View File

@ -563,7 +563,8 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
// If the condition constant folds and can be elided, try to avoid emitting // If the condition constant folds and can be elided, try to avoid emitting
// the condition and the dead arm of the if/else. // the condition and the dead arm of the if/else.
bool CondConstant; bool CondConstant;
if (ConstantFoldsToSimpleInteger(S.getCond(), CondConstant)) { if (ConstantFoldsToSimpleInteger(S.getCond(), CondConstant,
S.isConstexpr())) {
// Figure out which block (then or else) is executed. // Figure out which block (then or else) is executed.
const Stmt *Executed = S.getThen(); const Stmt *Executed = S.getThen();
const Stmt *Skipped = S.getElse(); const Stmt *Skipped = S.getElse();
@ -572,7 +573,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
// If the skipped block has no labels in it, just emit the executed block. // If the skipped block has no labels in it, just emit the executed block.
// This avoids emitting dead code and simplifies the CFG substantially. // This avoids emitting dead code and simplifies the CFG substantially.
if (!ContainsLabel(Skipped)) { if (S.isConstexpr() || !ContainsLabel(Skipped)) {
if (CondConstant) if (CondConstant)
incrementProfileCounter(&S); incrementProfileCounter(&S);
if (Executed) { if (Executed) {

View File

@ -1107,9 +1107,10 @@ bool CodeGenFunction::containsBreak(const Stmt *S) {
/// to a constant, or if it does but contains a label, return false. If it /// to a constant, or if it does but contains a label, return false. If it
/// constant folds return true and set the boolean result in Result. /// constant folds return true and set the boolean result in Result.
bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond, bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
bool &ResultBool) { bool &ResultBool,
bool AllowLabels) {
llvm::APSInt ResultInt; llvm::APSInt ResultInt;
if (!ConstantFoldsToSimpleInteger(Cond, ResultInt)) if (!ConstantFoldsToSimpleInteger(Cond, ResultInt, AllowLabels))
return false; return false;
ResultBool = ResultInt.getBoolValue(); ResultBool = ResultInt.getBoolValue();
@ -1119,15 +1120,16 @@ bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
/// ConstantFoldsToSimpleInteger - If the specified expression does not fold /// ConstantFoldsToSimpleInteger - If the specified expression does not fold
/// to a constant, or if it does but contains a label, return false. If it /// to a constant, or if it does but contains a label, return false. If it
/// constant folds return true and set the folded value. /// constant folds return true and set the folded value.
bool CodeGenFunction:: bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &ResultInt) { llvm::APSInt &ResultInt,
bool AllowLabels) {
// FIXME: Rename and handle conversion of other evaluatable things // FIXME: Rename and handle conversion of other evaluatable things
// to bool. // to bool.
llvm::APSInt Int; llvm::APSInt Int;
if (!Cond->EvaluateAsInt(Int, getContext())) if (!Cond->EvaluateAsInt(Int, getContext()))
return false; // Not foldable, not integer or not fully evaluatable. return false; // Not foldable, not integer or not fully evaluatable.
if (CodeGenFunction::ContainsLabel(Cond)) if (!AllowLabels && CodeGenFunction::ContainsLabel(Cond))
return false; // Contains a label. return false; // Contains a label.
ResultInt = Int; ResultInt = Int;

View File

@ -3051,13 +3051,15 @@ public:
/// ConstantFoldsToSimpleInteger - If the specified expression does not fold /// ConstantFoldsToSimpleInteger - If the specified expression does not fold
/// to a constant, or if it does but contains a label, return false. If it /// to a constant, or if it does but contains a label, return false. If it
/// constant folds return true and set the boolean result in Result. /// constant folds return true and set the boolean result in Result.
bool ConstantFoldsToSimpleInteger(const Expr *Cond, bool &Result); bool ConstantFoldsToSimpleInteger(const Expr *Cond, bool &Result,
bool AllowLabels = false);
/// ConstantFoldsToSimpleInteger - If the specified expression does not fold /// ConstantFoldsToSimpleInteger - If the specified expression does not fold
/// to a constant, or if it does but contains a label, return false. If it /// to a constant, or if it does but contains a label, return false. If it
/// constant folds return true and set the folded value. /// constant folds return true and set the folded value.
bool ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &Result); bool ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &Result,
bool AllowLabels = false);
/// EmitBranchOnBoolExpr - Emit a branch on a boolean condition (e.g. for an /// EmitBranchOnBoolExpr - Emit a branch on a boolean condition (e.g. for an
/// if statement) to the specified blocks. Based on the condition, this might /// if statement) to the specified blocks. Based on the condition, this might
/// try to simplify the codegen of the conditional based on the branch. /// try to simplify the codegen of the conditional based on the branch.

View File

@ -1108,6 +1108,14 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
assert(Tok.is(tok::kw_if) && "Not an if stmt!"); assert(Tok.is(tok::kw_if) && "Not an if stmt!");
SourceLocation IfLoc = ConsumeToken(); // eat the 'if'. SourceLocation IfLoc = ConsumeToken(); // eat the 'if'.
bool IsConstexpr = false;
if (Tok.is(tok::kw_constexpr)) {
Diag(Tok, getLangOpts().CPlusPlus1z ? diag::warn_cxx14_compat_constexpr_if
: diag::ext_constexpr_if);
IsConstexpr = true;
ConsumeToken();
}
if (Tok.isNot(tok::l_paren)) { if (Tok.isNot(tok::l_paren)) {
Diag(Tok, diag::err_expected_lparen_after) << "if"; Diag(Tok, diag::err_expected_lparen_after) << "if";
SkipUntil(tok::semi); SkipUntil(tok::semi);
@ -1132,9 +1140,15 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
// Parse the condition. // Parse the condition.
Sema::ConditionResult Cond; Sema::ConditionResult Cond;
if (ParseParenExprOrCondition(Cond, IfLoc, Sema::ConditionKind::Boolean)) if (ParseParenExprOrCondition(Cond, IfLoc,
IsConstexpr ? Sema::ConditionKind::ConstexprIf
: Sema::ConditionKind::Boolean))
return StmtError(); return StmtError();
llvm::Optional<bool> ConstexprCondition;
if (IsConstexpr)
ConstexprCondition = Cond.getKnownValue();
// C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if // C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if
// there is no compound stmt. C90 does not have this clause. We only do this // there is no compound stmt. C90 does not have this clause. We only do this
// if the body isn't a compound statement to avoid push/pop in common cases. // if the body isn't a compound statement to avoid push/pop in common cases.
@ -1159,7 +1173,13 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
SourceLocation ThenStmtLoc = Tok.getLocation(); SourceLocation ThenStmtLoc = Tok.getLocation();
SourceLocation InnerStatementTrailingElseLoc; SourceLocation InnerStatementTrailingElseLoc;
StmtResult ThenStmt(ParseStatement(&InnerStatementTrailingElseLoc)); StmtResult ThenStmt;
{
EnterExpressionEvaluationContext PotentiallyDiscarded(
Actions, Sema::DiscardedStatement, nullptr, false,
/*ShouldEnter=*/ConstexprCondition && !*ConstexprCondition);
ThenStmt = ParseStatement(&InnerStatementTrailingElseLoc);
}
// Pop the 'if' scope if needed. // Pop the 'if' scope if needed.
InnerScope.Exit(); InnerScope.Exit();
@ -1185,8 +1205,12 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
// The substatement in a selection-statement (each substatement, in the else // The substatement in a selection-statement (each substatement, in the else
// form of the if statement) implicitly defines a local scope. // form of the if statement) implicitly defines a local scope.
// //
ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, Tok.is(tok::l_brace)); ParseScope InnerScope(this, Scope::DeclScope, C99orCXX,
Tok.is(tok::l_brace));
EnterExpressionEvaluationContext PotentiallyDiscarded(
Actions, Sema::DiscardedStatement, nullptr, false,
/*ShouldEnter=*/ConstexprCondition && *ConstexprCondition);
ElseStmt = ParseStatement(); ElseStmt = ParseStatement();
// Pop the 'else' scope if needed. // Pop the 'else' scope if needed.
@ -1217,7 +1241,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
if (ElseStmt.isInvalid()) if (ElseStmt.isInvalid())
ElseStmt = Actions.ActOnNullStmt(ElseStmtLoc); ElseStmt = Actions.ActOnNullStmt(ElseStmtLoc);
return Actions.ActOnIfStmt(IfLoc, Cond, ThenStmt.get(), ElseLoc, return Actions.ActOnIfStmt(IfLoc, IsConstexpr, Cond, ThenStmt.get(), ElseLoc,
ElseStmt.get()); ElseStmt.get());
} }

View File

@ -319,6 +319,37 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
Jumps.push_back(S); Jumps.push_back(S);
break; 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: { case Stmt::CXXTryStmtClass: {
CXXTryStmt *TS = cast<CXXTryStmt>(S); CXXTryStmt *TS = cast<CXXTryStmt>(S);
{ {

View File

@ -12903,6 +12903,11 @@ static bool IsPotentiallyEvaluatedContext(Sema &SemaRef) {
// definition of a null pointer constant is completely crazy.) // definition of a null pointer constant is completely crazy.)
return false; 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::ConstantEvaluated:
case Sema::PotentiallyEvaluated: case Sema::PotentiallyEvaluated:
// We are in a potentially evaluated expression (or a constant-expression // We are in a potentially evaluated expression (or a constant-expression
@ -14192,6 +14197,7 @@ bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *Statement,
switch (ExprEvalContexts.back().Context) { switch (ExprEvalContexts.back().Context) {
case Unevaluated: case Unevaluated:
case UnevaluatedAbstract: case UnevaluatedAbstract:
case DiscardedStatement:
// The argument will never be evaluated, so don't complain. // The argument will never be evaluated, so don't complain.
break; break;
@ -14341,7 +14347,8 @@ void Sema::DiagnoseEqualityWithExtraParens(ParenExpr *ParenE) {
} }
} }
ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E) { ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E,
bool IsConstexpr) {
DiagnoseAssignmentAsCondition(E); DiagnoseAssignmentAsCondition(E);
if (ParenExpr *parenE = dyn_cast<ParenExpr>(E)) if (ParenExpr *parenE = dyn_cast<ParenExpr>(E))
DiagnoseEqualityWithExtraParens(parenE); DiagnoseEqualityWithExtraParens(parenE);
@ -14352,7 +14359,7 @@ ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E) {
if (!E->isTypeDependent()) { if (!E->isTypeDependent()) {
if (getLangOpts().CPlusPlus) if (getLangOpts().CPlusPlus)
return CheckCXXBooleanCondition(E); // C++ 6.4p4 return CheckCXXBooleanCondition(E, IsConstexpr); // C++ 6.4p4
ExprResult ERes = DefaultFunctionArrayLvalueConversion(E); ExprResult ERes = DefaultFunctionArrayLvalueConversion(E);
if (ERes.isInvalid()) if (ERes.isInvalid())
@ -14383,6 +14390,10 @@ Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc,
Cond = CheckBooleanCondition(Loc, SubExpr); Cond = CheckBooleanCondition(Loc, SubExpr);
break; break;
case ConditionKind::ConstexprIf:
Cond = CheckBooleanCondition(Loc, SubExpr, true);
break;
case ConditionKind::Switch: case ConditionKind::Switch:
Cond = CheckSwitchCondition(Loc, SubExpr); Cond = CheckSwitchCondition(Loc, SubExpr);
break; break;
@ -14390,7 +14401,8 @@ Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc,
if (Cond.isInvalid()) if (Cond.isInvalid())
return ConditionError(); return ConditionError();
return ConditionResult(nullptr, MakeFullExpr(Cond.get(), Loc)); return ConditionResult(*this, nullptr, MakeFullExpr(Cond.get(), Loc),
CK == ConditionKind::ConstexprIf);
} }
namespace { namespace {

View File

@ -3061,7 +3061,8 @@ Sema::ConditionResult Sema::ActOnConditionVariable(Decl *ConditionVar,
CheckConditionVariable(cast<VarDecl>(ConditionVar), StmtLoc, CK); CheckConditionVariable(cast<VarDecl>(ConditionVar), StmtLoc, CK);
if (E.isInvalid()) if (E.isInvalid())
return ConditionError(); 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, /// \brief Check the use of the given variable as a C++ condition in an if,
@ -3096,6 +3097,9 @@ ExprResult Sema::CheckConditionVariable(VarDecl *ConditionVar,
case ConditionKind::Boolean: case ConditionKind::Boolean:
return CheckBooleanCondition(StmtLoc, Condition.get()); return CheckBooleanCondition(StmtLoc, Condition.get());
case ConditionKind::ConstexprIf:
return CheckBooleanCondition(StmtLoc, Condition.get(), true);
case ConditionKind::Switch: case ConditionKind::Switch:
return CheckSwitchCondition(StmtLoc, Condition.get()); return CheckSwitchCondition(StmtLoc, Condition.get());
} }
@ -3104,7 +3108,7 @@ ExprResult Sema::CheckConditionVariable(VarDecl *ConditionVar,
} }
/// CheckCXXBooleanCondition - Returns true if a conversion to bool is invalid. /// 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: // C++ 6.4p4:
// The value of a condition that is an initialized declaration in a statement // 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 // other than a switch statement is the value of the declared variable
@ -3113,7 +3117,12 @@ ExprResult Sema::CheckCXXBooleanCondition(Expr *CondExpr) {
// The value of a condition that is an expression is the value of the // The value of a condition that is an expression is the value of the
// expression, implicitly converted to bool. // 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++ /// Helper function to determine whether this is the (deprecated) C++

View File

@ -142,6 +142,7 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
AbstractInstanceResult = IMA_Abstract; AbstractInstanceResult = IMA_Abstract;
break; break;
case Sema::DiscardedStatement:
case Sema::ConstantEvaluated: case Sema::ConstantEvaluated:
case Sema::PotentiallyEvaluated: case Sema::PotentiallyEvaluated:
case Sema::PotentiallyEvaluatedIfUsed: case Sema::PotentiallyEvaluatedIfUsed:

View File

@ -1635,6 +1635,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
ExprEvalContexts.back().Lambdas.push_back(Lambda); ExprEvalContexts.back().Lambdas.push_back(Lambda);
break; break;
case DiscardedStatement:
case PotentiallyEvaluated: case PotentiallyEvaluated:
case PotentiallyEvaluatedIfUsed: case PotentiallyEvaluatedIfUsed:
break; break;

View File

@ -504,31 +504,43 @@ public:
} }
StmtResult StmtResult
Sema::ActOnIfStmt(SourceLocation IfLoc, ConditionResult Cond, Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr, ConditionResult Cond,
Stmt *thenStmt, SourceLocation ElseLoc, Stmt *thenStmt, SourceLocation ElseLoc,
Stmt *elseStmt) { Stmt *elseStmt) {
auto CondVal = Cond.get(); if (Cond.isInvalid())
if (Cond.isInvalid()) { Cond = ConditionResult(
CondVal.first = nullptr; *this, nullptr,
CondVal.second = new (Context) MakeFullExpr(new (Context) OpaqueValueExpr(SourceLocation(),
OpaqueValueExpr(SourceLocation(), Context.BoolTy, VK_RValue); Context.BoolTy, VK_RValue),
} IfLoc),
false);
Expr *CondExpr = Cond.get().second;
if (!Diags.isIgnored(diag::warn_comma_operator, if (!Diags.isIgnored(diag::warn_comma_operator,
CondVal.second->getExprLoc())) CondExpr->getExprLoc()))
CommaVisitor(*this).Visit(CondVal.second); 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); DiagnoseUnusedExprResult(thenStmt);
if (!elseStmt) {
DiagnoseEmptyStmtBody(CondVal.second->getLocEnd(), thenStmt,
diag::warn_empty_if_body);
}
DiagnoseUnusedExprResult(elseStmt); DiagnoseUnusedExprResult(elseStmt);
return new (Context) IfStmt(Context, IfLoc, CondVal.first, CondVal.second, return new (Context) IfStmt(Context, IfLoc, IsConstexpr, Cond.get().first,
thenStmt, ElseLoc, elseStmt); Cond.get().second, thenStmt, ElseLoc, elseStmt);
} }
namespace { namespace {
@ -2836,8 +2848,21 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
CapturingScopeInfo *CurCap = cast<CapturingScopeInfo>(getCurFunction()); CapturingScopeInfo *CurCap = cast<CapturingScopeInfo>(getCurFunction());
QualType FnRetType = CurCap->ReturnType; QualType FnRetType = CurCap->ReturnType;
LambdaScopeInfo *CurLambda = dyn_cast<LambdaScopeInfo>(CurCap); 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'. // In C++1y, the return type may involve 'auto'.
// FIXME: Blocks might have a return type of 'auto' explicitly specified. // FIXME: Blocks might have a return type of 'auto' explicitly specified.
FunctionDecl *FD = CurLambda->CallOperator; FunctionDecl *FD = CurLambda->CallOperator;
@ -3118,9 +3143,8 @@ StmtResult
Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp, Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
Scope *CurScope) { Scope *CurScope) {
StmtResult R = BuildReturnStmt(ReturnLoc, RetValExp); StmtResult R = BuildReturnStmt(ReturnLoc, RetValExp);
if (R.isInvalid()) { if (R.isInvalid() || ExprEvalContexts.back().Context == DiscardedStatement)
return R; return R;
}
if (VarDecl *VD = if (VarDecl *VD =
const_cast<VarDecl*>(cast<ReturnStmt>(R.get())->getNRVOCandidate())) { const_cast<VarDecl*>(cast<ReturnStmt>(R.get())->getNRVOCandidate())) {
@ -3169,6 +3193,19 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
} else // If we don't have a function/method context, bail. } else // If we don't have a function/method context, bail.
return StmtError(); 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 // FIXME: Add a flag to the ScopeInfo to indicate whether we're performing
// deduction. // deduction.
if (getLangOpts().CPlusPlus14) { if (getLangOpts().CPlusPlus14) {

View File

@ -1174,9 +1174,10 @@ public:
/// ///
/// By default, performs semantic analysis to build the new statement. /// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior. /// Subclasses may override this routine to provide different behavior.
StmtResult RebuildIfStmt(SourceLocation IfLoc, Sema::ConditionResult Cond, StmtResult RebuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
Stmt *Then, SourceLocation ElseLoc, Stmt *Else) { Sema::ConditionResult Cond, Stmt *Then,
return getSema().ActOnIfStmt(IfLoc, Cond, Then, ElseLoc, Else); SourceLocation ElseLoc, Stmt *Else) {
return getSema().ActOnIfStmt(IfLoc, IsConstexpr, Cond, Then, ElseLoc, Else);
} }
/// \brief Start building a new switch statement. /// \brief Start building a new switch statement.
@ -6228,19 +6229,33 @@ TreeTransform<Derived>::TransformIfStmt(IfStmt *S) {
// Transform the condition // Transform the condition
Sema::ConditionResult Cond = getDerived().TransformCondition( Sema::ConditionResult Cond = getDerived().TransformCondition(
S->getIfLoc(), S->getConditionVariable(), S->getCond(), S->getIfLoc(), S->getConditionVariable(), S->getCond(),
Sema::ConditionKind::Boolean); S->isConstexpr() ? Sema::ConditionKind::ConstexprIf
: Sema::ConditionKind::Boolean);
if (Cond.isInvalid()) if (Cond.isInvalid())
return StmtError(); 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. // Transform the "then" branch.
StmtResult Then = getDerived().TransformStmt(S->getThen()); StmtResult Then;
if (Then.isInvalid()) if (!ConstexprConditionValue || *ConstexprConditionValue) {
return StmtError(); Then = getDerived().TransformStmt(S->getThen());
if (Then.isInvalid())
return StmtError();
} else {
Then = new (getSema().Context) NullStmt(S->getThen()->getLocStart());
}
// Transform the "else" branch. // Transform the "else" branch.
StmtResult Else = getDerived().TransformStmt(S->getElse()); StmtResult Else;
if (Else.isInvalid()) if (!ConstexprConditionValue || !*ConstexprConditionValue) {
return StmtError(); Else = getDerived().TransformStmt(S->getElse());
if (Else.isInvalid())
return StmtError();
}
if (!getDerived().AlwaysRebuild() && if (!getDerived().AlwaysRebuild() &&
Cond.get() == std::make_pair(S->getConditionVariable(), S->getCond()) && Cond.get() == std::make_pair(S->getConditionVariable(), S->getCond()) &&
@ -6248,8 +6263,8 @@ TreeTransform<Derived>::TransformIfStmt(IfStmt *S) {
Else.get() == S->getElse()) Else.get() == S->getElse())
return S; return S;
return getDerived().RebuildIfStmt(S->getIfLoc(), Cond, Then.get(), return getDerived().RebuildIfStmt(S->getIfLoc(), S->isConstexpr(), Cond,
S->getElseLoc(), Else.get()); Then.get(), S->getElseLoc(), Else.get());
} }
template<typename Derived> template<typename Derived>

View File

@ -184,6 +184,7 @@ void ASTStmtReader::VisitAttributedStmt(AttributedStmt *S) {
void ASTStmtReader::VisitIfStmt(IfStmt *S) { void ASTStmtReader::VisitIfStmt(IfStmt *S) {
VisitStmt(S); VisitStmt(S);
S->setConstexpr(Record[Idx++]);
S->setConditionVariable(Reader.getContext(), S->setConditionVariable(Reader.getContext(),
ReadDeclAs<VarDecl>(Record, Idx)); ReadDeclAs<VarDecl>(Record, Idx));
S->setCond(Reader.ReadSubExpr()); S->setCond(Reader.ReadSubExpr());

View File

@ -128,6 +128,7 @@ void ASTStmtWriter::VisitAttributedStmt(AttributedStmt *S) {
void ASTStmtWriter::VisitIfStmt(IfStmt *S) { void ASTStmtWriter::VisitIfStmt(IfStmt *S) {
VisitStmt(S); VisitStmt(S);
Record.push_back(S->isConstexpr());
Record.AddDeclRef(S->getConditionVariable()); Record.AddDeclRef(S->getConditionVariable());
Record.AddStmt(S->getCond()); Record.AddStmt(S->getCond());
Record.AddStmt(S->getThen()); Record.AddStmt(S->getThen());

View File

@ -0,0 +1,47 @@
// RUN: %clang_cc1 -std=c++1z -verify %s
template<typename T, typename U> constexpr bool same = false;
template<typename T> constexpr bool same<T, T> = true;
auto a() {
if constexpr (false)
return 0;
}
static_assert(same<decltype(a()), void>);
auto b() {
if constexpr (false)
return 0;
else
return 0.0;
}
static_assert(same<decltype(b()), double>);
auto c() {
if constexpr (true)
return "foo";
else
return 'x';
if constexpr (false)
return 7.6;
else
return 5; // expected-error {{deduced as 'int' here but deduced as 'const char *' in earlier}}
}
template<int k> auto d() {
if constexpr(k == 0)
return 0;
if constexpr(k == 1)
return "foo";
else if constexpr (k == 2)
return 1.0;
}
static_assert(same<decltype(d<0>()), int>);
static_assert(same<decltype(d<1>()), const char *>);
static_assert(same<decltype(d<2>()), double>);
static_assert(same<decltype(d<3>()), void>);
auto e = []{ if constexpr (false) return 0; }(); // expected-error {{variable has incomplete type 'void'}}
auto f = []{ if constexpr (true) return 0; }();
static_assert(same<decltype(e), int>);

View File

@ -0,0 +1,137 @@
// RUN: %clang_cc1 -std=c++1z -verify %s
// RUN: %clang_cc1 -std=c++1z -verify %s -DUNDEFINED
#ifdef UNDEFINED
// "used but not defined" errors don't get produced if we have more interesting
// errors.
namespace std_example {
template <typename T, typename... Rest> void g(T &&p, Rest &&... rs) {
// use p
if constexpr(sizeof...(rs) > 0)
g(rs...);
}
void use_g() {
g(1, 2, 3);
}
static int x(); // no definition of x required
int f() {
if constexpr (true)
return 0;
else if (x())
return x();
else
return -x();
}
}
namespace odr_use_in_selected_arm {
static int x(); // expected-warning {{is not defined}}
int f() {
if constexpr (false)
return 0;
else if (x()) // expected-note {{here}}
return x();
else
return -x();
}
}
#else
namespace ccce {
void f() {
if (5) {}
if constexpr (5) {} // expected-error {{cannot be narrowed}}
}
template<int N> void g() {
if constexpr (N) {} // expected-error {{cannot be narrowed}}
}
template void g<5>(); // expected-note {{instantiation of}}
}
namespace generic_lambda {
// Substituting for T produces a hard error here, even if substituting for
// the type of x would remove the error.
template<typename T> void f() {
[](auto x) {
if constexpr (sizeof(T) == 1 && sizeof(x) == 1)
T::error(); // expected-error 2{{'::'}}
} (0);
}
template<typename T> void g() {
[](auto x) {
if constexpr (sizeof(T) == 1)
if constexpr (sizeof(x) == 1)
T::error(); // expected-error {{'::'}}
} (0);
}
void use() {
f<int>(); // expected-note {{instantiation of}}
f<char>(); // expected-note {{instantiation of}}
g<int>(); // ok
g<char>(); // expected-note {{instantiation of}}
}
}
namespace potentially_discarded_branch_target {
void in_switch(int n) {
switch (n)
case 4: if constexpr(sizeof(n) == 4) return;
if constexpr(sizeof(n) == 4)
switch (n) case 4: return;
switch (n) {
if constexpr (sizeof(n) == 4) // expected-note 2{{constexpr if}}
case 4: return; // expected-error {{cannot jump}}
else
default: break; // expected-error {{cannot jump}}
}
}
template<typename T>
void in_switch_tmpl(int n) {
switch (n) {
if constexpr (sizeof(T) == 4) // expected-note 2{{constexpr if}}
case 4: return; // expected-error {{cannot jump}}
else
default: break; // expected-error {{cannot jump}}
}
}
void goto_scope(int n) {
goto foo; // expected-error {{cannot jump}}
if constexpr(sizeof(n) == 4) // expected-note {{constexpr if}}
foo: return;
bar:
if constexpr(sizeof(n) == 4)
goto bar; // ok
}
template<typename T>
void goto_scope(int n) {
goto foo; // expected-error {{cannot jump}}
if constexpr(sizeof(n) == 4) // expected-note {{constexpr if}}
foo: return;
bar:
if constexpr(sizeof(n) == 4)
goto bar; // ok
}
void goto_redef(int n) {
a: if constexpr(sizeof(n) == 4) // expected-error {{redefinition}} expected-note {{constexpr if}}
a: goto a; // expected-note 2{{previous}}
else
a: goto a; // expected-error {{redefinition}} expected-error {{cannot jump}}
}
void evil_things() {
goto evil_label; // expected-error {{cannot jump}}
if constexpr (true || ({evil_label: false;})) {} // expected-note {{constexpr if}}
if constexpr (true) // expected-note {{constexpr if}}
goto surprise; // expected-error {{cannot jump}}
else
surprise: {}
}
}
#endif

View File

@ -0,0 +1,21 @@
// RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - | FileCheck %s --implicit-check-not=should_not_be_used
void should_be_used_1();
void should_be_used_2();
void should_not_be_used();
void f() {
if constexpr (false)
should_not_be_used();
else
should_be_used_1();
if constexpr (true || ({ label: false; }))
should_be_used_2();
else {
goto foo;
foo: should_not_be_used();
}
}
// CHECK: should_be_used_1
// CHECK: should_be_used_2

View File

@ -671,6 +671,12 @@ as the draft C++1z standard evolves.</p>
<td><a href="http://wg21.link/p0245r1">P0245R1</a></td> <td><a href="http://wg21.link/p0245r1">P0245R1</a></td>
<td class="full" align="center">Yes</td> <td class="full" align="center">Yes</td>
</tr> </tr>
<!-- Oulu papers -->
<tr>
<td><tt>constexpr</tt> <em>if-statement</em>s</td>
<td><a href="http://wg21.link/p0292r2">P0292R2</a></td>
<td class="svn" align="center">SVN</td>
</tr>
</table> </table>
<p> <p>