diff options
Diffstat (limited to 'clang/lib/AST/ExprConstant.cpp')
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 155 |
1 files changed, 133 insertions, 22 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index eb690dcfabc..7b5b0a7d453 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1290,6 +1290,14 @@ void EvalInfo::addCallStack(unsigned Limit) { } } +/// Kinds of access we can perform on an object, for diagnostics. +enum AccessKinds { + AK_Read, + AK_Assign, + AK_Increment, + AK_Decrement +}; + namespace { struct ComplexValue { private: @@ -1395,21 +1403,36 @@ namespace { set(B, true); } + private: // Check that this LValue is not based on a null pointer. If it is, produce // a diagnostic and mark the designator as invalid. - bool checkNullPointer(EvalInfo &Info, const Expr *E, - CheckSubobjectKind CSK) { + template <typename GenDiagType> + bool checkNullPointerDiagnosingWith(const GenDiagType &GenDiag) { if (Designator.Invalid) return false; if (IsNullPtr) { - Info.CCEDiag(E, diag::note_constexpr_null_subobject) - << CSK; + GenDiag(); Designator.setInvalid(); return false; } return true; } + public: + bool checkNullPointer(EvalInfo &Info, const Expr *E, + CheckSubobjectKind CSK) { + return checkNullPointerDiagnosingWith([&Info, E, CSK] { + Info.CCEDiag(E, diag::note_constexpr_null_subobject) << CSK; + }); + } + + bool checkNullPointerForFoldAccess(EvalInfo &Info, const Expr *E, + AccessKinds AK) { + return checkNullPointerDiagnosingWith([&Info, E, AK] { + Info.FFDiag(E, diag::note_constexpr_access_null) << AK; + }); + } + // Check this LValue refers to an object. If not, set the designator to be // invalid and emit a diagnostic. bool checkSubobject(EvalInfo &Info, const Expr *E, CheckSubobjectKind CSK) { @@ -2746,14 +2769,6 @@ static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E, return false; } -/// Kinds of access we can perform on an object, for diagnostics. -enum AccessKinds { - AK_Read, - AK_Assign, - AK_Increment, - AK_Decrement -}; - namespace { /// A handle to a complete object (an object that is not a subobject of /// another object). @@ -6129,9 +6144,27 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return false; MaxLength = N.getExtValue(); } - - QualType CharTy = E->getArg(0)->getType()->getPointeeType(); - + // We cannot find the value if there are no candidates to match against. + if (MaxLength == 0u) + return ZeroInitialization(E); + if (!Result.checkNullPointerForFoldAccess(Info, E, AK_Read) || + Result.Designator.Invalid) + return false; + QualType CharTy = Result.Designator.getType(Info.Ctx); + bool IsRawByte = BuiltinOp == Builtin::BImemchr || + BuiltinOp == Builtin::BI__builtin_memchr; + assert(IsRawByte || + Info.Ctx.hasSameUnqualifiedType( + CharTy, E->getArg(0)->getType()->getPointeeType())); + // Pointers to const void may point to objects of incomplete type. + if (IsRawByte && CharTy->isIncompleteType()) { + Info.FFDiag(E, diag::note_constexpr_ltor_incomplete_type) << CharTy; + return false; + } + // 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()) + return false; // Figure out what value we're actually looking for (after converting to // the corresponding unsigned type if necessary). uint64_t DesiredVal; @@ -8385,8 +8418,6 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, !EvaluatePointer(E->getArg(1), String2, Info)) return false; - QualType CharTy = E->getArg(0)->getType()->getPointeeType(); - uint64_t MaxLength = uint64_t(-1); if (BuiltinOp != Builtin::BIstrcmp && BuiltinOp != Builtin::BIwcscmp && @@ -8397,6 +8428,88 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return false; MaxLength = N.getExtValue(); } + + // Empty substrings compare equal by definition. + if (MaxLength == 0u) + return Success(0, E); + + if (!String1.checkNullPointerForFoldAccess(Info, E, AK_Read) || + !String2.checkNullPointerForFoldAccess(Info, E, AK_Read) || + String1.Designator.Invalid || String2.Designator.Invalid) + return false; + + QualType CharTy1 = String1.Designator.getType(Info.Ctx); + QualType CharTy2 = String2.Designator.getType(Info.Ctx); + + bool IsRawByte = BuiltinOp == Builtin::BImemcmp || + BuiltinOp == Builtin::BI__builtin_memcmp; + + assert(IsRawByte || + (Info.Ctx.hasSameUnqualifiedType( + CharTy1, E->getArg(0)->getType()->getPointeeType()) && + Info.Ctx.hasSameUnqualifiedType(CharTy1, CharTy2))); + + const auto &ReadCurElems = [&](APValue &Char1, APValue &Char2) { + return handleLValueToRValueConversion(Info, E, CharTy1, String1, Char1) && + handleLValueToRValueConversion(Info, E, CharTy2, String2, Char2) && + Char1.isInt() && Char2.isInt(); + }; + const auto &AdvanceElems = [&] { + return HandleLValueArrayAdjustment(Info, E, String1, CharTy1, 1) && + 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::BIwmemcmp && BuiltinOp != Builtin::BI__builtin_memcmp && @@ -8407,11 +8520,10 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, BuiltinOp == Builtin::BI__builtin_wcscmp || BuiltinOp == Builtin::BI__builtin_wcsncmp || BuiltinOp == Builtin::BI__builtin_wmemcmp; + for (; MaxLength; --MaxLength) { APValue Char1, Char2; - if (!handleLValueToRValueConversion(Info, E, CharTy, String1, Char1) || - !handleLValueToRValueConversion(Info, E, CharTy, String2, Char2) || - !Char1.isInt() || !Char2.isInt()) + if (!ReadCurElems(Char1, Char2)) return false; if (Char1.getInt() != Char2.getInt()) { if (IsWide) // wmemcmp compares with wchar_t signedness. @@ -8422,8 +8534,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, if (StopAtNull && !Char1.getInt()) return Success(0, E); assert(!(StopAtNull && !Char2.getInt())); - if (!HandleLValueArrayAdjustment(Info, E, String1, CharTy, 1) || - !HandleLValueArrayAdjustment(Info, E, String2, CharTy, 1)) + if (!AdvanceElems()) return false; } // We hit the strncmp / memcmp limit. |