forked from OSchip/llvm-project
Implement p0292r2 (constexpr if), a likely C++1z feature.
llvm-svn: 273602
This commit is contained in:
parent
fe1397b977
commit
b130fe7d31
|
@ -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])
|
||||||
|
|
|
@ -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">;
|
||||||
|
|
|
@ -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<
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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++
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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>);
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue