Hello everyone,
I just came across this implementation in Store.cpp, the purpose of which is not entirely clear to me.
SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
SVal Base) {
if (Offset.isZeroConstant()) {
QualType BT = Base.getType(this->Ctx);
if (!BT.isNull() && !elementType.isNull()) {
QualType PointeeTy = BT->getPointeeType();
if (!PointeeTy.isNull() &&
PointeeTy.getCanonicalType() == elementType.getCanonicalType())
return Base;
}
}
...
if (!isa<nonloc::ConcreteInt>(Offset)) {
if (isa<ElementRegion>(BaseRegion->StripCasts()))
return UnknownVal();
return loc::MemRegionVal(MRMgr.getElementRegion(
elementType, Offset, cast<SubRegion>(ElemR->getSuperRegion()), Ctx));
}
...
}
This function behaves as follows for these examples:
extern int unknown_index;
extern int matrix[3][3];
int main() {
int val1 = matrix[0][unknown_index];
int val2 = matrix[1][unknown_index];
int val3 = matrix[unknown_index][unknown_index];
return 0;
}
matrix[0][unknown_index]:
-
- dim:
getLValueElementβ LValue: SVal = &Element{matrix,0 S64b,int[3]}
- dim:
-
- dim:
getLValueElementβ LValue: SVal = &Element{Element{matrix,0 S64b,int[3]},reg_$0,int}
- dim:
- RHS of the first assignment returns a meaningful MemRegionVal, since the evaluation of the first dimension returns the Base and therefore, the last return statement shown is taken
matrix[1][unknown_index]:
-
- dim:
getLValueElementβ LValue: SVal = &Element{matrix,1 S64b,int[3]}
- dim:
-
- dim:
getLValueElementβ LValue: SVal = Unknown
- dim:
- RHS of the second assignment returns an Unknown since the BaseRegion is an ElementRegion itself. Therefore, the second last return statement in the function is taken.
matrix[unknown_index][unknown_index]:
-
- dim:
getLValueElementβ LValue: SVal = LValue: SVal = &Element{matrix,reg_$0,int[3]}
- dim:
-
- dim:
getLValueElementβ LValue: SVal = Unknown
- dim:
- This results in the same paths in
getLValueElementas in the case of the second assignment.
It is not clear to me what advantages this early return has for BaseRegions that are ElementRegions. If this were simply removed, meaningful MemRegionVals would be returned in all cases. Outlined here:
SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
SVal Base) {
...
if (!isa<nonloc::ConcreteInt>(Offset)) {
// if (isa<ElementRegion>(BaseRegion->StripCasts()))
// return UnknownVal();
return loc::MemRegionVal(MRMgr.getElementRegion(
elementType, Offset, cast<SubRegion>(ElemR->getSuperRegion()), Ctx));
}
...
}
matrix[0][unknown_index]:
β LValue: SVal = &Element{Element{matrix,0 S64b,int[3]},reg_$0,int}
matrix[1][unknown_index]:
β LValue: SVal = &Element{Element{matrix,1 S64b,int[3]},reg_$0,int}
matrix[unknown_index][unknown_index]:
β LValue: SVal = &Element{Element{matrix,reg_$0,int[3]},reg_$0,int}
This proposed modification would have the advantage that possible callbacks such as checklocation will be triggered for the determined MemRegion and evaluated there (e.g. for array bound checks). At first glance, this solution seems very useful to me.
I would appreciate any feedback.