diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index f9bedad327d..68effdfba5e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -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 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) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java index 5cfbbc1301f..1c77b65f443 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java @@ -171,11 +171,20 @@ public class ErrorUtils { } private static JetType createErrorType(String debugMessage, JetScope memberScope) { - return new ErrorTypeImpl(new TypeConstructorImpl(ERROR_CLASS, Collections.emptyList(), false, "[ERROR : " + debugMessage + "]", Collections.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.emptyList(), false, debugName, Collections.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() { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java index 6304adf84db..82e4416c056 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -110,54 +110,65 @@ public class TypeUtils { } @Nullable - public static JetType intersect(JetTypeChecker typeChecker, Set types) { + public static JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set types) { assert !types.isEmpty(); if (types.size() == 1) { return types.iterator().next(); } - StringBuilder debugName = new StringBuilder(); - boolean nullable = false; - Set 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 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 resultingTypes = Lists.newArrayList(); outer: - for (Iterator 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 noAnnotations = Collections.emptyList(); TypeConstructor constructor = new TypeConstructorImpl( null, noAnnotations, false, - debugName.toString(), + makeDebugNameForIntersectionType(resultingTypes).toString(), Collections.emptyList(), resultingTypes); @@ -171,11 +182,25 @@ public class TypeUtils { return new JetTypeImpl( noAnnotations, constructor, - nullable, + allNullable, Collections.emptyList(), new ChainedScope(null, scopes)); // TODO : check intersectibility, don't use a chanied scope } + private static StringBuilder makeDebugNameForIntersectionType(Iterable resultingTypes) { + StringBuilder debugName = new StringBuilder("{"); + for (Iterator 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; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemImpl.java index e693c3dddf1..c5aff27be5c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemImpl.java @@ -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 upperBounds = Sets.newHashSet(); - private final Set lowerBounds = Sets.newHashSet(); + private final Set mutableUpperBounds = Sets.newHashSet(); + private final Set upperBounds = Collections.unmodifiableSet(mutableUpperBounds); + + private final Set mutableLowerBounds = Sets.newHashSet(); + private final Set 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 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 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 upperBounds = upperBound.getUpperBounds(); for (TypeValue transitiveBound : upperBounds) { diff --git a/compiler/testData/checkerWithErrorTypes/full/kt600.jet b/compiler/testData/checkerWithErrorTypes/full/kt600.jet new file mode 100644 index 00000000000..b5734ce9471 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/kt600.jet @@ -0,0 +1,8 @@ +//KT-600 Problem with 'sure' extension function type inference + +fun T?.sure() : T { if (this != null) return this else throw NullPointerException() } + +fun test() { + val i : Int? = 10 + val i2 : Int = i.sure() // inferred type is Int? but Int was excepted +} \ No newline at end of file diff --git a/compiler/testData/checkerWithErrorTypes/full/regression/kt549.jet b/compiler/testData/checkerWithErrorTypes/full/regression/kt549.jet new file mode 100644 index 00000000000..9e4725f142e --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/regression/kt549.jet @@ -0,0 +1,16 @@ +//KT-549 type inference failed +namespace demo + + fun filter(list : Array, filter : fun (T) : Boolean) : java.util.List { + val answer = java.util.ArrayList(); + for (l in list) { + if (filter(l)) answer.add(l) + } + return answer; + } + +fun main(args : Array) { + for (a in filter(args, {it.length > 1})) { + System.out?.println("Hello, ${a}!") + } +} diff --git a/compiler/testData/checkerWithErrorTypes/full/regression/kt580.jet b/compiler/testData/checkerWithErrorTypes/full/regression/kt580.jet new file mode 100644 index 00000000000..d05397ef6e0 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/regression/kt580.jet @@ -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 + 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 Array.lastIndex() = size - 1 +val Array.lastIndex : Int get() = size - 1 \ No newline at end of file diff --git a/compiler/testData/checkerWithErrorTypes/quick/regressions/kt571.jet b/compiler/testData/checkerWithErrorTypes/quick/regressions/kt571.jet new file mode 100644 index 00000000000..2333eb78644 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/quick/regressions/kt571.jet @@ -0,0 +1,3 @@ +//KT-571 Type inference failed +fun let(t : T, body : fun(T) : R) = body(t) +private fun double(d : Int) : Int = let(d * 2) {it / 10 + it * 2 % 10} diff --git a/compiler/testData/codegen/regressions/kt247.jet b/compiler/testData/codegen/regressions/kt247.jet index 49637518d25..af13811dee9 100644 --- a/compiler/testData/codegen/regressions/kt247.jet +++ b/compiler/testData/codegen/regressions/kt247.jet @@ -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 } diff --git a/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index bc2d5e09fa3..81687edd8c8 100644 --- a/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -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 {