зеркало из https://github.com/microsoft/clang-1.git
Add checks and diagnostics for many of the cases which C++11 considers to not
be constant expressions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@146479 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
9081c049ec
Коммит
c1c5f27c64
|
@ -3162,6 +3162,12 @@ inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
|
|||
DiagnosticsEngine::ak_nameddecl);
|
||||
return DB;
|
||||
}
|
||||
inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
|
||||
const NamedDecl* ND) {
|
||||
PD.AddTaggedVal(reinterpret_cast<intptr_t>(ND),
|
||||
DiagnosticsEngine::ak_nameddecl);
|
||||
return PD;
|
||||
}
|
||||
|
||||
template<typename decl_type>
|
||||
void Redeclarable<decl_type>::setPreviousDeclaration(decl_type *PrevDecl) {
|
||||
|
|
|
@ -15,6 +15,24 @@ def note_expr_divide_by_zero : Note<"division by zero">;
|
|||
def note_constexpr_invalid_cast : Note<
|
||||
"%select{reinterpret_cast|dynamic_cast|cast which performs the conversions of"
|
||||
" a reinterpret_cast|cast from %1}0 is not allowed in a constant expression">;
|
||||
def note_constexpr_overflow : Note<
|
||||
"value %0 is outside the range of representable values of type %1">;
|
||||
def note_constexpr_invalid_function : Note<
|
||||
"%select{non-constexpr|undefined}0 %select{function|constructor}1 %2 cannot "
|
||||
"be used in a constant expression">;
|
||||
def note_constexpr_nonliteral : Note<
|
||||
"non-literal type %0 cannot be used in a constant expression">;
|
||||
def note_constexpr_non_global : Note<
|
||||
"%select{pointer|reference}0 to %select{|subobject of }1"
|
||||
"%select{temporary|%4}2 %select{is not a constant expression|"
|
||||
"cannot be returned from a constexpr function|"
|
||||
"cannot be used to initialize a member in a constant expression}3">;
|
||||
def note_constexpr_past_end : Note<
|
||||
"dereferenced pointer past the end of %select{|subobject of}0 "
|
||||
"%select{temporary|%2}1 is not a constant expression">;
|
||||
def note_constexpr_temporary_here : Note<"temporary created here">;
|
||||
def note_constexpr_depth_limit_exceeded : Note<
|
||||
"constexpr evaluation exceeded maximum depth of %0 calls">;
|
||||
|
||||
// inline asm related.
|
||||
let CategoryName = "Inline Assembly Issue" in {
|
||||
|
|
|
@ -18,6 +18,7 @@ let Component = "Common" in {
|
|||
def fatal_too_many_errors
|
||||
: Error<"too many errors emitted, stopping now">, DefaultFatal;
|
||||
|
||||
def note_declared_at : Note<"declared here">;
|
||||
def note_previous_definition : Note<"previous definition is here">;
|
||||
def note_previous_declaration : Note<"previous declaration is here">;
|
||||
def note_previous_implicit_declaration : Note<
|
||||
|
|
|
@ -485,7 +485,6 @@ def warn_strict_multiple_method_decl : Warning<
|
|||
"multiple methods named %0 found">, InGroup<StrictSelector>, DefaultIgnore;
|
||||
def warn_accessor_property_type_mismatch : Warning<
|
||||
"type of property %0 does not match type of accessor %1">;
|
||||
def note_declared_at : Note<"declared here">;
|
||||
def note_method_declared_at : Note<"method declared here">;
|
||||
def err_setter_type_void : Error<"type of setter must be void">;
|
||||
def err_duplicate_method_decl : Error<"duplicate declaration of method %0">;
|
||||
|
|
|
@ -293,11 +293,15 @@ namespace {
|
|||
/// declaration whose initializer is being evaluated, if any.
|
||||
APValue *EvaluatingDeclValue;
|
||||
|
||||
/// HasActiveDiagnostic - Was the previous diagnostic stored? If so, further
|
||||
/// notes attached to it will also be stored, otherwise they will not be.
|
||||
bool HasActiveDiagnostic;
|
||||
|
||||
|
||||
EvalInfo(const ASTContext &C, Expr::EvalStatus &S)
|
||||
: Ctx(const_cast<ASTContext&>(C)), EvalStatus(S), CurrentCall(0),
|
||||
CallStackDepth(0), BottomFrame(*this, 0, 0), EvaluatingDecl(0),
|
||||
EvaluatingDeclValue(0) {}
|
||||
EvaluatingDeclValue(0), HasActiveDiagnostic(false) {}
|
||||
|
||||
const CCValue *getOpaqueValue(const OpaqueValueExpr *e) const {
|
||||
MapTy::const_iterator i = OpaqueValues.find(e);
|
||||
|
@ -312,33 +316,55 @@ namespace {
|
|||
|
||||
const LangOptions &getLangOpts() const { return Ctx.getLangOptions(); }
|
||||
|
||||
bool atCallLimit() const {
|
||||
return CallStackDepth > getLangOpts().ConstexprCallDepth;
|
||||
bool CheckCallLimit(SourceLocation Loc) {
|
||||
if (CallStackDepth <= getLangOpts().ConstexprCallDepth)
|
||||
return true;
|
||||
Diag(Loc, diag::note_constexpr_depth_limit_exceeded)
|
||||
<< getLangOpts().ConstexprCallDepth;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Add a diagnostic to the diagnostics list.
|
||||
PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId) {
|
||||
PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator());
|
||||
EvalStatus.Diag->push_back(std::make_pair(Loc, PD));
|
||||
return EvalStatus.Diag->back().second;
|
||||
}
|
||||
|
||||
public:
|
||||
/// Diagnose that the evaluation cannot be folded.
|
||||
OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId) {
|
||||
OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId,
|
||||
unsigned ExtraNotes = 0) {
|
||||
// If we have a prior diagnostic, it will be noting that the expression
|
||||
// isn't a constant expression. This diagnostic is more important.
|
||||
// FIXME: We might want to show both diagnostics to the user.
|
||||
if (EvalStatus.Diag) {
|
||||
HasActiveDiagnostic = true;
|
||||
EvalStatus.Diag->clear();
|
||||
EvalStatus.Diag->reserve(1);
|
||||
PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator());
|
||||
EvalStatus.Diag->push_back(std::make_pair(Loc, PD));
|
||||
EvalStatus.Diag->reserve(1 + ExtraNotes);
|
||||
// FIXME: Add a call stack for constexpr evaluation.
|
||||
return OptionalDiagnostic(&EvalStatus.Diag->back().second);
|
||||
return OptionalDiagnostic(&addDiag(Loc, DiagId));
|
||||
}
|
||||
HasActiveDiagnostic = false;
|
||||
return OptionalDiagnostic();
|
||||
}
|
||||
|
||||
/// Diagnose that the evaluation does not produce a C++11 core constant
|
||||
/// expression.
|
||||
OptionalDiagnostic CCEDiag(SourceLocation Loc, diag::kind DiagId) {
|
||||
OptionalDiagnostic CCEDiag(SourceLocation Loc, diag::kind DiagId,
|
||||
unsigned ExtraNotes = 0) {
|
||||
// Don't override a previous diagnostic.
|
||||
if (!EvalStatus.Diag || !EvalStatus.Diag->empty())
|
||||
return OptionalDiagnostic();
|
||||
return Diag(Loc, DiagId);
|
||||
return Diag(Loc, DiagId, ExtraNotes);
|
||||
}
|
||||
|
||||
/// Add a note to a prior diagnostic.
|
||||
OptionalDiagnostic Note(SourceLocation Loc, diag::kind DiagId) {
|
||||
if (!HasActiveDiagnostic)
|
||||
return OptionalDiagnostic();
|
||||
return OptionalDiagnostic(&addDiag(Loc, DiagId));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -515,11 +541,20 @@ namespace {
|
|||
return castBack(Base);
|
||||
}
|
||||
};
|
||||
|
||||
/// Kinds of constant expression checking, for diagnostics.
|
||||
enum CheckConstantExpressionKind {
|
||||
CCEK_Constant, ///< A normal constant.
|
||||
CCEK_ReturnValue, ///< A constexpr function return value.
|
||||
CCEK_MemberInit ///< A constexpr constructor mem-initializer.
|
||||
};
|
||||
}
|
||||
|
||||
static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E);
|
||||
static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info,
|
||||
const LValue &This, const Expr *E);
|
||||
const LValue &This, const Expr *E,
|
||||
CheckConstantExpressionKind CCEK
|
||||
= CCEK_Constant);
|
||||
static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info);
|
||||
static bool EvaluatePointer(const Expr *E, LValue &Result, EvalInfo &Info);
|
||||
static bool EvaluateMemberPointer(const Expr *E, MemberPtr &Result,
|
||||
|
@ -586,22 +621,53 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
|
|||
/// value for a constant expression. Type T should be either LValue or CCValue.
|
||||
template<typename T>
|
||||
static bool CheckLValueConstantExpression(EvalInfo &Info, const Expr *E,
|
||||
const T &LVal, APValue &Value) {
|
||||
if (!IsGlobalLValue(LVal.getLValueBase())) {
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
const T &LVal, APValue &Value,
|
||||
CheckConstantExpressionKind CCEK) {
|
||||
APValue::LValueBase Base = LVal.getLValueBase();
|
||||
const SubobjectDesignator &Designator = LVal.getLValueDesignator();
|
||||
|
||||
if (!IsGlobalLValue(Base)) {
|
||||
if (Info.getLangOpts().CPlusPlus0x) {
|
||||
const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>();
|
||||
Info.Diag(E->getExprLoc(), diag::note_constexpr_non_global, 1)
|
||||
<< E->isGLValue() << !Designator.Entries.empty()
|
||||
<< !!VD << CCEK << VD;
|
||||
if (VD)
|
||||
Info.Note(VD->getLocation(), diag::note_declared_at);
|
||||
else
|
||||
Info.Note(Base.dyn_cast<const Expr*>()->getExprLoc(),
|
||||
diag::note_constexpr_temporary_here);
|
||||
} else {
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const SubobjectDesignator &Designator = LVal.getLValueDesignator();
|
||||
// A constant expression must refer to an object or be a null pointer.
|
||||
if (Designator.Invalid ||
|
||||
(!LVal.getLValueBase() && !Designator.Entries.empty())) {
|
||||
// FIXME: This is not a constant expression.
|
||||
// FIXME: This is not a core constant expression. We should have already
|
||||
// produced a CCE diagnostic.
|
||||
Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(),
|
||||
APValue::NoLValuePath());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Does this refer one past the end of some object?
|
||||
// This is technically not an address constant expression nor a reference
|
||||
// constant expression, but we allow it for address constant expressions.
|
||||
if (E->isGLValue() && Base && Designator.OnePastTheEnd) {
|
||||
const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>();
|
||||
Info.Diag(E->getExprLoc(), diag::note_constexpr_past_end, 1)
|
||||
<< !Designator.Entries.empty() << !!VD << VD;
|
||||
if (VD)
|
||||
Info.Note(VD->getLocation(), diag::note_declared_at);
|
||||
else
|
||||
Info.Note(Base.dyn_cast<const Expr*>()->getExprLoc(),
|
||||
diag::note_constexpr_temporary_here);
|
||||
return false;
|
||||
}
|
||||
|
||||
Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(),
|
||||
Designator.Entries, Designator.OnePastTheEnd);
|
||||
return true;
|
||||
|
@ -611,12 +677,14 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, const Expr *E,
|
|||
/// constant expression, and if it is, produce the corresponding constant value.
|
||||
/// If not, report an appropriate diagnostic.
|
||||
static bool CheckConstantExpression(EvalInfo &Info, const Expr *E,
|
||||
const CCValue &CCValue, APValue &Value) {
|
||||
const CCValue &CCValue, APValue &Value,
|
||||
CheckConstantExpressionKind CCEK
|
||||
= CCEK_Constant) {
|
||||
if (!CCValue.isLValue()) {
|
||||
Value = CCValue;
|
||||
return true;
|
||||
}
|
||||
return CheckLValueConstantExpression(Info, E, CCValue, Value);
|
||||
return CheckLValueConstantExpression(Info, E, CCValue, Value, CCEK);
|
||||
}
|
||||
|
||||
const ValueDecl *GetLValueBaseDecl(const LValue &LVal) {
|
||||
|
@ -693,26 +761,41 @@ static bool EvaluateAsBooleanCondition(const Expr *E, bool &Result,
|
|||
return HandleConversionToBool(Val, Result);
|
||||
}
|
||||
|
||||
static APSInt HandleFloatToIntCast(QualType DestType, QualType SrcType,
|
||||
APFloat &Value, const ASTContext &Ctx) {
|
||||
unsigned DestWidth = Ctx.getIntWidth(DestType);
|
||||
template<typename T>
|
||||
static bool HandleOverflow(EvalInfo &Info, const Expr *E,
|
||||
const T &SrcValue, QualType DestType) {
|
||||
llvm::SmallVector<char, 32> Buffer;
|
||||
SrcValue.toString(Buffer);
|
||||
Info.Diag(E->getExprLoc(), diag::note_constexpr_overflow)
|
||||
<< StringRef(Buffer.data(), Buffer.size()) << DestType;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E,
|
||||
QualType SrcType, const APFloat &Value,
|
||||
QualType DestType, APSInt &Result) {
|
||||
unsigned DestWidth = Info.Ctx.getIntWidth(DestType);
|
||||
// Determine whether we are converting to unsigned or signed.
|
||||
bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
|
||||
|
||||
// FIXME: Warning for overflow.
|
||||
APSInt Result(DestWidth, !DestSigned);
|
||||
Result = APSInt(DestWidth, !DestSigned);
|
||||
bool ignored;
|
||||
(void)Value.convertToInteger(Result, llvm::APFloat::rmTowardZero, &ignored);
|
||||
return Result;
|
||||
if (Value.convertToInteger(Result, llvm::APFloat::rmTowardZero, &ignored)
|
||||
& APFloat::opInvalidOp)
|
||||
return HandleOverflow(Info, E, Value, DestType);
|
||||
return true;
|
||||
}
|
||||
|
||||
static APFloat HandleFloatToFloatCast(QualType DestType, QualType SrcType,
|
||||
APFloat &Value, const ASTContext &Ctx) {
|
||||
static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E,
|
||||
QualType SrcType, QualType DestType,
|
||||
APFloat &Result) {
|
||||
APFloat Value = Result;
|
||||
bool ignored;
|
||||
APFloat Result = Value;
|
||||
Result.convert(Ctx.getFloatTypeSemantics(DestType),
|
||||
APFloat::rmNearestTiesToEven, &ignored);
|
||||
return Result;
|
||||
if (Result.convert(Info.Ctx.getFloatTypeSemantics(DestType),
|
||||
APFloat::rmNearestTiesToEven, &ignored)
|
||||
& APFloat::opOverflow)
|
||||
return HandleOverflow(Info, E, Value, DestType);
|
||||
return true;
|
||||
}
|
||||
|
||||
static APSInt HandleIntToIntCast(QualType DestType, QualType SrcType,
|
||||
|
@ -726,13 +809,15 @@ static APSInt HandleIntToIntCast(QualType DestType, QualType SrcType,
|
|||
return Result;
|
||||
}
|
||||
|
||||
static APFloat HandleIntToFloatCast(QualType DestType, QualType SrcType,
|
||||
APSInt &Value, const ASTContext &Ctx) {
|
||||
|
||||
APFloat Result(Ctx.getFloatTypeSemantics(DestType), 1);
|
||||
Result.convertFromAPInt(Value, Value.isSigned(),
|
||||
APFloat::rmNearestTiesToEven);
|
||||
return Result;
|
||||
static bool HandleIntToFloatCast(EvalInfo &Info, const Expr *E,
|
||||
QualType SrcType, const APSInt &Value,
|
||||
QualType DestType, APFloat &Result) {
|
||||
Result = APFloat(Info.Ctx.getFloatTypeSemantics(DestType), 1);
|
||||
if (Result.convertFromAPInt(Value, Value.isSigned(),
|
||||
APFloat::rmNearestTiesToEven)
|
||||
& APFloat::opOverflow)
|
||||
return HandleOverflow(Info, E, Value, DestType);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool FindMostDerivedObject(EvalInfo &Info, const LValue &LVal,
|
||||
|
@ -1311,7 +1396,7 @@ enum EvalStmtResult {
|
|||
}
|
||||
|
||||
// Evaluate a statement.
|
||||
static EvalStmtResult EvaluateStmt(CCValue &Result, EvalInfo &Info,
|
||||
static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info,
|
||||
const Stmt *S) {
|
||||
switch (S->getStmtClass()) {
|
||||
default:
|
||||
|
@ -1321,10 +1406,15 @@ static EvalStmtResult EvaluateStmt(CCValue &Result, EvalInfo &Info,
|
|||
case Stmt::DeclStmtClass:
|
||||
return ESR_Succeeded;
|
||||
|
||||
case Stmt::ReturnStmtClass:
|
||||
if (Evaluate(Result, Info, cast<ReturnStmt>(S)->getRetValue()))
|
||||
return ESR_Returned;
|
||||
return ESR_Failed;
|
||||
case Stmt::ReturnStmtClass: {
|
||||
CCValue CCResult;
|
||||
const Expr *RetExpr = cast<ReturnStmt>(S)->getRetValue();
|
||||
if (!Evaluate(CCResult, Info, RetExpr) ||
|
||||
!CheckConstantExpression(Info, RetExpr, CCResult, Result,
|
||||
CCEK_ReturnValue))
|
||||
return ESR_Failed;
|
||||
return ESR_Returned;
|
||||
}
|
||||
|
||||
case Stmt::CompoundStmtClass: {
|
||||
const CompoundStmt *CS = cast<CompoundStmt>(S);
|
||||
|
@ -1339,6 +1429,27 @@ static EvalStmtResult EvaluateStmt(CCValue &Result, EvalInfo &Info,
|
|||
}
|
||||
}
|
||||
|
||||
/// CheckConstexprFunction - Check that a function can be called in a constant
|
||||
/// expression.
|
||||
static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
|
||||
const FunctionDecl *Declaration,
|
||||
const FunctionDecl *Definition) {
|
||||
// Can we evaluate this function call?
|
||||
if (Definition && Definition->isConstexpr() && !Definition->isInvalidDecl())
|
||||
return true;
|
||||
|
||||
if (Info.getLangOpts().CPlusPlus0x) {
|
||||
const FunctionDecl *DiagDecl = Definition ? Definition : Declaration;
|
||||
Info.Diag(CallLoc, diag::note_constexpr_invalid_function, 1)
|
||||
<< DiagDecl->isConstexpr() << isa<CXXConstructorDecl>(DiagDecl)
|
||||
<< DiagDecl;
|
||||
Info.Note(DiagDecl->getLocation(), diag::note_declared_at);
|
||||
} else {
|
||||
Info.Diag(CallLoc, diag::note_invalid_subexpr_in_const_expr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
typedef SmallVector<CCValue, 8> ArgVector;
|
||||
}
|
||||
|
@ -1356,12 +1467,9 @@ static bool EvaluateArgs(ArrayRef<const Expr*> Args, ArgVector &ArgValues,
|
|||
/// Evaluate a function call.
|
||||
static bool HandleFunctionCall(const Expr *CallExpr, const LValue *This,
|
||||
ArrayRef<const Expr*> Args, const Stmt *Body,
|
||||
EvalInfo &Info, CCValue &Result) {
|
||||
if (Info.atCallLimit()) {
|
||||
// FIXME: Add a specific proper diagnostic for this.
|
||||
Info.Diag(CallExpr->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
EvalInfo &Info, APValue &Result) {
|
||||
if (!Info.CheckCallLimit(CallExpr->getExprLoc()))
|
||||
return false;
|
||||
}
|
||||
|
||||
ArgVector ArgValues(Args.size());
|
||||
if (!EvaluateArgs(Args, ArgValues, Info))
|
||||
|
@ -1377,11 +1485,8 @@ static bool HandleConstructorCall(const Expr *CallExpr, const LValue &This,
|
|||
const CXXConstructorDecl *Definition,
|
||||
EvalInfo &Info,
|
||||
APValue &Result) {
|
||||
if (Info.atCallLimit()) {
|
||||
// FIXME: Add a specific diagnostic for this.
|
||||
Info.Diag(CallExpr->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
if (!Info.CheckCallLimit(CallExpr->getExprLoc()))
|
||||
return false;
|
||||
}
|
||||
|
||||
ArgVector ArgValues(Args.size());
|
||||
if (!EvaluateArgs(Args, ArgValues, Info))
|
||||
|
@ -1430,12 +1535,12 @@ static bool HandleConstructorCall(const Expr *CallExpr, const LValue &This,
|
|||
HandleLValueMember(Info, Subobject, FD, &Layout);
|
||||
if (RD->isUnion()) {
|
||||
Result = APValue(FD);
|
||||
if (!EvaluateConstantExpression(Result.getUnionValue(), Info,
|
||||
Subobject, (*I)->getInit()))
|
||||
if (!EvaluateConstantExpression(Result.getUnionValue(), Info, Subobject,
|
||||
(*I)->getInit(), CCEK_MemberInit))
|
||||
return false;
|
||||
} else if (!EvaluateConstantExpression(
|
||||
Result.getStructField(FD->getFieldIndex()),
|
||||
Info, Subobject, (*I)->getInit()))
|
||||
Info, Subobject, (*I)->getInit(), CCEK_MemberInit))
|
||||
return false;
|
||||
} else {
|
||||
// FIXME: handle indirect field initializers
|
||||
|
@ -1745,16 +1850,12 @@ public:
|
|||
} else
|
||||
return Error(E);
|
||||
|
||||
const FunctionDecl *Definition;
|
||||
const FunctionDecl *Definition = 0;
|
||||
Stmt *Body = FD->getBody(Definition);
|
||||
CCValue CCResult;
|
||||
APValue Result;
|
||||
|
||||
if (!Body || !Definition->isConstexpr() || Definition->isInvalidDecl())
|
||||
return Error(E);
|
||||
|
||||
if (!HandleFunctionCall(E, This, Args, Body, Info, CCResult) ||
|
||||
!CheckConstantExpression(Info, E, CCResult, Result))
|
||||
if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition) ||
|
||||
!HandleFunctionCall(E, This, Args, Body, Info, Result))
|
||||
return false;
|
||||
|
||||
return DerivedSuccess(CCValue(Result, CCValue::GlobalValue()), E);
|
||||
|
@ -1878,6 +1979,10 @@ public:
|
|||
if (!EvaluatePointer(E->getBase(), Result, this->Info))
|
||||
return false;
|
||||
BaseTy = E->getBase()->getType()->getAs<PointerType>()->getPointeeType();
|
||||
} else if (E->getBase()->isRValue()) {
|
||||
if (!EvaluateTemporary(E->getBase(), Result, this->Info))
|
||||
return false;
|
||||
BaseTy = E->getBase()->getType();
|
||||
} else {
|
||||
if (!this->Visit(E->getBase()))
|
||||
return false;
|
||||
|
@ -2516,8 +2621,8 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
|
|||
const FunctionDecl *Definition = 0;
|
||||
FD->getBody(Definition);
|
||||
|
||||
if (!Definition || !Definition->isConstexpr() || Definition->isInvalidDecl())
|
||||
return Error(E);
|
||||
if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition))
|
||||
return false;
|
||||
|
||||
// FIXME: Elide the copy/move construction wherever we can.
|
||||
if (E->isElidable())
|
||||
|
@ -2837,8 +2942,8 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
|
|||
const FunctionDecl *Definition = 0;
|
||||
FD->getBody(Definition);
|
||||
|
||||
if (!Definition || !Definition->isConstexpr() || Definition->isInvalidDecl())
|
||||
return Error(E);
|
||||
if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition))
|
||||
return false;
|
||||
|
||||
// FIXME: The Subobject here isn't necessarily right. This rarely matters,
|
||||
// but sometimes does:
|
||||
|
@ -3881,7 +3986,10 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
|
|||
if (!EvaluateFloat(SubExpr, F, Info))
|
||||
return false;
|
||||
|
||||
return Success(HandleFloatToIntCast(DestType, SrcType, F, Info.Ctx), E);
|
||||
APSInt Value;
|
||||
if (!HandleFloatToIntCast(Info, E, SrcType, F, DestType, Value))
|
||||
return false;
|
||||
return Success(Value, E);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4130,19 +4238,16 @@ bool FloatExprEvaluator::VisitCastExpr(const CastExpr *E) {
|
|||
|
||||
case CK_IntegralToFloating: {
|
||||
APSInt IntResult;
|
||||
if (!EvaluateInteger(SubExpr, IntResult, Info))
|
||||
return false;
|
||||
Result = HandleIntToFloatCast(E->getType(), SubExpr->getType(),
|
||||
IntResult, Info.Ctx);
|
||||
return true;
|
||||
return EvaluateInteger(SubExpr, IntResult, Info) &&
|
||||
HandleIntToFloatCast(Info, E, SubExpr->getType(), IntResult,
|
||||
E->getType(), Result);
|
||||
}
|
||||
|
||||
case CK_FloatingCast: {
|
||||
if (!Visit(SubExpr))
|
||||
return false;
|
||||
Result = HandleFloatToFloatCast(E->getType(), SubExpr->getType(),
|
||||
Result, Info.Ctx);
|
||||
return true;
|
||||
return HandleFloatToFloatCast(Info, E, SubExpr->getType(), E->getType(),
|
||||
Result);
|
||||
}
|
||||
|
||||
case CK_FloatingComplexToReal: {
|
||||
|
@ -4289,11 +4394,8 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
|
|||
QualType From
|
||||
= E->getSubExpr()->getType()->getAs<ComplexType>()->getElementType();
|
||||
|
||||
Result.FloatReal
|
||||
= HandleFloatToFloatCast(To, From, Result.FloatReal, Info.Ctx);
|
||||
Result.FloatImag
|
||||
= HandleFloatToFloatCast(To, From, Result.FloatImag, Info.Ctx);
|
||||
return true;
|
||||
return HandleFloatToFloatCast(Info, E, From, To, Result.FloatReal) &&
|
||||
HandleFloatToFloatCast(Info, E, From, To, Result.FloatImag);
|
||||
}
|
||||
|
||||
case CK_FloatingComplexToIntegralComplex: {
|
||||
|
@ -4304,9 +4406,10 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
|
|||
QualType From
|
||||
= E->getSubExpr()->getType()->getAs<ComplexType>()->getElementType();
|
||||
Result.makeComplexInt();
|
||||
Result.IntReal = HandleFloatToIntCast(To, From, Result.FloatReal, Info.Ctx);
|
||||
Result.IntImag = HandleFloatToIntCast(To, From, Result.FloatImag, Info.Ctx);
|
||||
return true;
|
||||
return HandleFloatToIntCast(Info, E, From, Result.FloatReal,
|
||||
To, Result.IntReal) &&
|
||||
HandleFloatToIntCast(Info, E, From, Result.FloatImag,
|
||||
To, Result.IntImag);
|
||||
}
|
||||
|
||||
case CK_IntegralRealToComplex: {
|
||||
|
@ -4340,9 +4443,10 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
|
|||
QualType From
|
||||
= E->getSubExpr()->getType()->getAs<ComplexType>()->getElementType();
|
||||
Result.makeComplexFloat();
|
||||
Result.FloatReal = HandleIntToFloatCast(To, From, Result.IntReal, Info.Ctx);
|
||||
Result.FloatImag = HandleIntToFloatCast(To, From, Result.IntImag, Info.Ctx);
|
||||
return true;
|
||||
return HandleIntToFloatCast(Info, E, From, Result.IntReal,
|
||||
To, Result.FloatReal) &&
|
||||
HandleIntToFloatCast(Info, E, From, Result.IntImag,
|
||||
To, Result.FloatImag);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4581,8 +4685,16 @@ static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E) {
|
|||
return false;
|
||||
Result = Info.CurrentCall->Temporaries[E];
|
||||
} else if (E->getType()->isVoidType()) {
|
||||
if (Info.getLangOpts().CPlusPlus0x)
|
||||
Info.CCEDiag(E->getExprLoc(), diag::note_constexpr_nonliteral)
|
||||
<< E->getType();
|
||||
else
|
||||
Info.CCEDiag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
if (!EvaluateVoid(E, Info))
|
||||
return false;
|
||||
} else if (Info.getLangOpts().CPlusPlus0x) {
|
||||
Info.Diag(E->getExprLoc(), diag::note_constexpr_nonliteral) << E->getType();
|
||||
return false;
|
||||
} else {
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
return false;
|
||||
|
@ -4596,7 +4708,8 @@ static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E) {
|
|||
/// since later initializers for an object can indirectly refer to subobjects
|
||||
/// which were initialized earlier.
|
||||
static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info,
|
||||
const LValue &This, const Expr *E) {
|
||||
const LValue &This, const Expr *E,
|
||||
CheckConstantExpressionKind CCEK) {
|
||||
if (E->isRValue() && E->getType()->isLiteralType()) {
|
||||
// Evaluate arrays and record types in-place, so that later initializers can
|
||||
// refer to earlier-initialized members of the object.
|
||||
|
@ -4609,7 +4722,7 @@ static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info,
|
|||
// For any other type, in-place evaluation is unimportant.
|
||||
CCValue CoreConstResult;
|
||||
return Evaluate(CoreConstResult, Info, E) &&
|
||||
CheckConstantExpression(Info, E, CoreConstResult, Result);
|
||||
CheckConstantExpression(Info, E, CoreConstResult, Result, CCEK);
|
||||
}
|
||||
|
||||
/// EvaluateAsRValue - Try to evaluate this expression, performing an implicit
|
||||
|
@ -4681,7 +4794,8 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const {
|
|||
|
||||
LValue LV;
|
||||
return EvaluateLValue(this, LV, Info) && !Result.HasSideEffects &&
|
||||
CheckLValueConstantExpression(Info, this, LV, Result.Val);
|
||||
CheckLValueConstantExpression(Info, this, LV, Result.Val,
|
||||
CCEK_Constant);
|
||||
}
|
||||
|
||||
/// isEvaluatable - Call EvaluateAsRValue to see if this expression can be
|
||||
|
|
|
@ -1,4 +1,357 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -pedantic -verify -fcxx-exceptions %s
|
||||
|
||||
// A conditional-expression is a core constant expression unless it involves one
|
||||
// of the following as a potentially evaluated subexpression [...]:
|
||||
|
||||
// - this (5.1.1 [expr.prim.general]) [Note: when evaluating a constant
|
||||
// expression, function invocation substitution (7.1.5 [dcl.constexpr])
|
||||
// replaces each occurrence of this in a constexpr member function with a
|
||||
// pointer to the class object. -end note];
|
||||
struct This {
|
||||
int this1 : this1; // expected-error {{undeclared}}
|
||||
int this2 : this->this1; // expected-error {{invalid}}
|
||||
void this3() {
|
||||
int n1[this->this1]; // expected-warning {{variable length array}}
|
||||
int n2[this1]; // expected-warning {{variable length array}}
|
||||
(void)n1, (void)n2;
|
||||
}
|
||||
};
|
||||
|
||||
// - an invocation of a function other than a constexpr constructor for a
|
||||
// literal class or a constexpr function [ Note: Overload resolution (13.3)
|
||||
// is applied as usual - end note ];
|
||||
struct NonConstexpr1 {
|
||||
static int f() { return 1; } // expected-note {{here}}
|
||||
int n : f(); // expected-error {{constant expression}} expected-note {{non-constexpr function 'f' cannot be used in a constant expression}}
|
||||
};
|
||||
struct NonConstexpr2 {
|
||||
constexpr NonConstexpr2(); // expected-note {{here}}
|
||||
int n;
|
||||
};
|
||||
struct NonConstexpr3 {
|
||||
NonConstexpr3();
|
||||
int m : NonConstexpr2().n; // expected-error {{constant expression}} expected-note {{undefined constructor 'NonConstexpr2'}}
|
||||
};
|
||||
struct NonConstexpr4 {
|
||||
NonConstexpr4();
|
||||
int n;
|
||||
};
|
||||
struct NonConstexpr5 {
|
||||
int n : NonConstexpr4().n; // expected-error {{constant expression}} expected-note {{non-literal type 'NonConstexpr4' cannot be used in a constant expression}}
|
||||
};
|
||||
|
||||
// - an invocation of an undefined constexpr function or an undefined
|
||||
// constexpr constructor;
|
||||
struct UndefinedConstexpr {
|
||||
constexpr UndefinedConstexpr();
|
||||
static constexpr int undefinedConstexpr1(); // expected-note {{here}}
|
||||
int undefinedConstexpr2 : undefinedConstexpr1(); // expected-error {{constant expression}} expected-note {{undefined function 'undefinedConstexpr1' cannot be used in a constant expression}}
|
||||
};
|
||||
|
||||
// - an invocation of a constexpr function with arguments that, when substituted
|
||||
// by function invocation substitution (7.1.5), do not produce a constant
|
||||
// expression;
|
||||
namespace NonConstExprReturn {
|
||||
static constexpr const int &id_ref(const int &n) {
|
||||
return n; // expected-note {{reference to temporary cannot be returned from a constexpr function}}
|
||||
}
|
||||
struct NonConstExprFunction {
|
||||
int n : id_ref( // expected-error {{constant expression}}
|
||||
16 // expected-note {{temporary created here}}
|
||||
);
|
||||
};
|
||||
constexpr const int *address_of(const int &a) {
|
||||
return &a; // expected-note {{pointer to 'n' cannot be returned from a constexpr function}}
|
||||
}
|
||||
constexpr const int *return_param(int n) { // expected-note {{declared here}}
|
||||
return address_of(n);
|
||||
}
|
||||
struct S {
|
||||
int n : *return_param(0); // expected-error {{constant expression}}
|
||||
};
|
||||
}
|
||||
|
||||
// - an invocation of a constexpr constructor with arguments that, when
|
||||
// substituted by function invocation substitution (7.1.5), do not produce all
|
||||
// constant expressions for the constructor calls and full-expressions in the
|
||||
// mem-initializers (including conversions);
|
||||
namespace NonConstExprCtor {
|
||||
struct T {
|
||||
constexpr T(const int &r) :
|
||||
r(r) { // expected-note {{reference to temporary cannot be used to initialize a member in a constant expression}}
|
||||
}
|
||||
const int &r;
|
||||
};
|
||||
constexpr int n = 0;
|
||||
constexpr T t1(n); // ok
|
||||
constexpr T t2(0); // expected-error {{must be initialized by a constant expression}}
|
||||
|
||||
struct S {
|
||||
int n : T(4).r; // expected-error {{constant expression}} expected-note {{temporary created here}}
|
||||
};
|
||||
}
|
||||
|
||||
// - an invocation of a constexpr function or a constexpr constructor that would
|
||||
// exceed the implementation-defined recursion limits (see Annex B);
|
||||
namespace RecursionLimits {
|
||||
constexpr int RecurseForever(int n) {
|
||||
return n + RecurseForever(n+1); // expected-note {{constexpr evaluation exceeded maximum depth of 512 calls}}
|
||||
}
|
||||
struct AlsoRecurseForever {
|
||||
constexpr AlsoRecurseForever(int n) :
|
||||
n(AlsoRecurseForever(n+1).n) // expected-note {{constexpr evaluation exceeded maximum depth of 512 calls}}
|
||||
{}
|
||||
int n;
|
||||
};
|
||||
struct S {
|
||||
int k : RecurseForever(0); // expected-error {{constant expression}}
|
||||
int l : AlsoRecurseForever(0).n; // expected-error {{constant expression}}
|
||||
};
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
// - an operation that would have undefined behavior [Note: including, for
|
||||
// example, signed integer overflow (Clause 5 [expr]), certain pointer
|
||||
// arithmetic (5.7 [expr.add]), division by zero (5.6 [expr.mul]), or certain
|
||||
// shift operations (5.8 [expr.shift]) -end note];
|
||||
namespace UndefinedBehavior {
|
||||
void f(int n) {
|
||||
switch (n) {
|
||||
case (int)4.4e9: // expected-error {{constant expression}} expected-note {{value 4.4E+9 is outside the range of representable values of type 'int'}}
|
||||
case (int)(unsigned)(long long)4.4e9: // ok
|
||||
case (float)1e300: // expected-error {{constant expression}} expected-note {{value 1.0E+300 is outside the range of representable values of type 'float'}}
|
||||
case (int)((float)1e37 / 1e30): // ok
|
||||
case (int)(__fp16)65536: // expected-error {{constant expression}} expected-note {{value 65536 is outside the range of representable values of type 'half'}}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct S {
|
||||
int m;
|
||||
};
|
||||
constexpr S s = { 5 }; // expected-note {{declared here}}
|
||||
constexpr const int *p = &s.m + 1;
|
||||
constexpr const int &f(const int *q) {
|
||||
return q[0]; // expected-note {{dereferenced pointer past the end of subobject of 's' is not a constant expression}}
|
||||
}
|
||||
struct T {
|
||||
int n : f(p); // expected-error {{not an integer constant expression}}
|
||||
};
|
||||
}
|
||||
|
||||
// - a lambda-expression (5.1.2);
|
||||
struct Lambda {
|
||||
// FIXME: clang crashes when trying to parse this! Revisit this check once
|
||||
// lambdas are fully implemented.
|
||||
//int n : []{ return 1; }();
|
||||
};
|
||||
|
||||
// FIXME:
|
||||
// - an lvalue-to-rvalue conversion (4.1) unless it is applied to
|
||||
//
|
||||
// - a non-volatile glvalue of integral or enumeration type that refers to a
|
||||
// non-volatile const object with a preceding initialization, initialized with
|
||||
// a constant expression [Note: a string literal (2.14.5 [lex.string])
|
||||
// corresponds to an array of such objects. -end note], or
|
||||
//
|
||||
// - a non-volatile glvalue of literal type that refers to a non-volatile
|
||||
// object defined with constexpr, or that refers to a sub-object of such an
|
||||
// object, or
|
||||
//
|
||||
// - a non-volatile glvalue of literal type that refers to a non-volatile
|
||||
// temporary object whose lifetime has not ended, initialized with a constant
|
||||
// expression;
|
||||
|
||||
// FIXME:
|
||||
//
|
||||
// DR1312: The proposed wording for this defect has issues, so we instead
|
||||
// prohibit casts from pointers to cv void (see core-20842 and core-20845).
|
||||
//
|
||||
// - an lvalue-to-rvalue conversion (4.1 [conv.lval]) that is applied to a
|
||||
// glvalue of type cv1 T that refers to an object of type cv2 U, where T and U
|
||||
// are neither the same type nor similar types (4.4 [conv.qual]);
|
||||
|
||||
// FIXME:
|
||||
// - an lvalue-to-rvalue conversion (4.1) that is applied to a glvalue that
|
||||
// refers to a non-active member of a union or a subobject thereof;
|
||||
|
||||
// FIXME:
|
||||
// - an id-expression that refers to a variable or data member of reference type
|
||||
// unless the reference has a preceding initialization, initialized with a
|
||||
// constant expression;
|
||||
namespace References {
|
||||
const int a = 2;
|
||||
int &b = *const_cast<int*>(&a);
|
||||
int c = 10;
|
||||
int &d = c;
|
||||
constexpr int e = 42;
|
||||
int &f = const_cast<int&>(e);
|
||||
extern int &g;
|
||||
constexpr int &h(); // expected-note {{here}}
|
||||
int &i = h();
|
||||
constexpr int &j() { return b; }
|
||||
int &k = j();
|
||||
|
||||
struct S {
|
||||
int A : a;
|
||||
int B : b;
|
||||
int C : c; // expected-error {{constant expression}}
|
||||
int D : d; // expected-error {{constant expression}}
|
||||
int D2 : &d - &c + 1;
|
||||
int E : e / 2;
|
||||
int F : f - 11;
|
||||
int G : g; // expected-error {{constant expression}}
|
||||
int H : h(); // expected-error {{constant expression}} expected-note {{undefined function 'h'}}
|
||||
int I : i; // expected-error {{constant expression}}
|
||||
int J : j();
|
||||
int K : k;
|
||||
};
|
||||
}
|
||||
|
||||
// - a dynamic_cast (5.2.7);
|
||||
namespace DynamicCast {
|
||||
struct S { int n; };
|
||||
constexpr S s { 16 };
|
||||
struct T {
|
||||
int n : dynamic_cast<const S*>(&s)->n; // expected-warning {{constant expression}} expected-note {{dynamic_cast}}
|
||||
};
|
||||
}
|
||||
|
||||
// - a reinterpret_cast (5.2.10);
|
||||
namespace ReinterpretCast {
|
||||
struct S { int n; };
|
||||
constexpr S s { 16 };
|
||||
struct T {
|
||||
int n : reinterpret_cast<const S*>(&s)->n; // expected-warning {{constant expression}} expected-note {{reinterpret_cast}}
|
||||
};
|
||||
struct U {
|
||||
int m : (long)(S*)6; // expected-warning {{constant expression}} expected-note {{reinterpret_cast}}
|
||||
};
|
||||
}
|
||||
|
||||
// - a pseudo-destructor call (5.2.4);
|
||||
namespace PseudoDtor {
|
||||
int k;
|
||||
typedef int I;
|
||||
struct T {
|
||||
int n : (k.~I(), 0); // expected-error {{constant expression}} expected-note{{subexpression}}
|
||||
};
|
||||
}
|
||||
|
||||
// - increment or decrement operations (5.2.6, 5.3.2);
|
||||
namespace IncDec {
|
||||
int k = 2;
|
||||
struct T {
|
||||
int n : ++k; // expected-error {{constant expression}}
|
||||
int m : --k; // expected-error {{constant expression}}
|
||||
};
|
||||
}
|
||||
|
||||
// - a typeid expression (5.2.8) whose operand is of a polymorphic class type;
|
||||
namespace std {
|
||||
struct type_info {
|
||||
virtual ~type_info();
|
||||
const char *name;
|
||||
};
|
||||
}
|
||||
namespace TypeId {
|
||||
struct S { virtual void f(); };
|
||||
constexpr S *p = 0;
|
||||
constexpr const std::type_info &ti1 = typeid(*p); // expected-error {{must be initialized by a constant expression}}
|
||||
|
||||
// FIXME: Implement typeid evaluation.
|
||||
struct T {} t;
|
||||
constexpr const std::type_info &ti2 = typeid(t); // unexpected-error {{must be initialized by a constant expression}}
|
||||
}
|
||||
|
||||
// - a new-expression (5.3.4);
|
||||
// - a delete-expression (5.3.5);
|
||||
namespace NewDelete {
|
||||
int *p = 0;
|
||||
struct T {
|
||||
int n : *new int(4); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
int m : (delete p, 2); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
};
|
||||
}
|
||||
|
||||
// - a relational (5.9) or equality (5.10) operator where the result is
|
||||
// unspecified;
|
||||
namespace UnspecifiedRelations {
|
||||
int a, b;
|
||||
constexpr int *p = &a, *q = &b;
|
||||
// C++11 [expr.rel]p2: If two pointers p and q of the same type point to
|
||||
// different objects that are not members of the same array or to different
|
||||
// functions, or if only one of them is null, the results of p<q, p>q, p<=q,
|
||||
// and p>=q are unspecified.
|
||||
constexpr bool u1 = p < q; // expected-error {{constant expression}}
|
||||
constexpr bool u2 = p > q; // expected-error {{constant expression}}
|
||||
constexpr bool u3 = p <= q; // expected-error {{constant expression}}
|
||||
constexpr bool u4 = p >= q; // expected-error {{constant expression}}
|
||||
constexpr bool u5 = p < 0; // expected-error {{constant expression}}
|
||||
constexpr bool u6 = p <= 0; // expected-error {{constant expression}}
|
||||
constexpr bool u7 = p > 0; // expected-error {{constant expression}}
|
||||
constexpr bool u8 = p >= 0; // expected-error {{constant expression}}
|
||||
constexpr bool u9 = 0 < q; // expected-error {{constant expression}}
|
||||
constexpr bool u10 = 0 <= q; // expected-error {{constant expression}}
|
||||
constexpr bool u11 = 0 > q; // expected-error {{constant expression}}
|
||||
constexpr bool u12 = 0 >= q; // expected-error {{constant expression}}
|
||||
void f(), g();
|
||||
|
||||
constexpr void (*pf)() = &f, (*pg)() = &g;
|
||||
constexpr bool u13 = pf < pg; // expected-error {{constant expression}}
|
||||
constexpr bool u14 = pf == pg;
|
||||
|
||||
// FIXME:
|
||||
// If two pointers point to non-static data members of the same object with
|
||||
// different access control, the result is unspecified.
|
||||
|
||||
// FIXME:
|
||||
// [expr.rel]p3: Pointers to void can be compared [...] if both pointers
|
||||
// represent the same address or are both the null pointer [...]; otherwise
|
||||
// the result is unspecified.
|
||||
|
||||
// FIXME: Implement comparisons of pointers to members.
|
||||
// [expr.eq]p2: If either is a pointer to a virtual member function and
|
||||
// neither is null, the result is unspecified.
|
||||
}
|
||||
|
||||
// - an assignment or a compound assignment (5.17); or
|
||||
namespace Assignment {
|
||||
int k;
|
||||
struct T {
|
||||
int n : (k = 9); // expected-error {{constant expression}}
|
||||
int m : (k *= 2); // expected-error {{constant expression}}
|
||||
};
|
||||
|
||||
struct Literal {
|
||||
constexpr Literal(const char *name) : name(name) {}
|
||||
const char *name;
|
||||
};
|
||||
struct Expr {
|
||||
constexpr Expr(Literal l) : IsLiteral(true), l(l) {}
|
||||
bool IsLiteral;
|
||||
union {
|
||||
Literal l;
|
||||
// ...
|
||||
};
|
||||
};
|
||||
struct MulEq {
|
||||
constexpr MulEq(Expr a, Expr b) : LHS(a), RHS(b) {}
|
||||
Expr LHS;
|
||||
Expr RHS;
|
||||
};
|
||||
constexpr MulEq operator*=(Expr a, Expr b) { return MulEq(a, b); }
|
||||
Literal a("a");
|
||||
Literal b("b");
|
||||
MulEq c = a *= b; // ok
|
||||
}
|
||||
|
||||
// - a throw-expression (15.1)
|
||||
namespace Throw {
|
||||
struct S {
|
||||
int n : (throw "hello", 10); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
};
|
||||
}
|
||||
|
||||
// PR9999
|
||||
template<bool v>
|
||||
|
|
|
@ -10,7 +10,7 @@ static_assert(false, "test"); // expected-error {{test}}
|
|||
|
||||
// FIXME: support const T& parameters here.
|
||||
//template<typename T> constexpr T id(const T &t) { return t; }
|
||||
template<typename T> constexpr T id(T t) { return t; }
|
||||
template<typename T> constexpr T id(T t) { return t; } // expected-note {{here}}
|
||||
// FIXME: support templates here.
|
||||
//template<typename T> constexpr T min(const T &a, const T &b) {
|
||||
// return a < b ? a : b;
|
||||
|
@ -95,9 +95,9 @@ namespace CaseStatements {
|
|||
void f(int n) {
|
||||
switch (n) {
|
||||
// FIXME: Produce the 'add ()' fixit for this.
|
||||
case MemberZero().zero: // desired-error {{did you mean to call it with no arguments?}} expected-error {{not an integer constant expression}}
|
||||
case MemberZero().zero: // desired-error {{did you mean to call it with no arguments?}} expected-error {{not an integer constant expression}} expected-note {{non-literal type '<bound member function type>'}}
|
||||
// FIXME: This should be accepted once we implement the new ICE rules.
|
||||
case id(1): // expected-error {{not an integer constant expression}}
|
||||
case id(1): // expected-error {{not an integer constant expression}} expected-note {{undefined function}}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
enum E {};
|
||||
|
||||
struct Z {};
|
||||
struct Z {}; // expected-note {{here}}
|
||||
typedef int Integer;
|
||||
|
||||
struct X {
|
||||
|
@ -14,5 +14,5 @@ struct X {
|
|||
|
||||
struct Y {
|
||||
enum E : int(2);
|
||||
enum E : Z(); // expected-error{{not an integer constant}}
|
||||
enum E : Z(); // expected-error{{not an integer constant}} expected-note {{non-constexpr constructor 'Z'}}
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче