Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -437,6 +437,10 @@ RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B, const SubRegion *R); + Optional getConstantValFromConstArrayInitializer( + RegionBindingsConstRef B, const VarRegion *VR, const ElementRegion *R); + Optional getSValFromInitListExpr(const InitListExpr *ILE, + uint64_t Offset, QualType ElemT); public: // Part of public interface to class. @@ -1625,6 +1629,93 @@ return Result; } +Optional RegionStoreManager::getConstantValFromConstArrayInitializer( + RegionBindingsConstRef B, const VarRegion *VR, const ElementRegion *R) { + assert(R && VR && "Regions should not be null"); + + // Array should be immutable. + const VarDecl *VD = VR->getDecl(); + if (!VD->getType().isConstQualified() && + !R->getElementType().isConstQualified() && + (!B.isMainAnalysis() || !VD->hasGlobalStorage())) + return None; + + // Array's declaration should have ConstantArrayType type. + const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(VD->getType()); + if (!CAT) + return None; + + // Array should be one-dimensional. + // TODO: Support multidimensional array. + if (isa(CAT->getElementType())) // is multidimensional + return None; + + // Array's offset should be a concrete value. + // Return Unknown value if symbolic index presented. + // FIXME: We also need to take ElementRegions with symbolic + // indexes into account. + const auto OffsetVal = R->getIndex().getAs(); + if (!OffsetVal.hasValue()) + return UnknownVal(); + + // Check offset for being out of bounds. + // C++20 [expr.add] 7.6.6.4 (excerpt): + // If P points to an array element i of an array object x with n + // elements, where i < 0 or i > n, the behavior is undefined. + // Dereferencing is not allowed on the "one past the last + // element", when i == n. + // Example: + // const int arr[4] = {1, 2}; + // const int *ptr = arr; + // int x0 = ptr[0]; // 1 + // int x1 = ptr[1]; // 2 + // int x2 = ptr[2]; // 0 + // int x3 = ptr[3]; // 0 + // int x4 = ptr[4]; // UB + // int x5 = ptr[-1]; // UB + const llvm::APSInt &OffsetInt = OffsetVal->getValue(); + const auto Offset = static_cast(OffsetInt.getExtValue()); + // Use `getZExtValue` because array extent can not be negative. + const uint64_t Extent = CAT->getSize().getZExtValue(); + // Check for `OffsetInt < 0` but NOT for `Offset < 0`, because `OffsetInt` + // CAN be negative, but `Offset` can NOT, because `Offset` is an uint64_t. + if (OffsetInt < 0 || Offset >= Extent) + return UndefinedVal(); + // From here `Offset` is in the bounds. + + // Array's declaration should have an initializer. + const Expr *Init = VD->getAnyInitializer(); + if (!Init) + return None; + + // Handle InitListExpr. + if (const auto *ILE = dyn_cast(Init)) + return getSValFromInitListExpr(ILE, Offset, R->getElementType()); + + // FIXME: Handle StringLiteral. + + // FIXME: Handle CompoundLiteralExpr. + + return None; +} + +Optional +RegionStoreManager::getSValFromInitListExpr(const InitListExpr *ILE, + uint64_t Offset, QualType ElemT) { + assert(ILE && "InitListExpr should not be null"); + + // C++20 [expr.add] 9.4.17.5 (excerpt): + // i-th array element is value-initialized for each k < i ≤ n, + // where k is an expression-list size and n is an array extent. + if (Offset >= ILE->getNumInits()) + return svalBuilder.makeZeroVal(ElemT); + + // Return a constant value, if it is presented. + // FIXME: Support other SVals. + const Expr *E = ILE->getInit(Offset); + return svalBuilder.getConstantVal(E); +} + SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const ElementRegion* R) { // Check if the region has a binding. @@ -1658,64 +1749,8 @@ return svalBuilder.makeIntVal(c, T); } } else if (const VarRegion *VR = dyn_cast(superR)) { - // Check if the containing array has an initialized value that we can trust. - // We can trust a const value or a value of a global initializer in main(). - const VarDecl *VD = VR->getDecl(); - if (VD->getType().isConstQualified() || - R->getElementType().isConstQualified() || - (B.isMainAnalysis() && VD->hasGlobalStorage())) { - if (const Expr *Init = VD->getAnyInitializer()) { - if (const auto *InitList = dyn_cast(Init)) { - // The array index has to be known. - if (auto CI = R->getIndex().getAs()) { - // If it is not an array, return Undef. - QualType T = VD->getType(); - const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(T); - if (!CAT) - return UndefinedVal(); - - // Support one-dimensional array. - // C++20 [expr.add] 7.6.6.4 (excerpt): - // If P points to an array element i of an array object x with n - // elements, where i < 0 or i > n, the behavior is undefined. - // Dereferencing is not allowed on the "one past the last - // element", when i == n. - // Example: - // const int arr[4] = {1, 2}; - // const int *ptr = arr; - // int x0 = ptr[0]; // 1 - // int x1 = ptr[1]; // 2 - // int x2 = ptr[2]; // 0 - // int x3 = ptr[3]; // 0 - // int x4 = ptr[4]; // UB - // TODO: Support multidimensional array. - if (!isa(CAT->getElementType())) { - // One-dimensional array. - const llvm::APSInt &Idx = CI->getValue(); - const auto I = static_cast(Idx.getExtValue()); - // Use `getZExtValue` because array extent can not be negative. - const uint64_t Extent = CAT->getSize().getZExtValue(); - // Check for `Idx < 0`, NOT for `I < 0`, because `Idx` CAN be - // negative, but `I` can NOT. - if (Idx < 0 || I >= Extent) - return UndefinedVal(); - - // C++20 [expr.add] 9.4.17.5 (excerpt): - // i-th array element is value-initialized for each k < i ≤ n, - // where k is an expression-list size and n is an array extent. - if (I >= InitList->getNumInits()) - return svalBuilder.makeZeroVal(R->getElementType()); - - // Return a constant value, if it is presented. - // FIXME: Support other SVals. - const Expr *E = InitList->getInit(I); - if (Optional V = svalBuilder.getConstantVal(E)) - return *V; - } - } - } - } - } + if (Optional V = getConstantValFromConstArrayInitializer(B, VR, R)) + return *V; } // Check for loads from a code text region. For such loads, just give up.