KT-549 type inference failed
KT-580 Type inference failed KT-600 Problem with 'sure' extension function type inference KT-571 Type inference failed
This commit is contained in:
@@ -39,6 +39,8 @@ import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
|
||||
* @author abreslav
|
||||
*/
|
||||
public class CallResolver {
|
||||
private static final JetType DONT_CARE = ErrorUtils.createErrorTypeWithCustomDebugName("DONT_CARE");
|
||||
|
||||
private final JetSemanticServices semanticServices;
|
||||
private final OverloadingConflictResolver overloadingConflictResolver;
|
||||
private final DataFlowInfo dataFlowInfo;
|
||||
@@ -412,7 +414,7 @@ public class CallResolver {
|
||||
constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO
|
||||
}
|
||||
|
||||
TypeSubstitutor substituteUnknown = ConstraintSystemImpl.makeConstantSubstitutor(candidate.getTypeParameters(), ErrorUtils.createErrorType("Unknown"));
|
||||
TypeSubstitutor substituteDontCare = ConstraintSystemImpl.makeConstantSubstitutor(candidate.getTypeParameters(), DONT_CARE);
|
||||
|
||||
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : candidateCall.getValueArguments().entrySet()) {
|
||||
ResolvedValueArgument valueArgument = entry.getValue();
|
||||
@@ -428,7 +430,7 @@ public class CallResolver {
|
||||
// We'll type check the arguments later, with the inferred types expected
|
||||
TemporaryBindingTrace traceForUnknown = TemporaryBindingTrace.create(temporaryTrace);
|
||||
ExpressionTypingServices temporaryServices = new ExpressionTypingServices(semanticServices, traceForUnknown);
|
||||
JetType type = temporaryServices.getType(scope, expression, substituteUnknown.substitute(valueParameterDescriptor.getOutType(), Variance.INVARIANT));
|
||||
JetType type = temporaryServices.getType(scope, expression, substituteDontCare.substitute(valueParameterDescriptor.getOutType(), Variance.INVARIANT));
|
||||
if (type != null) {
|
||||
constraintSystem.addSubtypingConstraint(type, effectiveExpectedType);
|
||||
}
|
||||
@@ -439,10 +441,10 @@ public class CallResolver {
|
||||
}
|
||||
|
||||
// Error is already reported if something is missing
|
||||
ReceiverDescriptor receiverParameter = candidateCall.getReceiverArgument();
|
||||
ReceiverDescriptor candidateReceiver = candidate.getReceiverParameter();
|
||||
if (receiverParameter.exists() && candidateReceiver.exists()) {
|
||||
constraintSystem.addSubtypingConstraint(receiverParameter.getType(), candidateReceiver.getType());
|
||||
ReceiverDescriptor receiverArgument = candidateCall.getReceiverArgument();
|
||||
ReceiverDescriptor receiverParameter = candidate.getReceiverParameter();
|
||||
if (receiverArgument.exists() && receiverParameter.exists()) {
|
||||
constraintSystem.addSubtypingConstraint(receiverArgument.getType(), receiverParameter.getType());
|
||||
}
|
||||
|
||||
if (expectedType != NO_EXPECTED_TYPE) {
|
||||
|
||||
@@ -171,11 +171,20 @@ public class ErrorUtils {
|
||||
}
|
||||
|
||||
private static JetType createErrorType(String debugMessage, JetScope memberScope) {
|
||||
return new ErrorTypeImpl(new TypeConstructorImpl(ERROR_CLASS, Collections.<AnnotationDescriptor>emptyList(), false, "[ERROR : " + debugMessage + "]", Collections.<TypeParameterDescriptor>emptyList(), Collections.singleton(JetStandardClasses.getAnyType())), memberScope);
|
||||
return createErrorTypeWithCustomDebugName(memberScope, "[ERROR : " + debugMessage + "]");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetType createErrorTypeWithCustomDebugName(String debugName) {
|
||||
return createErrorTypeWithCustomDebugName(ERROR_SCOPE, debugName);
|
||||
}
|
||||
|
||||
private static JetType createErrorTypeWithCustomDebugName(JetScope memberScope, String debugName) {
|
||||
return new ErrorTypeImpl(new TypeConstructorImpl(ERROR_CLASS, Collections.<AnnotationDescriptor>emptyList(), false, debugName, Collections.<TypeParameterDescriptor>emptyList(), Collections.singleton(JetStandardClasses.getAnyType())), memberScope);
|
||||
}
|
||||
|
||||
public static JetType createWrongVarianceErrorType(TypeProjection value) {
|
||||
return createErrorType(value + " is not allowed here]", value.getType().getMemberScope());
|
||||
return createErrorType(value + " is not allowed here", value.getType().getMemberScope());
|
||||
}
|
||||
|
||||
public static ClassifierDescriptor getErrorClass() {
|
||||
|
||||
@@ -110,54 +110,65 @@ public class TypeUtils {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JetType intersect(JetTypeChecker typeChecker, Set<JetType> types) {
|
||||
public static JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set<JetType> types) {
|
||||
assert !types.isEmpty();
|
||||
|
||||
if (types.size() == 1) {
|
||||
return types.iterator().next();
|
||||
}
|
||||
|
||||
StringBuilder debugName = new StringBuilder();
|
||||
boolean nullable = false;
|
||||
Set<JetType> resultingTypes = Sets.newHashSet();
|
||||
// Intersection of T1..Tn is an intersection of their non-null versions,
|
||||
// made nullable is they all were nullable
|
||||
boolean allNullable = true;
|
||||
boolean nothingTypePresent = false;
|
||||
List<JetType> nullabilityStripped = Lists.newArrayList();
|
||||
for (JetType type : types) {
|
||||
nothingTypePresent |= JetStandardClasses.isNothingOrNullableNothing(type);
|
||||
allNullable &= type.isNullable();
|
||||
nullabilityStripped.add(makeNotNullable(type));
|
||||
}
|
||||
|
||||
if (nothingTypePresent) {
|
||||
return allNullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType();
|
||||
}
|
||||
|
||||
// Now we remove types that have subtypes in the list
|
||||
List<JetType> resultingTypes = Lists.newArrayList();
|
||||
outer:
|
||||
for (Iterator<JetType> iterator = types.iterator(); iterator.hasNext();) {
|
||||
JetType type = iterator.next();
|
||||
|
||||
for (JetType type : nullabilityStripped) {
|
||||
if (!canHaveSubtypes(typeChecker, type)) {
|
||||
for (JetType other : types) {
|
||||
for (JetType other : nullabilityStripped) {
|
||||
// It makes sense to check for subtyping of other <: type, despite that
|
||||
// type is not supposed to be open, for there're enums
|
||||
if (!type.equals(other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
return makeNullableAsSpecified(type, allNullable);
|
||||
}
|
||||
else {
|
||||
for (JetType other : types) {
|
||||
for (JetType other : nullabilityStripped) {
|
||||
if (!type.equals(other) && typeChecker.isSubtypeOf(other, type)) {
|
||||
continue outer;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
nullable |= type.isNullable();
|
||||
|
||||
resultingTypes.add(type);
|
||||
debugName.append(type.toString());
|
||||
if (iterator.hasNext()) {
|
||||
debugName.append(" & ");
|
||||
}
|
||||
}
|
||||
|
||||
if (resultingTypes.size() == 1) {
|
||||
return makeNullableAsSpecified(resultingTypes.get(0), allNullable);
|
||||
}
|
||||
|
||||
|
||||
List<AnnotationDescriptor> noAnnotations = Collections.<AnnotationDescriptor>emptyList();
|
||||
TypeConstructor constructor = new TypeConstructorImpl(
|
||||
null,
|
||||
noAnnotations,
|
||||
false,
|
||||
debugName.toString(),
|
||||
makeDebugNameForIntersectionType(resultingTypes).toString(),
|
||||
Collections.<TypeParameterDescriptor>emptyList(),
|
||||
resultingTypes);
|
||||
|
||||
@@ -171,11 +182,25 @@ public class TypeUtils {
|
||||
return new JetTypeImpl(
|
||||
noAnnotations,
|
||||
constructor,
|
||||
nullable,
|
||||
allNullable,
|
||||
Collections.<TypeProjection>emptyList(),
|
||||
new ChainedScope(null, scopes)); // TODO : check intersectibility, don't use a chanied scope
|
||||
}
|
||||
|
||||
private static StringBuilder makeDebugNameForIntersectionType(Iterable<JetType> resultingTypes) {
|
||||
StringBuilder debugName = new StringBuilder("{");
|
||||
for (Iterator<JetType> iterator = resultingTypes.iterator(); iterator.hasNext(); ) {
|
||||
JetType type = iterator.next();
|
||||
|
||||
debugName.append(type.toString());
|
||||
if (iterator.hasNext()) {
|
||||
debugName.append(" & ");
|
||||
}
|
||||
}
|
||||
debugName.append("}");
|
||||
return debugName;
|
||||
}
|
||||
|
||||
public static boolean canHaveSubtypes(JetTypeChecker typeChecker, JetType type) {
|
||||
if (type.isNullable()) {
|
||||
return true;
|
||||
|
||||
+40
-9
@@ -9,6 +9,7 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.types.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -46,8 +47,19 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
}
|
||||
|
||||
public static abstract class TypeValue {
|
||||
private final Set<TypeValue> upperBounds = Sets.newHashSet();
|
||||
private final Set<TypeValue> lowerBounds = Sets.newHashSet();
|
||||
private final Set<TypeValue> mutableUpperBounds = Sets.newHashSet();
|
||||
private final Set<TypeValue> upperBounds = Collections.unmodifiableSet(mutableUpperBounds);
|
||||
|
||||
private final Set<TypeValue> mutableLowerBounds = Sets.newHashSet();
|
||||
private final Set<TypeValue> lowerBounds = Collections.unmodifiableSet(mutableLowerBounds);
|
||||
|
||||
public void addUpperBound(@NotNull TypeValue bound) {
|
||||
mutableUpperBounds.add(bound);
|
||||
}
|
||||
|
||||
public void addLowerBound(@NotNull TypeValue bound) {
|
||||
mutableLowerBounds.add(bound);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<TypeValue> getUpperBounds() {
|
||||
@@ -86,8 +98,8 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
throw new LoopInTypeVariableConstraintsException();
|
||||
}
|
||||
if (value == null) {
|
||||
JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
|
||||
beingComputed = true;
|
||||
JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
|
||||
try {
|
||||
if (positionVariance == Variance.IN_VARIANCE) {
|
||||
// maximal solution
|
||||
@@ -320,8 +332,8 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
private void addSubtypingConstraintOnTypeValues(TypeValue typeValueForLower, TypeValue typeValueForUpper) {
|
||||
println(typeValueForLower + " :< " + typeValueForUpper);
|
||||
if (typeValueForLower != typeValueForUpper) {
|
||||
typeValueForLower.getUpperBounds().add(typeValueForUpper);
|
||||
typeValueForUpper.getLowerBounds().add(typeValueForLower);
|
||||
typeValueForLower.addUpperBound(typeValueForUpper);
|
||||
typeValueForUpper.addLowerBound(typeValueForLower);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,13 +370,20 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
|
||||
// effective bounds for each node
|
||||
Set<TypeValue> visited = Sets.newHashSet();
|
||||
for (KnownType knownType : knownTypes.values()) {
|
||||
transitiveClosure(knownType, visited);
|
||||
}
|
||||
for (UnknownType unknownType : unknownTypes.values()) {
|
||||
transitiveClosure(unknownType, visited);
|
||||
}
|
||||
|
||||
for (UnknownType unknownType : unknownTypes.values()) {
|
||||
println("Constraints for " + unknownType.getTypeParameterDescriptor());
|
||||
printTypeValue(unknownType);
|
||||
}
|
||||
|
||||
for (KnownType knownType : knownTypes.values()) {
|
||||
println("Constraints for " + knownType.getType());
|
||||
printTypeValue(knownType);
|
||||
}
|
||||
|
||||
// Find inconsistencies
|
||||
Solution solution = new Solution();
|
||||
|
||||
@@ -382,6 +401,15 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
return solution;
|
||||
}
|
||||
|
||||
private void printTypeValue(TypeValue typeValue) {
|
||||
for (TypeValue bound : typeValue.getUpperBounds()) {
|
||||
println(" :< " + bound);
|
||||
}
|
||||
for (TypeValue bound : typeValue.getLowerBounds()) {
|
||||
println(" :> " + bound);
|
||||
}
|
||||
}
|
||||
|
||||
private void check(TypeValue typeValue, Solution solution) {
|
||||
try {
|
||||
KnownType resultingValue = typeValue.getValue();
|
||||
@@ -416,7 +444,7 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
}
|
||||
}
|
||||
solution.registerError("[TODO] Loop in constraints");
|
||||
e.printStackTrace();
|
||||
// e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,6 +454,9 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
}
|
||||
|
||||
for (TypeValue upperBound : Sets.newHashSet(current.getUpperBounds())) {
|
||||
if (upperBound instanceof KnownType) {
|
||||
continue;
|
||||
}
|
||||
transitiveClosure(upperBound, visited);
|
||||
Set<TypeValue> upperBounds = upperBound.getUpperBounds();
|
||||
for (TypeValue transitiveBound : upperBounds) {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
//KT-600 Problem with 'sure' extension function type inference
|
||||
|
||||
fun <T : Any> T?.sure() : T { if (this != null) return this else throw NullPointerException() }
|
||||
|
||||
fun test() {
|
||||
val i : Int? = 10
|
||||
val <!UNUSED_VARIABLE!>i2<!> : Int = i.sure() // inferred type is Int? but Int was excepted
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//KT-549 type inference failed
|
||||
namespace demo
|
||||
|
||||
fun filter<T>(list : Array<T>, filter : fun (T) : Boolean) : java.util.List<T> {
|
||||
val answer = java.util.ArrayList<T>();
|
||||
for (l in list) {
|
||||
if (filter(l)) answer.add(l)
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
for (a in filter(args, {it.length > 1})) {
|
||||
System.out?.println("Hello, ${a}!")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//KT-580 Type inference failed
|
||||
namespace whats.the.difference
|
||||
|
||||
import java.util.*
|
||||
|
||||
fun iarray(vararg a : String) = a // BUG
|
||||
|
||||
fun main(vals : IntArray) {
|
||||
val vals = iarray("789", "678", "567")
|
||||
val diffs = ArrayList<Int>
|
||||
for (i in vals.indices) {
|
||||
for (j in i..vals.lastIndex()) // Type inference failed
|
||||
diffs.add(vals[i].length - vals[j].length)
|
||||
for (j in i..vals.lastIndex) // Type inference failed
|
||||
diffs.add(vals[i].length - vals[j].length)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Array<T>.lastIndex() = size - 1
|
||||
val <T> Array<T>.lastIndex : Int get() = size - 1
|
||||
@@ -0,0 +1,3 @@
|
||||
//KT-571 Type inference failed
|
||||
fun <T, R> let(t : T, body : fun(T) : R) = body(t)
|
||||
private fun double(d : Int) : Int = let(d * 2) {it / 10 + it * 2 % 10}
|
||||
@@ -19,7 +19,7 @@ fun t3() {
|
||||
|
||||
fun t4() {
|
||||
val e: E? = E()
|
||||
System.out?.println(e?.foo() == e) //verify error
|
||||
System.out?.println(e?.bar() == e) //verify error
|
||||
System.out?.println(e?.foo()) //verify error
|
||||
}
|
||||
|
||||
@@ -35,4 +35,5 @@ class C(val x: Int)
|
||||
class D(val s: String)
|
||||
class E() {
|
||||
fun foo() = 1
|
||||
fun bar() = this
|
||||
}
|
||||
|
||||
@@ -162,6 +162,22 @@ public class JetTypeCheckerTest extends JetLiteFixture {
|
||||
assertIntersection("Int?", "Int?", "Int?");
|
||||
assertIntersection("Int", "Int?", "Int");
|
||||
assertIntersection("Int", "Int", "Int?");
|
||||
|
||||
assertIntersection("Int", "Any", "Int");
|
||||
assertIntersection("Int", "Int", "Any");
|
||||
|
||||
assertIntersection("Int", "Any", "Int?");
|
||||
assertIntersection("Int", "Int?", "Any");
|
||||
assertIntersection("Int", "Any?", "Int");
|
||||
assertIntersection("Int", "Int", "Any?");
|
||||
|
||||
assertIntersection("Nothing", "Nothing", "Nothing");
|
||||
assertIntersection("Nothing?", "Nothing?", "Nothing?");
|
||||
assertIntersection("Nothing", "Nothing", "Nothing?");
|
||||
assertIntersection("Nothing", "Nothing?", "Nothing");
|
||||
|
||||
assertIntersection("Nothing?", "String?", "Nothing?");
|
||||
assertIntersection("Nothing?", "Nothing?", "String?");
|
||||
}
|
||||
|
||||
public void testBasicSubtyping() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user