diff options
Diffstat (limited to 'clang/lib/AST/ExprConstant.cpp')
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 864 |
1 files changed, 636 insertions, 228 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index c4b27b5d1daa2..d20c2382b6ac1 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -54,6 +54,7 @@ #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallBitVector.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" #include <cstring> @@ -674,6 +675,7 @@ namespace { None, Bases, AfterBases, + AfterFields, Destroying, DestroyingBases }; @@ -821,6 +823,9 @@ namespace { void finishedConstructingBases() { EI.ObjectsUnderConstruction[Object] = ConstructionPhase::AfterBases; } + void finishedConstructingFields() { + EI.ObjectsUnderConstruction[Object] = ConstructionPhase::AfterFields; + } ~EvaluatingConstructorRAII() { if (DidInsert) EI.ObjectsUnderConstruction.erase(Object); } @@ -1417,6 +1422,31 @@ static bool isFormalAccess(AccessKinds AK) { return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy; } +/// Is this kind of axcess valid on an indeterminate object value? +static bool isValidIndeterminateAccess(AccessKinds AK) { + switch (AK) { + case AK_Read: + case AK_Increment: + case AK_Decrement: + // These need the object's value. + return false; + + case AK_ReadObjectRepresentation: + case AK_Assign: + case AK_Construct: + case AK_Destroy: + // Construction and destruction don't need the value. + return true; + + case AK_MemberCall: + case AK_DynamicCast: + case AK_TypeId: + // These aren't really meaningful on scalars. + return true; + } + llvm_unreachable("unknown access kind"); +} + namespace { struct ComplexValue { private: @@ -1865,7 +1895,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) { if (const VarDecl *VD = dyn_cast<VarDecl>(D)) return VD->hasGlobalStorage(); // ... the address of a function, - return isa<FunctionDecl>(D); + // ... the address of a GUID [MS extension], + return isa<FunctionDecl>(D) || isa<MSGuidDecl>(D); } if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>()) @@ -1888,7 +1919,6 @@ static bool IsGlobalLValue(APValue::LValueBase B) { case Expr::PredefinedExprClass: case Expr::ObjCStringLiteralClass: case Expr::ObjCEncodeExprClass: - case Expr::CXXUuidofExprClass: return true; case Expr::ObjCBoxedExprClass: return cast<ObjCBoxedExpr>(E)->isExpressibleAsConstantInitializer(); @@ -2005,6 +2035,17 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, APValue::LValueBase Base = LVal.getLValueBase(); const SubobjectDesignator &Designator = LVal.getLValueDesignator(); + if (auto *VD = LVal.getLValueBase().dyn_cast<const ValueDecl *>()) { + if (auto *FD = dyn_cast<FunctionDecl>(VD)) { + if (FD->isConsteval()) { + Info.FFDiag(Loc, diag::note_consteval_address_accessible) + << !Type->isAnyPointerType(); + Info.Note(FD->getLocation(), diag::note_declared_at); + return false; + } + } + } + // Check that the object is a global. Note that the fake 'this' object we // manufacture when checking potential constant expressions is conservatively // assumed to be global here. @@ -2114,6 +2155,11 @@ static bool CheckMemberPointerConstantExpression(EvalInfo &Info, const auto *FD = dyn_cast_or_null<CXXMethodDecl>(Member); if (!FD) return true; + if (FD->isConsteval()) { + Info.FFDiag(Loc, diag::note_consteval_address_accessible) << /*pointer*/ 0; + Info.Note(FD->getLocation(), diag::note_declared_at); + return false; + } return Usage == Expr::EvaluateForMangling || FD->isVirtual() || !FD->hasAttr<DLLImportAttr>(); } @@ -2533,7 +2579,7 @@ static bool handleIntIntBinOp(EvalInfo &Info, const Expr *E, const APSInt &LHS, if (SA != RHS) { Info.CCEDiag(E, diag::note_constexpr_large_shift) << RHS << E->getType() << LHS.getBitWidth(); - } else if (LHS.isSigned() && !Info.getLangOpts().CPlusPlus2a) { + } else if (LHS.isSigned() && !Info.getLangOpts().CPlusPlus20) { // C++11 [expr.shift]p2: A signed left shift must have a non-negative // operand, and must not overflow the corresponding unsigned type. // C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to @@ -2618,6 +2664,155 @@ static bool handleFloatFloatBinOp(EvalInfo &Info, const Expr *E, return true; } +static bool handleLogicalOpForVector(const APInt &LHSValue, + BinaryOperatorKind Opcode, + const APInt &RHSValue, APInt &Result) { + bool LHS = (LHSValue != 0); + bool RHS = (RHSValue != 0); + + if (Opcode == BO_LAnd) + Result = LHS && RHS; + else + Result = LHS || RHS; + return true; +} +static bool handleLogicalOpForVector(const APFloat &LHSValue, + BinaryOperatorKind Opcode, + const APFloat &RHSValue, APInt &Result) { + bool LHS = !LHSValue.isZero(); + bool RHS = !RHSValue.isZero(); + + if (Opcode == BO_LAnd) + Result = LHS && RHS; + else + Result = LHS || RHS; + return true; +} + +static bool handleLogicalOpForVector(const APValue &LHSValue, + BinaryOperatorKind Opcode, + const APValue &RHSValue, APInt &Result) { + // The result is always an int type, however operands match the first. + if (LHSValue.getKind() == APValue::Int) + return handleLogicalOpForVector(LHSValue.getInt(), Opcode, + RHSValue.getInt(), Result); + assert(LHSValue.getKind() == APValue::Float && "Should be no other options"); + return handleLogicalOpForVector(LHSValue.getFloat(), Opcode, + RHSValue.getFloat(), Result); +} + +template <typename APTy> +static bool +handleCompareOpForVectorHelper(const APTy &LHSValue, BinaryOperatorKind Opcode, + const APTy &RHSValue, APInt &Result) { + switch (Opcode) { + default: + llvm_unreachable("unsupported binary operator"); + case BO_EQ: + Result = (LHSValue == RHSValue); + break; + case BO_NE: + Result = (LHSValue != RHSValue); + break; + case BO_LT: + Result = (LHSValue < RHSValue); + break; + case BO_GT: + Result = (LHSValue > RHSValue); + break; + case BO_LE: + Result = (LHSValue <= RHSValue); + break; + case BO_GE: + Result = (LHSValue >= RHSValue); + break; + } + + return true; +} + +static bool handleCompareOpForVector(const APValue &LHSValue, + BinaryOperatorKind Opcode, + const APValue &RHSValue, APInt &Result) { + // The result is always an int type, however operands match the first. + if (LHSValue.getKind() == APValue::Int) + return handleCompareOpForVectorHelper(LHSValue.getInt(), Opcode, + RHSValue.getInt(), Result); + assert(LHSValue.getKind() == APValue::Float && "Should be no other options"); + return handleCompareOpForVectorHelper(LHSValue.getFloat(), Opcode, + RHSValue.getFloat(), Result); +} + +// Perform binary operations for vector types, in place on the LHS. +static bool handleVectorVectorBinOp(EvalInfo &Info, const Expr *E, + BinaryOperatorKind Opcode, + APValue &LHSValue, + const APValue &RHSValue) { + assert(Opcode != BO_PtrMemD && Opcode != BO_PtrMemI && + "Operation not supported on vector types"); + + const auto *VT = E->getType()->castAs<VectorType>(); + unsigned NumElements = VT->getNumElements(); + QualType EltTy = VT->getElementType(); + + // In the cases (typically C as I've observed) where we aren't evaluating + // constexpr but are checking for cases where the LHS isn't yet evaluatable, + // just give up. + if (!LHSValue.isVector()) { + assert(LHSValue.isLValue() && + "A vector result that isn't a vector OR uncalculated LValue"); + Info.FFDiag(E); + return false; + } + + assert(LHSValue.getVectorLength() == NumElements && + RHSValue.getVectorLength() == NumElements && "Different vector sizes"); + + SmallVector<APValue, 4> ResultElements; + + for (unsigned EltNum = 0; EltNum < NumElements; ++EltNum) { + APValue LHSElt = LHSValue.getVectorElt(EltNum); + APValue RHSElt = RHSValue.getVectorElt(EltNum); + + if (EltTy->isIntegerType()) { + APSInt EltResult{Info.Ctx.getIntWidth(EltTy), + EltTy->isUnsignedIntegerType()}; + bool Success = true; + + if (BinaryOperator::isLogicalOp(Opcode)) + Success = handleLogicalOpForVector(LHSElt, Opcode, RHSElt, EltResult); + else if (BinaryOperator::isComparisonOp(Opcode)) + Success = handleCompareOpForVector(LHSElt, Opcode, RHSElt, EltResult); + else + Success = handleIntIntBinOp(Info, E, LHSElt.getInt(), Opcode, + RHSElt.getInt(), EltResult); + + if (!Success) { + Info.FFDiag(E); + return false; + } + ResultElements.emplace_back(EltResult); + + } else if (EltTy->isFloatingType()) { + assert(LHSElt.getKind() == APValue::Float && + RHSElt.getKind() == APValue::Float && + "Mismatched LHS/RHS/Result Type"); + APFloat LHSFloat = LHSElt.getFloat(); + + if (!handleFloatFloatBinOp(Info, E, LHSFloat, Opcode, + RHSElt.getFloat())) { + Info.FFDiag(E); + return false; + } + + ResultElements.emplace_back(LHSFloat); + } + } + + LHSValue = APValue(ResultElements.data(), ResultElements.size()); + return true; +} + /// Cast an lvalue referring to a base subobject to a derived class, by /// truncating the lvalue's path to the given length. static bool CastToDerivedClass(EvalInfo &Info, const Expr *E, LValue &Result, @@ -2830,7 +3025,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, if (Info.checkingPotentialConstantExpression()) return false; if (!Frame || !Frame->Arguments) { - Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); + Info.FFDiag(E, diag::note_constexpr_function_param_value_unknown) << VD; return false; } Result = &Frame->Arguments[PVD->getFunctionScopeIndex()]; @@ -2861,12 +3056,34 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, } // Dig out the initializer, and use the declaration which it's attached to. + // FIXME: We should eventually check whether the variable has a reachable + // initializing declaration. const Expr *Init = VD->getAnyInitializer(VD); - if (!Init || Init->isValueDependent()) { - // If we're checking a potential constant expression, the variable could be - // initialized later. - if (!Info.checkingPotentialConstantExpression()) - Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); + if (!Init) { + // Don't diagnose during potential constant expression checking; an + // initializer might be added later. + if (!Info.checkingPotentialConstantExpression()) { + Info.FFDiag(E, diag::note_constexpr_var_init_unknown, 1) + << VD; + Info.Note(VD->getLocation(), diag::note_declared_at); + } + return false; + } + + if (Init->isValueDependent()) { + // The DeclRefExpr is not value-dependent, but the variable it refers to + // has a value-dependent initializer. This should only happen in + // constant-folding cases, where the variable is not actually of a suitable + // type for use in a constant expression (otherwise the DeclRefExpr would + // have been value-dependent too), so diagnose that. + assert(!VD->mightBeUsableInConstantExpressions(Info.Ctx)); + if (!Info.checkingPotentialConstantExpression()) { + Info.FFDiag(E, Info.getLangOpts().CPlusPlus11 + ? diag::note_constexpr_ltor_non_constexpr + : diag::note_constexpr_ltor_non_integral, 1) + << VD << VD->getType(); + Info.Note(VD->getLocation(), diag::note_declared_at); + } return false; } @@ -2877,13 +3094,6 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, return true; } - // Never evaluate the initializer of a weak variable. We can't be sure that - // this is the definition which will be used. - if (VD->isWeak()) { - Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr); - return false; - } - // Check that we can fold the initializer. In C++, we will have already done // this in the cases where it matters for conformance. SmallVector<PartialDiagnosticAt, 8> Notes; @@ -2893,13 +3103,24 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, Info.Note(VD->getLocation(), diag::note_declared_at); Info.addNotes(Notes); return false; - } else if (!VD->checkInitIsICE()) { + } + + // Check that the variable is actually usable in constant expressions. + if (!VD->checkInitIsICE()) { Info.CCEDiag(E, diag::note_constexpr_var_init_non_constant, Notes.size() + 1) << VD; Info.Note(VD->getLocation(), diag::note_declared_at); Info.addNotes(Notes); } + // Never use the initializer of a weak variable, not even for constant + // folding. We can't be sure that this is the definition that will be used. + if (VD->isWeak()) { + Info.FFDiag(E, diag::note_constexpr_var_init_weak) << VD; + Info.Note(VD->getLocation(), diag::note_declared_at); + return false; + } + Result = VD->getEvaluatedValue(); return true; } @@ -3006,15 +3227,22 @@ static void expandArray(APValue &Array, unsigned Index) { /// is trivial. Note that this is never true for a union type with fields /// (because the copy always "reads" the active member) and always true for /// a non-class type. +static bool isReadByLvalueToRvalueConversion(const CXXRecordDecl *RD); static bool isReadByLvalueToRvalueConversion(QualType T) { CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); - if (!RD || (RD->isUnion() && !RD->field_empty())) - return true; + return !RD || isReadByLvalueToRvalueConversion(RD); +} +static bool isReadByLvalueToRvalueConversion(const CXXRecordDecl *RD) { + // FIXME: A trivial copy of a union copies the object representation, even if + // the union is empty. + if (RD->isUnion()) + return !RD->field_empty(); if (RD->isEmpty()) return false; for (auto *Field : RD->fields()) - if (isReadByLvalueToRvalueConversion(Field->getType())) + if (!Field->isUnnamedBitfield() && + isReadByLvalueToRvalueConversion(Field->getType())) return true; for (auto &BaseSpec : RD->bases()) @@ -3124,6 +3352,13 @@ struct CompleteObject { : Base(Base), Value(Value), Type(Type) {} bool mayAccessMutableMembers(EvalInfo &Info, AccessKinds AK) const { + // If this isn't a "real" access (eg, if it's just accessing the type + // info), allow it. We assume the type doesn't change dynamically for + // subobjects of constexpr objects (even though we'd hit UB here if it + // did). FIXME: Is this right? + if (!isAnyAccess(AK)) + return true; + // In C++14 onwards, it is permitted to read a mutable member whose // lifetime began within the evaluation. // FIXME: Should we also allow this in C++11? @@ -3178,9 +3413,8 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj, for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) { // Reading an indeterminate value is undefined, but assigning over one is OK. if ((O->isAbsent() && !(handler.AccessKind == AK_Construct && I == N)) || - (O->isIndeterminate() && handler.AccessKind != AK_Construct && - handler.AccessKind != AK_Assign && - handler.AccessKind != AK_ReadObjectRepresentation)) { + (O->isIndeterminate() && + !isValidIndeterminateAccess(handler.AccessKind))) { if (!Info.checkingPotentialConstantExpression()) Info.FFDiag(E, diag::note_constexpr_access_uninit) << handler.AccessKind << O->isIndeterminate(); @@ -3548,7 +3782,30 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, APValue *BaseVal = nullptr; QualType BaseType = getType(LVal.Base); - if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl*>()) { + if (const ConstantExpr *CE = + dyn_cast_or_null<ConstantExpr>(LVal.Base.dyn_cast<const Expr *>())) { + /// Nested immediate invocation have been previously removed so if we found + /// a ConstantExpr it can only be the EvaluatingDecl. + assert(CE->isImmediateInvocation() && CE == Info.EvaluatingDecl); + (void)CE; + BaseVal = Info.EvaluatingDeclValue; + } else if (const ValueDecl *D = LVal.Base.dyn_cast<const ValueDecl *>()) { + // Allow reading from a GUID declaration. + if (auto *GD = dyn_cast<MSGuidDecl>(D)) { + if (isModification(AK)) { + // All the remaining cases do not permit modification of the object. + Info.FFDiag(E, diag::note_constexpr_modify_global); + return CompleteObject(); + } + APValue &V = GD->getAsAPValue(); + if (V.isAbsent()) { + Info.FFDiag(E, diag::note_constexpr_unsupported_layout) + << GD->getType(); + return CompleteObject(); + } + return CompleteObject(LVal.Base, &V, GD->getType()); + } + // In C++98, const, non-volatile integers initialized with ICEs are ICEs. // In C++11, constexpr, non-volatile variables initialized with constant // expressions are constant expressions too. Inside constexpr functions, @@ -3566,6 +3823,11 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, return CompleteObject(); } + // In OpenCL if a variable is in constant address space it is a const value. + bool IsConstant = BaseType.isConstQualified() || + (Info.getLangOpts().OpenCL && + BaseType.getAddressSpace() == LangAS::opencl_constant); + // Unless we're looking at a local variable or argument in a constexpr call, // the variable we're reading must be const. if (!Frame) { @@ -3583,9 +3845,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, } else if (BaseType->isIntegralOrEnumerationType()) { // In OpenCL if a variable is in constant address space it is a const // value. - if (!(BaseType.isConstQualified() || - (Info.getLangOpts().OpenCL && - BaseType.getAddressSpace() == LangAS::opencl_constant))) { + if (!IsConstant) { if (!IsAccess) return CompleteObject(LVal.getLValueBase(), nullptr, BaseType); if (Info.getLangOpts().CPlusPlus) { @@ -3598,27 +3858,29 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, } } else if (!IsAccess) { return CompleteObject(LVal.getLValueBase(), nullptr, BaseType); - } else if (BaseType->isFloatingType() && BaseType.isConstQualified()) { - // We support folding of const floating-point types, in order to make - // static const data members of such types (supported as an extension) - // more useful. - if (Info.getLangOpts().CPlusPlus11) { - Info.CCEDiag(E, diag::note_constexpr_ltor_non_constexpr, 1) << VD; + } else if (IsConstant && Info.checkingPotentialConstantExpression() && + BaseType->isLiteralType(Info.Ctx) && !VD->hasDefinition()) { + // This variable might end up being constexpr. Don't diagnose it yet. + } else if (IsConstant) { + // Keep evaluating to see what we can do. In particular, we support + // folding of const floating-point types, in order to make static const + // data members of such types (supported as an extension) more useful. + if (Info.getLangOpts().CPlusPlus) { + Info.CCEDiag(E, Info.getLangOpts().CPlusPlus11 + ? diag::note_constexpr_ltor_non_constexpr + : diag::note_constexpr_ltor_non_integral, 1) + << VD << BaseType; Info.Note(VD->getLocation(), diag::note_declared_at); } else { Info.CCEDiag(E); } - } else if (BaseType.isConstQualified() && VD->hasDefinition(Info.Ctx)) { - Info.CCEDiag(E, diag::note_constexpr_ltor_non_constexpr) << VD; - // Keep evaluating to see what we can do. } else { - // FIXME: Allow folding of values of any literal type in all languages. - if (Info.checkingPotentialConstantExpression() && - VD->getType().isConstQualified() && !VD->hasDefinition(Info.Ctx)) { - // The definition of this variable could be constexpr. We can't - // access it right now, but may be able to in future. - } else if (Info.getLangOpts().CPlusPlus11) { - Info.FFDiag(E, diag::note_constexpr_ltor_non_constexpr, 1) << VD; + // Never allow reading a non-const value. + if (Info.getLangOpts().CPlusPlus) { + Info.FFDiag(E, Info.getLangOpts().CPlusPlus11 + ? diag::note_constexpr_ltor_non_constexpr + : diag::note_constexpr_ltor_non_integral, 1) + << VD << BaseType; Info.Note(VD->getLocation(), diag::note_declared_at); } else { Info.FFDiag(E); @@ -3828,12 +4090,26 @@ struct CompoundAssignSubobjectHandler { return false; case APValue::LValue: return foundPointer(Subobj, SubobjType); + case APValue::Vector: + return foundVector(Subobj, SubobjType); default: // FIXME: can this happen? Info.FFDiag(E); return false; } } + + bool foundVector(APValue &Value, QualType SubobjType) { + if (!checkConst(SubobjType)) + return false; + + if (!SubobjType->isVectorType()) { + Info.FFDiag(E); + return false; + } + return handleVectorVectorBinOp(Info, E, Opcode, Value, RHS); + } + bool found(APSInt &Value, QualType SubobjType) { if (!checkConst(SubobjType)) return false; @@ -4230,37 +4506,48 @@ static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E, } /// Get the value to use for a default-initialized object of type T. -static APValue getDefaultInitValue(QualType T) { +/// Return false if it encounters something invalid. +static bool getDefaultInitValue(QualType T, APValue &Result) { + bool Success = true; if (auto *RD = T->getAsCXXRecordDecl()) { - if (RD->isUnion()) - return APValue((const FieldDecl*)nullptr); - - APValue Struct(APValue::UninitStruct(), RD->getNumBases(), - std::distance(RD->field_begin(), RD->field_end())); + if (RD->isInvalidDecl()) { + Result = APValue(); + return false; + } + if (RD->isUnion()) { + Result = APValue((const FieldDecl *)nullptr); + return true; + } + Result = APValue(APValue::UninitStruct(), RD->getNumBases(), + std::distance(RD->field_begin(), RD->field_end())); unsigned Index = 0; for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), - End = RD->bases_end(); I != End; ++I, ++Index) - Struct.getStructBase(Index) = getDefaultInitValue(I->getType()); + End = RD->bases_end(); + I != End; ++I, ++Index) + Success &= getDefaultInitValue(I->getType(), Result.getStructBase(Index)); for (const auto *I : RD->fields()) { if (I->isUnnamedBitfield()) continue; - Struct.getStructField(I->getFieldIndex()) = - getDefaultInitValue(I->getType()); + Success &= getDefaultInitValue(I->getType(), + Result.getStructField(I->getFieldIndex())); } - return Struct; + return Success; } if (auto *AT = dyn_cast_or_null<ConstantArrayType>(T->getAsArrayTypeUnsafe())) { - APValue Array(APValue::UninitArray(), 0, AT->getSize().getZExtValue()); - if (Array.hasArrayFiller()) - Array.getArrayFiller() = getDefaultInitValue(AT->getElementType()); - return Array; + Result = APValue(APValue::UninitArray(), 0, AT->getSize().getZExtValue()); + if (Result.hasArrayFiller()) + Success &= + getDefaultInitValue(AT->getElementType(), Result.getArrayFiller()); + + return Success; } - return APValue::IndeterminateValue(); + Result = APValue::IndeterminateValue(); + return true; } namespace { @@ -4290,10 +4577,8 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) { Info.CurrentCall->createTemporary(VD, VD->getType(), true, Result); const Expr *InitE = VD->getInit(); - if (!InitE) { - Val = getDefaultInitValue(VD->getType()); - return true; - } + if (!InitE) + return getDefaultInitValue(VD->getType(), Val); if (InitE->isValueDependent()) return false; @@ -4901,7 +5186,7 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc, // DR1872: An instantiated virtual constexpr function can't be called in a // constant expression (prior to C++20). We can still constant-fold such a // call. - if (!Info.Ctx.getLangOpts().CPlusPlus2a && isa<CXXMethodDecl>(Declaration) && + if (!Info.Ctx.getLangOpts().CPlusPlus20 && isa<CXXMethodDecl>(Declaration) && cast<CXXMethodDecl>(Declaration)->isVirtual()) Info.CCEDiag(CallLoc, diag::note_constexpr_virtual_call); @@ -4910,6 +5195,13 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc, return false; } + if (const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>(Definition)) { + for (const auto *InitExpr : CtorDecl->inits()) { + if (InitExpr->getInit() && InitExpr->getInit()->containsErrors()) + return false; + } + } + // Can we evaluate this function call? if (Definition && Definition->isConstexpr() && Body) return true; @@ -5060,6 +5352,7 @@ static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, const Expr *E, case ConstructionPhase::None: case ConstructionPhase::AfterBases: + case ConstructionPhase::AfterFields: case ConstructionPhase::Destroying: // We've finished constructing the base classes and not yet started // destroying them again, so this is the dynamic type. @@ -5278,12 +5571,15 @@ static bool HandleDynamicCast(EvalInfo &Info, const ExplicitCastExpr *E, namespace { struct StartLifetimeOfUnionMemberHandler { + EvalInfo &Info; + const Expr *LHSExpr; const FieldDecl *Field; - + bool DuringInit; + bool Failed = false; static const AccessKinds AccessKind = AK_Assign; typedef bool result_type; - bool failed() { return false; } + bool failed() { return Failed; } bool found(APValue &Subobj, QualType SubobjType) { // We are supposed to perform no initialization but begin the lifetime of // the object. We interpret that as meaning to do what default @@ -5294,9 +5590,22 @@ struct StartLifetimeOfUnionMemberHandler { // * No variant members' lifetimes begin // * All scalar subobjects whose lifetimes begin have indeterminate values assert(SubobjType->isUnionType()); - if (!declaresSameEntity(Subobj.getUnionField(), Field) || - !Subobj.getUnionValue().hasValue()) - Subobj.setUnion(Field, getDefaultInitValue(Field->getType())); + if (declaresSameEntity(Subobj.getUnionField(), Field)) { + // This union member is already active. If it's also in-lifetime, there's + // nothing to do. + if (Subobj.getUnionValue().hasValue()) + return true; + } else if (DuringInit) { + // We're currently in the process of initializing a different union + // member. If we carried on, that initialization would attempt to + // store to an inactive union member, resulting in undefined behavior. + Info.FFDiag(LHSExpr, + diag::note_constexpr_union_member_change_during_init); + return false; + } + APValue Result; + Failed = !getDefaultInitValue(Field->getType(), Result); + Subobj.setUnion(Field, Result); return true; } bool found(APSInt &Value, QualType SubobjType) { @@ -5399,7 +5708,10 @@ static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *LHSExpr, SubobjectDesignator D = LHS.Designator; D.truncate(Info.Ctx, LHS.Base, LengthAndField.first); - StartLifetimeOfUnionMemberHandler StartLifetime{LengthAndField.second}; + bool DuringInit = Info.isEvaluatingCtorDtor(LHS.Base, D.Entries) == + ConstructionPhase::AfterBases; + StartLifetimeOfUnionMemberHandler StartLifetime{ + Info, LHSExpr, LengthAndField.second, DuringInit}; if (!findSubobject(Info, LHSExpr, Obj, D, StartLifetime)) return false; } @@ -5407,22 +5719,6 @@ static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *LHSExpr, return true; } -/// Determine if a class has any fields that might need to be copied by a -/// trivial copy or move operation. -static bool hasFields(const CXXRecordDecl *RD) { - if (!RD || RD->isEmpty()) - return false; - for (auto *FD : RD->fields()) { - if (FD->isUnnamedBitfield()) - continue; - return true; - } - for (auto &Base : RD->bases()) - if (hasFields(Base.getType()->getAsCXXRecordDecl())) - return true; - return false; -} - namespace { typedef SmallVector<APValue, 8> ArgVector; } @@ -5447,6 +5743,8 @@ static bool EvaluateArgs(ArrayRef<const Expr *> Args, ArgVector &ArgValues, } } } + // FIXME: This is the wrong evaluation order for an assignment operator + // called via operator syntax. for (unsigned Idx = 0; Idx < Args.size(); Idx++) { if (!Evaluate(ArgValues[Idx], Info, Args[Idx])) { // If we're checking for a potential constant expression, evaluate all @@ -5491,7 +5789,8 @@ static bool HandleFunctionCall(SourceLocation CallLoc, const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Callee); if (MD && MD->isDefaulted() && (MD->getParent()->isUnion() || - (MD->isTrivial() && hasFields(MD->getParent())))) { + (MD->isTrivial() && + isReadByLvalueToRvalueConversion(MD->getParent())))) { assert(This && (MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator())); LValue RHS; @@ -5500,7 +5799,7 @@ static bool HandleFunctionCall(SourceLocation CallLoc, if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(), RHS, RHSValue, MD->getParent()->isUnion())) return false; - if (Info.getLangOpts().CPlusPlus2a && MD->isTrivial() && + if (Info.getLangOpts().CPlusPlus20 && MD->isTrivial() && !HandleUnionActiveMemberChange(Info, Args[0], *This)) return false; if (!handleAssignment(Info, Args[0], *This, MD->getThisType(), @@ -5578,7 +5877,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, // actually read them. if (Definition->isDefaulted() && Definition->isCopyOrMoveConstructor() && (Definition->getParent()->isUnion() || - (Definition->isTrivial() && hasFields(Definition->getParent())))) { + (Definition->isTrivial() && + isReadByLvalueToRvalueConversion(Definition->getParent())))) { LValue RHS; RHS.setFrom(Info.Ctx, ArgValues[0]); return handleLValueToRValueConversion( @@ -5587,9 +5887,14 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, } // Reserve space for the struct members. - if (!RD->isUnion() && !Result.hasValue()) - Result = APValue(APValue::UninitStruct(), RD->getNumBases(), - std::distance(RD->field_begin(), RD->field_end())); + if (!Result.hasValue()) { + if (!RD->isUnion()) + Result = APValue(APValue::UninitStruct(), RD->getNumBases(), + std::distance(RD->field_begin(), RD->field_end())); + else + // A union starts with no active member. + Result = APValue((const FieldDecl*)nullptr); + } if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); @@ -5616,8 +5921,9 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, for (; !declaresSameEntity(*FieldIt, FD); ++FieldIt) { assert(FieldIt != RD->field_end() && "missing field?"); if (!FieldIt->isUnnamedBitfield()) - Result.getStructField(FieldIt->getFieldIndex()) = - getDefaultInitValue(FieldIt->getType()); + Success &= getDefaultInitValue( + FieldIt->getType(), + Result.getStructField(FieldIt->getFieldIndex())); } ++FieldIt; }; @@ -5669,10 +5975,10 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, if (CD->isUnion()) *Value = APValue(FD); else - // FIXME: This immediately starts the lifetime of all members of an - // anonymous struct. It would be preferable to strictly start member - // lifetime in initialization order. - *Value = getDefaultInitValue(Info.Ctx.getRecordType(CD)); + // FIXME: This immediately starts the lifetime of all members of + // an anonymous struct. It would be preferable to strictly start + // member lifetime in initialization order. + Success &= getDefaultInitValue(Info.Ctx.getRecordType(CD), *Value); } // Store Subobject as its parent before updating it for the last element // in the chain. @@ -5719,11 +6025,14 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, if (!RD->isUnion()) { for (; FieldIt != RD->field_end(); ++FieldIt) { if (!FieldIt->isUnnamedBitfield()) - Result.getStructField(FieldIt->getFieldIndex()) = - getDefaultInitValue(FieldIt->getType()); + Success &= getDefaultInitValue( + FieldIt->getType(), + Result.getStructField(FieldIt->getFieldIndex())); } } + EvalObj.finishedConstructingFields(); + return Success && EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed && LifetimeExtendedScope.destroy(); @@ -5964,7 +6273,7 @@ static bool HandleOperatorNewCall(EvalInfo &Info, const CallExpr *E, // This is permitted only within a call to std::allocator<T>::allocate. auto Caller = Info.getStdAllocatorCaller("allocate"); if (!Caller) { - Info.FFDiag(E->getExprLoc(), Info.getLangOpts().CPlusPlus2a + Info.FFDiag(E->getExprLoc(), Info.getLangOpts().CPlusPlus20 ? diag::note_constexpr_new_untyped : diag::note_constexpr_new); return false; @@ -6697,8 +7006,13 @@ public: return Error(E); } - bool VisitConstantExpr(const ConstantExpr *E) - { return StmtVisitorTy::Visit(E->getSubExpr()); } + bool VisitConstantExpr(const ConstantExpr *E) { + if (E->hasAPValueResult()) + return DerivedSuccess(E->getAPValueResult(), E); + + return StmtVisitorTy::Visit(E->getSubExpr()); + } + bool VisitParenExpr(const ParenExpr *E) { return StmtVisitorTy::Visit(E->getSubExpr()); } bool VisitUnaryExtension(const UnaryOperator *E) @@ -6741,7 +7055,7 @@ public: return static_cast<Derived*>(this)->VisitCastExpr(E); } bool VisitCXXDynamicCastExpr(const CXXDynamicCastExpr *E) { - if (!Info.Ctx.getLangOpts().CPlusPlus2a) + if (!Info.Ctx.getLangOpts().CPlusPlus20) CCEDiag(E, diag::note_constexpr_invalid_cast) << 1; return static_cast<Derived*>(this)->VisitCastExpr(E); } @@ -6900,12 +7214,10 @@ public: return Error(Callee); This = &ThisVal; } else if (const auto *PDE = dyn_cast<CXXPseudoDestructorExpr>(Callee)) { - if (!Info.getLangOpts().CPlusPlus2a) + if (!Info.getLangOpts().CPlusPlus20) Info.CCEDiag(PDE, diag::note_constexpr_pseudo_destructor); - // FIXME: If pseudo-destructor calls ever start ending the lifetime of - // their callee, we should start calling HandleDestruction here. - // For now, we just evaluate the object argument and discard it. - return EvaluateObjectArgument(Info, PDE->getBase(), ThisVal); + return EvaluateObjectArgument(Info, PDE->getBase(), ThisVal) && + HandleDestruction(Info, PDE, ThisVal, PDE->getDestroyedType()); } else return Error(Callee); FD = Member; @@ -7369,6 +7681,8 @@ public: // from the AST (FIXME). // * A MaterializeTemporaryExpr that has static storage duration, with no // CallIndex, for a lifetime-extended temporary. +// * The ConstantExpr that is currently being evaluated during evaluation of an +// immediate invocation. // plus an offset in bytes. //===----------------------------------------------------------------------===// namespace { @@ -7448,6 +7762,8 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { return VisitVarDecl(E, VD); if (const BindingDecl *BD = dyn_cast<BindingDecl>(E->getDecl())) return Visit(BD->getBinding()); + if (const MSGuidDecl *GD = dyn_cast<MSGuidDecl>(E->getDecl())) + return Success(GD); return Error(E); } @@ -7604,7 +7920,7 @@ bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) { else TypeInfo = TypeInfoLValue(E->getExprOperand()->getType().getTypePtr()); } else { - if (!Info.Ctx.getLangOpts().CPlusPlus2a) { + if (!Info.Ctx.getLangOpts().CPlusPlus20) { Info.CCEDiag(E, diag::note_constexpr_typeid_polymorphic) << E->getExprOperand()->getType() << E->getExprOperand()->getSourceRange(); @@ -7626,7 +7942,7 @@ bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) { } bool LValueExprEvaluator::VisitCXXUuidofExpr(const CXXUuidofExpr *E) { - return Success(E); + return Success(E->getGuidDecl()); } bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) { @@ -7740,7 +8056,7 @@ bool LValueExprEvaluator::VisitBinAssign(const BinaryOperator *E) { if (!Evaluate(NewVal, this->Info, E->getRHS())) return false; - if (Info.getLangOpts().CPlusPlus2a && + if (Info.getLangOpts().CPlusPlus20 && !HandleUnionActiveMemberChange(Info, E->getLHS(), Result)) return false; @@ -8235,6 +8551,12 @@ bool PointerExprEvaluator::VisitCallExpr(const CallExpr *E) { return visitNonBuiltinCallExpr(E); } +// Determine if T is a character type for which we guarantee that +// sizeof(T) == 1. +static bool isOneByteCharacterType(QualType T) { + return T->isCharType() || T->isChar8Type(); +} + bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinOp) { switch (BuiltinOp) { @@ -8385,8 +8707,12 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, } // Give up on byte-oriented matching against multibyte elements. // FIXME: We can compare the bytes in the correct order. - if (IsRawByte && Info.Ctx.getTypeSizeInChars(CharTy) != CharUnits::One()) + if (IsRawByte && !isOneByteCharacterType(CharTy)) { + Info.FFDiag(E, diag::note_constexpr_memchr_unsupported) + << (std::string("'") + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'") + << CharTy; return false; + } // Figure out what value we're actually looking for (after converting to // the corresponding unsigned type if necessary). uint64_t DesiredVal; @@ -8502,6 +8828,7 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, QualType T = Dest.Designator.getType(Info.Ctx); QualType SrcT = Src.Designator.getType(Info.Ctx); if (!Info.Ctx.hasSameUnqualifiedType(T, SrcT)) { + // FIXME: Consider using our bit_cast implementation to support this. Info.FFDiag(E, diag::note_constexpr_memcpy_type_pun) << Move << SrcT << T; return false; } @@ -8593,9 +8920,13 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, APValue &Result, const InitListExpr *ILE, QualType AllocType); +static bool EvaluateArrayNewConstructExpr(EvalInfo &Info, LValue &This, + APValue &Result, + const CXXConstructExpr *CCE, + QualType AllocType); bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { - if (!Info.getLangOpts().CPlusPlus2a) + if (!Info.getLangOpts().CPlusPlus20) Info.CCEDiag(E, diag::note_constexpr_new); // We cannot speculatively evaluate a delete expression. @@ -8642,6 +8973,7 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { const Expr *Init = E->getInitializer(); const InitListExpr *ResizedArrayILE = nullptr; + const CXXConstructExpr *ResizedArrayCCE = nullptr; QualType AllocType = E->getAllocatedType(); if (Optional<const Expr*> ArraySize = E->getArraySize()) { @@ -8685,7 +9017,7 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { // -- the new-initializer is a braced-init-list and the number of // array elements for which initializers are provided [...] // exceeds the number of elements to initialize - if (Init) { + if (Init && !isa<CXXConstructExpr>(Init)) { auto *CAT = Info.Ctx.getAsConstantArrayType(Init->getType()); assert(CAT && "unexpected type for array initializer"); @@ -8708,6 +9040,8 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { // special handling for this case when we initialize. if (InitBound != AllocBound) ResizedArrayILE = cast<InitListExpr>(Init); + } else if (Init) { + ResizedArrayCCE = cast<CXXConstructExpr>(Init); } AllocType = Info.Ctx.getConstantArrayType(AllocType, ArrayBound, nullptr, @@ -8772,11 +9106,15 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { if (!EvaluateArrayNewInitList(Info, Result, *Val, ResizedArrayILE, AllocType)) return false; + } else if (ResizedArrayCCE) { + if (!EvaluateArrayNewConstructExpr(Info, Result, *Val, ResizedArrayCCE, + AllocType)) + return false; } else if (Init) { if (!EvaluateInPlace(*Val, Info, Result, Init)) return false; - } else { - *Val = getDefaultInitValue(AllocType); + } else if (!getDefaultInitValue(AllocType, *Val)) { + return false; } // Array new returns a pointer to the first element, not a pointer to the @@ -9126,6 +9464,8 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { } } + EvalObj.finishedConstructingFields(); + return Success; } @@ -9145,8 +9485,7 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E, if (ZeroInit) return ZeroInitialization(E, T); - Result = getDefaultInitValue(T); - return true; + return getDefaultInitValue(T, Result); } const FunctionDecl *Definition = nullptr; @@ -9204,24 +9543,30 @@ bool RecordExprEvaluator::VisitCXXStdInitializerListExpr( // Get a pointer to the first element of the array. Array.addArray(Info, E, ArrayType); + auto InvalidType = [&] { + Info.FFDiag(E, diag::note_constexpr_unsupported_layout) + << E->getType(); + return false; + }; + // FIXME: Perform the checks on the field types in SemaInit. RecordDecl *Record = E->getType()->castAs<RecordType>()->getDecl(); RecordDecl::field_iterator Field = Record->field_begin(); if (Field == Record->field_end()) - return Error(E); + return InvalidType(); // Start pointer. if (!Field->getType()->isPointerType() || !Info.Ctx.hasSameType(Field->getType()->getPointeeType(), ArrayType->getElementType())) - return Error(E); + return InvalidType(); // FIXME: What if the initializer_list type has base classes, etc? Result = APValue(APValue::UninitStruct(), 0, 2); Array.moveInto(Result.getStructField(0)); if (++Field == Record->field_end()) - return Error(E); + return InvalidType(); if (Field->getType()->isPointerType() && Info.Ctx.hasSameType(Field->getType()->getPointeeType(), @@ -9236,10 +9581,10 @@ bool RecordExprEvaluator::VisitCXXStdInitializerListExpr( // Length. Result.getStructField(1) = APValue(APSInt(ArrayType->getSize())); else - return Error(E); + return InvalidType(); if (++Field != Record->field_end()) - return Error(E); + return InvalidType(); return true; } @@ -9376,10 +9721,9 @@ namespace { bool VisitCastExpr(const CastExpr* E); bool VisitInitListExpr(const InitListExpr *E); bool VisitUnaryImag(const UnaryOperator *E); - // FIXME: Missing: unary -, unary ~, binary add/sub/mul/div, - // binary comparisons, binary and/or/xor, - // conditional operator (for GNU conditional select), - // shufflevector, ExtVectorElementExpr + bool VisitBinaryOperator(const BinaryOperator *E); + // FIXME: Missing: unary -, unary ~, conditional operator (for GNU + // conditional select), shufflevector, ExtVectorElementExpr }; } // end anonymous namespace @@ -9527,6 +9871,41 @@ bool VectorExprEvaluator::VisitUnaryImag(const UnaryOperator *E) { return ZeroInitialization(E); } +bool VectorExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { + BinaryOperatorKind Op = E->getOpcode(); + assert(Op != BO_PtrMemD && Op != BO_PtrMemI && Op != BO_Cmp && + "Operation not supported on vector types"); + + if (Op == BO_Comma) + return ExprEvaluatorBaseTy::VisitBinaryOperator(E); + + Expr *LHS = E->getLHS(); + Expr *RHS = E->getRHS(); + + assert(LHS->getType()->isVectorType() && RHS->getType()->isVectorType() && + "Must both be vector types"); + // Checking JUST the types are the same would be fine, except shifts don't + // need to have their types be the same (since you always shift by an int). + assert(LHS->getType()->getAs<VectorType>()->getNumElements() == + E->getType()->getAs<VectorType>()->getNumElements() && + RHS->getType()->getAs<VectorType>()->getNumElements() == + E->getType()->getAs<VectorType>()->getNumElements() && + "All operands must be the same size."); + + APValue LHSValue; + APValue RHSValue; + bool LHSOK = Evaluate(LHSValue, Info, LHS); + if (!LHSOK && !Info.noteFailure()) + return false; + if (!Evaluate(RHSValue, Info, RHS) || !LHSOK) + return false; + + if (!handleVectorVectorBinOp(Info, E, Op, LHSValue, RHSValue)) + return false; + + return Success(LHSValue, E); +} + //===----------------------------------------------------------------------===// // Array Evaluation //===----------------------------------------------------------------------===// @@ -9550,8 +9929,18 @@ namespace { bool ZeroInitialization(const Expr *E) { const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(E->getType()); - if (!CAT) + if (!CAT) { + if (const IncompleteArrayType *IAT = + Info.Ctx.getAsIncompleteArrayType(E->getType())) { + // We can be asked to zero-initialize a flexible array member; this + // is represented as an ImplicitValueInitExpr of incomplete array + // type. In this case, the array has zero elements. + Result = APValue(APValue::UninitArray(), 0, 0); + return true; + } + // FIXME: We could handle VLAs here. return Error(E); + } Result = APValue(APValue::UninitArray(), 0, CAT->getSize().getZExtValue()); @@ -9597,9 +9986,19 @@ static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, .VisitInitListExpr(ILE, AllocType); } +static bool EvaluateArrayNewConstructExpr(EvalInfo &Info, LValue &This, + APValue &Result, + const CXXConstructExpr *CCE, + QualType AllocType) { + assert(CCE->isRValue() && CCE->getType()->isArrayType() && + "not an array rvalue"); + return ArrayExprEvaluator(Info, This, Result) + .VisitCXXConstructExpr(CCE, This, &Result, AllocType); +} + // Return true iff the given array filler may depend on the element index. static bool MaybeElementDependentArrayFiller(const Expr *FillerExpr) { - // For now, just whitelist non-class value-initialization and initialization + // For now, just allow non-class value-initialization and initialization // lists comprised of them. if (isa<ImplicitValueInitExpr>(FillerExpr)) return false; @@ -9836,8 +10235,6 @@ public: // Visitor Methods //===--------------------------------------------------------------------===// - bool VisitConstantExpr(const ConstantExpr *E); - bool VisitIntegerLiteral(const IntegerLiteral *E) { return Success(E->getValue(), E); } @@ -9912,6 +10309,7 @@ public: bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); bool VisitSourceLocExpr(const SourceLocExpr *E); bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E); + bool VisitRequiresExpr(const RequiresExpr *E); // FIXME: Missing: array subscript of vector, member of vector }; @@ -10199,10 +10597,12 @@ EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) { case Type::BlockPointer: case Type::Vector: case Type::ExtVector: + case Type::ConstantMatrix: case Type::ObjCObject: case Type::ObjCInterface: case Type::ObjCObjectPointer: case Type::Pipe: + case Type::ExtInt: // GCC classifies vectors as None. We follow its lead and classify all // other types that don't fit into the regular classification the same way. return GCCTypeClass::None; @@ -10286,7 +10686,7 @@ static bool EvaluateBuiltinConstantP(EvalInfo &Info, const Expr *Arg) { ArgType->isAnyComplexType() || ArgType->isPointerType() || ArgType->isNullPtrType()) { APValue V; - if (!::EvaluateAsRValue(Info, Arg, V)) { + if (!::EvaluateAsRValue(Info, Arg, V) || Info.EvalStatus.HasSideEffects) { Fold.keepDiagnostics(); return false; } @@ -10464,9 +10864,9 @@ static bool isUserWritingOffTheEnd(const ASTContext &Ctx, const LValue &LVal) { // the array at the end was flexible, or if it had 0 or 1 elements. This // broke some common standard library extensions (PR30346), but was // otherwise seemingly fine. It may be useful to reintroduce this behavior - // with some sort of whitelist. OTOH, it seems that GCC is always + // with some sort of list. OTOH, it seems that GCC is always // conservative with the last element in structs (if it's an array), so our - // current behavior is more compatible than a whitelisting approach would + // current behavior is more compatible than an explicit list approach would // be. return LVal.InvalidBase && Designator.Entries.size() == Designator.MostDerivedPathLength && @@ -10616,13 +11016,6 @@ static bool tryEvaluateBuiltinObjectSize(const Expr *E, unsigned Type, return true; } -bool IntExprEvaluator::VisitConstantExpr(const ConstantExpr *E) { - llvm::SaveAndRestore<bool> InConstantContext(Info.InConstantContext, true); - if (E->getResultAPValueKind() != APValue::None) - return Success(E->getAPValueResult(), E); - return ExprEvaluatorBaseTy::VisitConstantExpr(E); -} - bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { if (unsigned BuiltinOp = E->getBuiltinCallee()) return VisitBuiltinCallExpr(E, BuiltinOp); @@ -10659,7 +11052,7 @@ static bool getBuiltinAlignArguments(const CallExpr *E, EvalInfo &Info, bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinOp) { - switch (unsigned BuiltinOp = E->getBuiltinCallee()) { + switch (BuiltinOp) { default: return ExprEvaluatorBaseTy::VisitCallExpr(E); @@ -10848,6 +11241,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, } case Builtin::BI__builtin_expect: + case Builtin::BI__builtin_expect_with_probability: return Visit(E->getArg(0)); case Builtin::BI__builtin_ffs: @@ -11041,6 +11435,17 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, CharTy1, E->getArg(0)->getType()->getPointeeType()) && Info.Ctx.hasSameUnqualifiedType(CharTy1, CharTy2))); + // For memcmp, allow comparing any arrays of '[[un]signed] char' or + // 'char8_t', but no other types. + if (IsRawByte && + !(isOneByteCharacterType(CharTy1) && isOneByteCharacterType(CharTy2))) { + // FIXME: Consider using our bit_cast implementation to support this. + Info.FFDiag(E, diag::note_constexpr_memcmp_unsupported) + << (std::string("'") + Info.Ctx.BuiltinInfo.getName(BuiltinOp) + "'") + << CharTy1 << CharTy2; + return false; + } + const auto &ReadCurElems = [&](APValue &Char1, APValue &Char2) { return handleLValueToRValueConversion(Info, E, CharTy1, String1, Char1) && handleLValueToRValueConversion(Info, E, CharTy2, String2, Char2) && @@ -11051,57 +11456,6 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, HandleLValueArrayAdjustment(Info, E, String2, CharTy2, 1); }; - if (IsRawByte) { - uint64_t BytesRemaining = MaxLength; - // Pointers to const void may point to objects of incomplete type. - if (CharTy1->isIncompleteType()) { - Info.FFDiag(E, diag::note_constexpr_ltor_incomplete_type) << CharTy1; - return false; - } - if (CharTy2->isIncompleteType()) { - Info.FFDiag(E, diag::note_constexpr_ltor_incomplete_type) << CharTy2; - return false; - } - uint64_t CharTy1Width{Info.Ctx.getTypeSize(CharTy1)}; - CharUnits CharTy1Size = Info.Ctx.toCharUnitsFromBits(CharTy1Width); - // Give up on comparing between elements with disparate widths. - if (CharTy1Size != Info.Ctx.getTypeSizeInChars(CharTy2)) - return false; - uint64_t BytesPerElement = CharTy1Size.getQuantity(); - assert(BytesRemaining && "BytesRemaining should not be zero: the " - "following loop considers at least one element"); - while (true) { - APValue Char1, Char2; - if (!ReadCurElems(Char1, Char2)) - return false; - // We have compatible in-memory widths, but a possible type and - // (for `bool`) internal representation mismatch. - // Assuming two's complement representation, including 0 for `false` and - // 1 for `true`, we can check an appropriate number of elements for - // equality even if they are not byte-sized. - APSInt Char1InMem = Char1.getInt().extOrTrunc(CharTy1Width); - APSInt Char2InMem = Char2.getInt().extOrTrunc(CharTy1Width); - if (Char1InMem.ne(Char2InMem)) { - // If the elements are byte-sized, then we can produce a three-way - // comparison result in a straightforward manner. - if (BytesPerElement == 1u) { - // memcmp always compares unsigned chars. - return Success(Char1InMem.ult(Char2InMem) ? -1 : 1, E); - } - // The result is byte-order sensitive, and we have multibyte elements. - // FIXME: We can compare the remaining bytes in the correct order. - return false; - } - if (!AdvanceElems()) - return false; - if (BytesRemaining <= BytesPerElement) - break; - BytesRemaining -= BytesPerElement; - } - // Enough elements are equal to account for the memcmp limit. - return Success(0, E); - } - bool StopAtNull = (BuiltinOp != Builtin::BImemcmp && BuiltinOp != Builtin::BIbcmp && BuiltinOp != Builtin::BIwmemcmp && @@ -11119,7 +11473,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, APValue Char1, Char2; if (!ReadCurElems(Char1, Char2)) return false; - if (Char1.getInt() != Char2.getInt()) { + if (Char1.getInt().ne(Char2.getInt())) { if (IsWide) // wmemcmp compares with wchar_t signedness. return Success(Char1.getInt() < Char2.getInt() ? -1 : 1, E); // memcmp always compares unsigned chars. @@ -12524,6 +12878,9 @@ bool IntExprEvaluator::VisitConceptSpecializationExpr( return Success(E->isSatisfied(), E); } +bool IntExprEvaluator::VisitRequiresExpr(const RequiresExpr *E) { + return Success(E->isSatisfied(), E); +} bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { switch (E->getOpcode()) { @@ -12566,8 +12923,14 @@ bool FixedPointExprEvaluator::VisitCastExpr(const CastExpr *E) { return false; bool Overflowed; APFixedPoint Result = Src.convert(DestFXSema, &Overflowed); - if (Overflowed && !HandleOverflow(Info, E, Result, DestType)) - return false; + if (Overflowed) { + if (Info.checkingForUndefinedBehavior()) + Info.Ctx.getDiagnostics().Report(E->getExprLoc(), + diag::warn_fixedpoint_constant_overflow) + << Result.toString() << E->getType(); + else if (!HandleOverflow(Info, E, Result, E->getType())) + return false; + } return Success(Result, E); } case CK_IntegralToFixedPoint: { @@ -12579,8 +12942,14 @@ bool FixedPointExprEvaluator::VisitCastExpr(const CastExpr *E) { APFixedPoint IntResult = APFixedPoint::getFromIntValue( Src, Info.Ctx.getFixedPointSemantics(DestType), &Overflowed); - if (Overflowed && !HandleOverflow(Info, E, IntResult, DestType)) - return false; + if (Overflowed) { + if (Info.checkingForUndefinedBehavior()) + Info.Ctx.getDiagnostics().Report(E->getExprLoc(), + diag::warn_fixedpoint_constant_overflow) + << IntResult.toString() << E->getType(); + else if (!HandleOverflow(Info, E, IntResult, E->getType())) + return false; + } return Success(IntResult, E); } @@ -12593,6 +12962,9 @@ bool FixedPointExprEvaluator::VisitCastExpr(const CastExpr *E) { } bool FixedPointExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { + if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma) + return ExprEvaluatorBaseTy::VisitBinaryOperator(E); + const Expr *LHS = E->getLHS(); const Expr *RHS = E->getRHS(); FixedPointSemantics ResultFXSema = @@ -12605,20 +12977,45 @@ bool FixedPointExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { if (!EvaluateFixedPointOrInteger(RHS, RHSFX, Info)) return false; + bool OpOverflow = false, ConversionOverflow = false; + APFixedPoint Result(LHSFX.getSemantics()); switch (E->getOpcode()) { case BO_Add: { - bool AddOverflow, ConversionOverflow; - APFixedPoint Result = LHSFX.add(RHSFX, &AddOverflow) - .convert(ResultFXSema, &ConversionOverflow); - if ((AddOverflow || ConversionOverflow) && - !HandleOverflow(Info, E, Result, E->getType())) + Result = LHSFX.add(RHSFX, &OpOverflow) + .convert(ResultFXSema, &ConversionOverflow); + break; + } + case BO_Sub: { + Result = LHSFX.sub(RHSFX, &OpOverflow) + .convert(ResultFXSema, &ConversionOverflow); + break; + } + case BO_Mul: { + Result = LHSFX.mul(RHSFX, &OpOverflow) + .convert(ResultFXSema, &ConversionOverflow); + break; + } + case BO_Div: { + if (RHSFX.getValue() == 0) { + Info.FFDiag(E, diag::note_expr_divide_by_zero); return false; - return Success(Result, E); + } + Result = LHSFX.div(RHSFX, &OpOverflow) + .convert(ResultFXSema, &ConversionOverflow); + break; } default: return false; } - llvm_unreachable("Should've exited before this"); + if (OpOverflow || ConversionOverflow) { + if (Info.checkingForUndefinedBehavior()) + Info.Ctx.getDiagnostics().Report(E->getExprLoc(), + diag::warn_fixedpoint_constant_overflow) + << Result.toString() << E->getType(); + else if (!HandleOverflow(Info, E, Result, E->getType())) + return false; + } + return Success(Result, E); } //===----------------------------------------------------------------------===// @@ -13470,7 +13867,7 @@ bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) { // This is the only case where we need to produce an extension warning: // the only other way we can succeed is if we find a dynamic allocation, // and we will have warned when we allocated it in that case. - if (!Info.getLangOpts().CPlusPlus2a) + if (!Info.getLangOpts().CPlusPlus20) Info.CCEDiag(E, diag::note_constexpr_new); return true; } @@ -13823,7 +14220,7 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx, } bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage, - const ASTContext &Ctx) const { + const ASTContext &Ctx, bool InPlace) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); @@ -13831,7 +14228,14 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage, EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; - if (!::Evaluate(Result.Val, Info, this) || Result.HasSideEffects) + if (InPlace) { + Info.setEvaluatingDecl(this, Result.Val); + LValue LVal; + LVal.set(this); + if (!::EvaluateInPlace(Result.Val, Info, LVal, this) || + Result.HasSideEffects) + return false; + } else if (!::Evaluate(Result.Val, Info, this) || Result.HasSideEffects) return false; if (!Info.discardCleanups()) @@ -13874,18 +14278,6 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, LValue LVal; LVal.set(VD); - // C++11 [basic.start.init]p2: - // Variables with static storage duration or thread storage duration shall - // be zero-initialized before any other initialization takes place. - // This behavior is not present in C. - if (Ctx.getLangOpts().CPlusPlus && !VD->hasLocalStorage() && - !DeclTy->isReferenceType()) { - ImplicitValueInitExpr VIE(DeclTy); - if (!EvaluateInPlace(Value, Info, LVal, &VIE, - /*AllowNonLiteralTypes=*/true)) - return false; - } - if (!EvaluateInPlace(Value, Info, LVal, this, /*AllowNonLiteralTypes=*/true) || EStatus.HasSideEffects) @@ -13904,14 +14296,17 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, bool VarDecl::evaluateDestruction( SmallVectorImpl<PartialDiagnosticAt> &Notes) const { - assert(getEvaluatedValue() && !getEvaluatedValue()->isAbsent() && - "cannot evaluate destruction of non-constant-initialized variable"); - Expr::EvalStatus EStatus; EStatus.Diag = &Notes; - // Make a copy of the value for the destructor to mutate. - APValue DestroyedValue = *getEvaluatedValue(); + // Make a copy of the value for the destructor to mutate, if we know it. + // Otherwise, treat the value as default-initialized; if the destructor works + // anyway, then the destruction is constant (and must be essentially empty). + APValue DestroyedValue; + if (getEvaluatedValue() && !getEvaluatedValue()->isAbsent()) + DestroyedValue = *getEvaluatedValue(); + else if (!getDefaultInitValue(getType(), DestroyedValue)) + return false; EvalInfo Info(getASTContext(), EStatus, EvalInfo::EM_ConstantExpression); Info.setEvaluatingDecl(this, DestroyedValue, @@ -13924,8 +14319,6 @@ bool VarDecl::evaluateDestruction( LValue LVal; LVal.set(this); - // FIXME: Consider storing whether this variable has constant destruction in - // the EvaluatedStmt so that CodeGen can query it. if (!HandleDestruction(Info, DeclLoc, LVal.Base, DestroyedValue, DeclTy) || EStatus.HasSideEffects) return false; @@ -14073,7 +14466,10 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::ImaginaryLiteralClass: case Expr::StringLiteralClass: case Expr::ArraySubscriptExprClass: + case Expr::MatrixSubscriptExprClass: case Expr::OMPArraySectionExprClass: + case Expr::OMPArrayShapingExprClass: + case Expr::OMPIteratorExprClass: case Expr::MemberExprClass: case Expr::CompoundAssignOperatorClass: case Expr::CompoundLiteralExprClass: @@ -14090,6 +14486,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::StmtExprClass: case Expr::CXXMemberCallExprClass: case Expr::CUDAKernelCallExprClass: + case Expr::CXXAddrspaceCastExprClass: case Expr::CXXDynamicCastExprClass: case Expr::CXXTypeidExprClass: case Expr::CXXUuidofExprClass: @@ -14104,6 +14501,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CXXPseudoDestructorExprClass: case Expr::UnresolvedLookupExprClass: case Expr::TypoExprClass: + case Expr::RecoveryExprClass: case Expr::DependentScopeDeclRefExprClass: case Expr::CXXConstructExprClass: case Expr::CXXInheritedCtorInitExprClass: @@ -14182,6 +14580,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CXXScalarValueInitExprClass: case Expr::TypeTraitExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::CXXNoexceptExprClass: @@ -14627,6 +15026,15 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, if (FD->isDependentContext()) return true; + // Bail out if a constexpr constructor has an initializer that contains an + // error. We deliberately don't produce a diagnostic, as we have produced a + // relevant diagnostic when parsing the error initializer. + if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FD)) { + for (const auto *InitExpr : Ctor->inits()) { + if (InitExpr->getInit() && InitExpr->getInit()->containsErrors()) + return false; + } + } Expr::EvalStatus Status; Status.Diag = &Diags; |