@@ -717,11 +717,16 @@ class Resolver(
717717     * Constructs a type for the addr. 
718718     * 
719719     * There are three options here: 
720+      * 
720721     * * it successfully constructs a type suitable with defaultType and returns it; 
722+      * 
721723     * * it constructs a type that cannot be assigned in a variable with the [defaultType] and we `touched` 
722-      * the [addr] during the analysis. In such case the method returns [defaultType] as a result 
723-      * if the constructed type is not an array, and in case of array it returns the [defaultType] if it has the same 
724-      * dimensions as the constructed type and its ancestors includes the constructed type, or null otherwise; 
724+      * the [addr] during the analysis. In such case we have only two options: either this object was aliased with 
725+      * an object of another type (by solver's decision) and we didn't touch it in fact, or it happened because 
726+      * of missed connection between an array type and types of its elements. For example, we might case 
727+      * array to a succ type after we `touched` it's element. In the first scenario null will be returns, in the 
728+      * second one -- a default type (that will be a subtype of actualType). 
729+      * 
725730     * * it constructs a type that cannot be assigned in a variable with the [defaultType] and we did **not** `touched` 
726731     * the [addr] during the analysis. It means we can create [UtNullModel] to represent such element. In such case 
727732     * the method returns null as the result. 
@@ -816,19 +821,35 @@ class Resolver(
816821        //  as const or store model.
817822        if  (defaultBaseType is  PrimType ) return  null 
818823
824+         //  There is no way you have java.lang.Object as a defaultType here, since it'd mean that
825+         //  some actualType is not an inheritor of it
819826        require(! defaultType.isJavaLangObject()) {
820827            " Object type $defaultType  is unexpected in fallback to default type" 
821828        }
822829
823-         if  (defaultType.numDimensions ==  0 ) {
824-             return  defaultType
830+         //  It might happen only if we have a wrong aliasing here: some object has not been touched
831+         //  during the execution, and solver returned for him an address of already existed object
832+         //  with another type. In such case `UtNullModel` should be constructed.
833+         if  (defaultBaseType.isJavaLangObject() &&  actualType.numDimensions <  defaultType.numDimensions) {
834+             return  null 
835+         }
836+ 
837+         //  All cases with `java.lang.Object` as default base type should have been already processed
838+         require(! defaultBaseType.isJavaLangObject()) {
839+             " Unexpected `java.lang.Object` as a default base type" 
825840        }
826841
827842        val  actualBaseType =  actualType.baseType
828843
829844        require(actualBaseType is  RefType ) { " Expected RefType, but $actualBaseType  found" 
830845        require(defaultBaseType is  RefType ) { " Expected RefType, but $defaultBaseType  found" 
831846
847+ 
848+         //  The same idea about fake aliasing. It might happen only if there have been an aliasing
849+         //  because of the solver's decision. In fact an object for which we construct type has not been
850+         //  touched during analysis.
851+         if  (actualType.numDimensions !=  defaultType.numDimensions) return  null 
852+ 
832853        val  ancestors =  typeResolver.findOrConstructAncestorsIncludingTypes(defaultBaseType)
833854
834855        //  This is intended to fix a specific problem. We have code:
@@ -840,7 +861,27 @@ class Resolver(
840861        //  when the array is ColoredPoint[], but the first element of it got type Point from the solver.
841862        //  In such case here we'll have ColoredPoint as defaultType and Point as actualType. It is obvious from the example
842863        //  that we can construct ColoredPoint instance instead of it with randomly filled colored-specific fields.
843-         return  defaultType.takeIf  { actualBaseType in  ancestors &&  actualType.numDimensions ==  defaultType.numDimensions }
864+         //  Note that it won't solve a problem when this `array[0]` has already been constructed somewhere above,
865+         //  since a model for it is already presented in cache and will be taken by addr from it.
866+         //  TODO corresponding issue https://github.com/UnitTestBot/UTBotJava/issues/1232
867+         if  (actualBaseType in  ancestors) return  defaultType
868+ 
869+         val  inheritors =  typeResolver.findOrConstructInheritorsIncludingTypes(defaultBaseType)
870+ 
871+         //  If we have an actual type that is not a subclass of defaultBaseType and isTouched is true,
872+         //  it means that we have encountered unexpected aliasing between an object for which we resolve its type
873+         //  and some object in the system. The reason is `isTouched` means that we processed this object
874+         //  during the analysis, therefore we create correct type constraints for it. Since we have
875+         //  inappropriate actualType here, these constraints were supposed to define type for another object,
876+         //  and, in fact, we didn't touch our object.
877+         //  For example, we have an array of two elements: Integer[] = new Integer[2], and this instance: ThisClass.
878+         //  During the execution we `touched` only the first element of the array, and we know its type.
879+         //  During resolving we found that second element of the array has set in true `isTouched` field,
880+         //  and its actualType is `ThisClass`. So, we have wrong aliasing here and can return `null` to construct
881+         //  UtNullModel instead.
882+         if  (actualBaseType !in  inheritors) return  null 
883+ 
884+         return  null 
844885    }
845886
846887    /* *
0 commit comments