KT-702 Type inference failed

This commit is contained in:
Andrey Breslav
2011-12-01 22:33:43 +03:00
parent df733a5b60
commit 13a1777d39
8 changed files with 259 additions and 167 deletions
@@ -116,8 +116,10 @@ public class SubstitutingScope implements JetScope {
allDescriptors = Sets.newHashSet();
for (DeclarationDescriptor descriptor : workerScope.getAllDescriptors()) {
DeclarationDescriptor substitute = substitute(descriptor);
assert substitute != null;
allDescriptors.add(substitute);
// assert substitute != null : descriptor;
if (substitute != null) {
allDescriptors.add(substitute);
}
}
}
return allDescriptors;
@@ -125,22 +125,33 @@ public class TypeSubstitutor {
private JetType unsafeSubstitute(@NotNull JetType type, @NotNull Variance howThisTypeIsUsed) throws SubstitutionException {
if (ErrorUtils.isErrorType(type)) return type;
TypeConstructor constructor = type.getConstructor();
TypeProjection value = substitution.get(constructor);
TypeProjection value = getValueWithCorrectNullability(substitution, type);
if (value != null) {
TypeConstructor constructor = type.getConstructor();
assert constructor.getDeclarationDescriptor() instanceof TypeParameterDescriptor;
return TypeUtils.makeNullableIfNeeded(substitutionResult((TypeParameterDescriptor) constructor.getDeclarationDescriptor(), howThisTypeIsUsed, Variance.INVARIANT, value).getType(), type.isNullable());
TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) constructor.getDeclarationDescriptor();
// if (!allows(howThisTypeIsUsed, value.getProjectionKind())) {
// throw new SubstitutionException("!!" + value.toString());
// }
// return value.getType();
TypeProjection result = substitutionResult(typeParameterDescriptor, howThisTypeIsUsed, Variance.INVARIANT, value);
return TypeUtils.makeNullableIfNeeded(result.getType(), type.isNullable());
}
return specializeType(type, howThisTypeIsUsed);
}
private TypeProjection getValueWithCorrectNullability(TypeSubstitution substitution, JetType type) {
TypeProjection typeProjection = substitution.get(type.getConstructor());
if (typeProjection == null) return null;
return type.isNullable() ? makeNullableProjection(typeProjection) : typeProjection;
}
@NotNull
private static TypeProjection makeNullableProjection(@NotNull TypeProjection value) {
return new TypeProjection(value.getProjectionKind(), TypeUtils.makeNullable(value.getType()));
}
private JetType specializeType(JetType subjectType, Variance callSiteVariance) throws SubstitutionException {
if (ErrorUtils.isErrorType(subjectType)) return subjectType;
@@ -178,7 +189,7 @@ public class TypeSubstitutor {
Variance effectiveProjectionKind = asymmetricOr(passedProjectionKind, parameterVariance);
Variance effectiveContextVariance = contextCallSiteVariance.superpose(effectiveProjectionKind);
TypeProjection projectionValue = substitutionContext.get(typeToSubstituteIn.getConstructor());
TypeProjection projectionValue = getValueWithCorrectNullability(substitutionContext, typeToSubstituteIn);
if (projectionValue != null) {
assert typeToSubstituteIn.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor;
@@ -3,16 +3,8 @@ package org.jetbrains.jet.lang.types.checker;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.types.*;
import java.util.List;
import static org.jetbrains.jet.lang.types.Variance.INVARIANT;
import static org.jetbrains.jet.lang.types.Variance.IN_VARIANCE;
import static org.jetbrains.jet.lang.types.Variance.OUT_VARIANCE;
/**
* @author abreslav
*/
@@ -30,156 +22,48 @@ public class JetTypeChecker {
}
public boolean equalTypes(@NotNull JetType a, @NotNull JetType b) {
return equalTypes(a, b, EMPTY_AXIOMS);
return TYPE_CHECKER.equalTypes(a, b);
}
public boolean equalTypes(@NotNull JetType a, @NotNull JetType b, @NotNull BiMap<TypeConstructor, TypeConstructor> equalityAxioms) {
return TYPE_CHECKER.equalTypes(a, b, equalityAxioms);
}
// This method returns the supertype of the first parameter that has the same constructor
// as the second parameter, applying the substitution of type arguments to it
@Nullable
private static JetType findCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
TypeConstructor constructor = subtype.getConstructor();
if (constructor.equals(supertype.getConstructor())) {
return subtype;
}
for (JetType immediateSupertype : constructor.getSupertypes()) {
JetType correspondingSupertype = findCorrespondingSupertype(immediateSupertype, supertype);
if (correspondingSupertype != null) {
return TypeSubstitutor.create(subtype).safeSubstitute(correspondingSupertype, Variance.INVARIANT);
public boolean equalTypes(@NotNull JetType a, @NotNull JetType b, @NotNull final BiMap<TypeConstructor, TypeConstructor> equalityAxioms) {
return new TypeCheckingProcedure(new TypeCheckerTypingConstraints() {
@Override
public boolean assertEqualTypeConstructors(@NotNull TypeConstructor constructor1, @NotNull TypeConstructor constructor2) {
if (!constructor1.equals(constructor2)) {
TypeConstructor img1 = equalityAxioms.get(constructor1);
TypeConstructor img2 = equalityAxioms.get(constructor2);
if (!(img1 != null && img1.equals(constructor2)) &&
!(img2 != null && img2.equals(constructor1))) {
return false;
}
}
return true;
}
}
return null;
}).equalTypes(a, b);
}
private static JetType getOutType(TypeParameterDescriptor parameter, TypeProjection argument) {
boolean isOutProjected = argument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE;
return isOutProjected ? parameter.getUpperBoundsAsType() : argument.getType();
}
private static final TypeCheckingProcedure TYPE_CHECKER = new TypeCheckingProcedure(new TypeCheckerTypingConstraints());
private static JetType getInType(TypeParameterDescriptor parameter, TypeProjection argument) {
boolean isOutProjected = argument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE;
return isOutProjected ? JetStandardClasses.getNothingType() : argument.getType();
}
/**
* Methods of this class return true to continue type checking and false to fail
*/
public interface TypingConstraintBuilder {
boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b);
boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype);
boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype);
}
private static final TypeCheckingProcedure TYPE_CHECKER = new TypeCheckingProcedure(new TypingConstraintBuilder() {
private static class TypeCheckerTypingConstraints implements TypingConstraints {
@Override
public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b) {
return TypeUtils.equalTypes(a, b);
public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) {
return typeCheckingProcedure.equalTypes(a, b);
// return TypeUtils.equalTypes(a, b);
}
@Override
public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype) {
return INSTANCE.isSubtypeOf(subtype, supertype);
public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
return a.equals(b);
}
@Override
public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) {
return typeCheckingProcedure.isSubtypeOf(subtype, supertype);
}
@Override
public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
return false; // type checking fails
}
});
public static class TypeCheckingProcedure {
private final TypingConstraintBuilder constraintBuilder;
public TypeCheckingProcedure(TypingConstraintBuilder constraintBuilder) {
this.constraintBuilder = constraintBuilder;
}
public boolean equalTypes(@NotNull JetType type1, @NotNull JetType type2, @NotNull BiMap<TypeConstructor, TypeConstructor> equalityAxioms) {
if (type1.isNullable() != type2.isNullable()) {
return false;
}
TypeConstructor constructor1 = type1.getConstructor();
TypeConstructor constructor2 = type2.getConstructor();
if (!constructor1.equals(constructor2)) {
TypeConstructor img1 = equalityAxioms.get(constructor1);
TypeConstructor img2 = equalityAxioms.get(constructor2);
if (!(img1 != null && img1.equals(constructor2)) &&
!(img2 != null && img2.equals(constructor1))) {
return false;
}
}
List<TypeProjection> type1Arguments = type1.getArguments();
List<TypeProjection> type2Arguments = type2.getArguments();
if (type1Arguments.size() != type2Arguments.size()) {
return false;
}
for (int i = 0; i < type1Arguments.size(); i++) {
TypeProjection typeProjection1 = type1Arguments.get(i);
TypeProjection typeProjection2 = type2Arguments.get(i);
if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) {
return false;
}
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType(), equalityAxioms)) {
return false;
}
}
return true;
}
public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) {
if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) {
return true;
}
if (!supertype.isNullable() && subtype.isNullable()) {
return false;
}
subtype = TypeUtils.makeNotNullable(subtype);
supertype = TypeUtils.makeNotNullable(supertype);
if (JetStandardClasses.isNothingOrNullableNothing(subtype)) {
return true;
}
@Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype);
if (closestSupertype == null) {
return constraintBuilder.noCorrespondingSupertype(subtype, supertype); // if this returns true, there still isn't any supertype to continue with
}
return checkSubtypeForTheSameConstructor(closestSupertype, supertype);
}
private boolean checkSubtypeForTheSameConstructor(@NotNull JetType subtype, @NotNull JetType supertype) {
TypeConstructor constructor = subtype.getConstructor();
assert constructor.equals(supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor();
List<TypeProjection> subArguments = subtype.getArguments();
List<TypeProjection> superArguments = supertype.getArguments();
List<TypeParameterDescriptor> parameters = constructor.getParameters();
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
TypeParameterDescriptor parameter = parameters.get(i);
TypeProjection subArgument = subArguments.get(i);
JetType subIn = getInType(parameter, subArgument);
JetType subOut = getOutType(parameter, subArgument);
TypeProjection superArgument = superArguments.get(i);
JetType superIn = getInType(parameter, superArgument);
JetType superOut = getOutType(parameter, superArgument);
if (parameter.getVariance() == INVARIANT && subArgument.getProjectionKind() == INVARIANT && superArgument.getProjectionKind() == INVARIANT) {
if (!constraintBuilder.assertEqualTypes(subArgument.getType(), superArgument.getType())) return false;
}
else {
if (!constraintBuilder.assertSubtype(subOut, superOut)) return false;
if (!constraintBuilder.assertSubtype(superIn, subIn)) return false;
}
}
return true;
}
}
}
@@ -0,0 +1,136 @@
package org.jetbrains.jet.lang.types.checker;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.types.*;
import java.util.List;
import static org.jetbrains.jet.lang.types.Variance.*;
/**
* @author abreslav
*/
public class TypeCheckingProcedure {
// This method returns the supertype of the first parameter that has the same constructor
// as the second parameter, applying the substitution of type arguments to it
@Nullable
private static JetType findCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
TypeConstructor constructor = subtype.getConstructor();
if (constructor.equals(supertype.getConstructor())) {
return subtype;
}
for (JetType immediateSupertype : constructor.getSupertypes()) {
JetType correspondingSupertype = findCorrespondingSupertype(immediateSupertype, supertype);
if (correspondingSupertype != null) {
return TypeSubstitutor.create(subtype).safeSubstitute(correspondingSupertype, Variance.INVARIANT);
}
}
return null;
}
private static JetType getOutType(TypeParameterDescriptor parameter, TypeProjection argument) {
boolean isOutProjected = argument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE;
return isOutProjected ? parameter.getUpperBoundsAsType() : argument.getType();
}
private static JetType getInType(TypeParameterDescriptor parameter, TypeProjection argument) {
boolean isOutProjected = argument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE;
return isOutProjected ? JetStandardClasses.getNothingType() : argument.getType();
}
private final TypingConstraints constraints;
public TypeCheckingProcedure(TypingConstraints constraints) {
this.constraints = constraints;
}
public boolean equalTypes(@NotNull JetType type1, @NotNull JetType type2) {
if (type1.isNullable() != type2.isNullable()) {
return false;
}
if (type1.isNullable()) {
// Then type2 is nullable, too (see the previous condition
return constraints.assertEqualTypes(TypeUtils.makeNotNullable(type1), TypeUtils.makeNotNullable(type2), this);
}
TypeConstructor constructor1 = type1.getConstructor();
TypeConstructor constructor2 = type2.getConstructor();
if (!constraints.assertEqualTypeConstructors(constructor1, constructor2)) {
return false;
}
List<TypeProjection> type1Arguments = type1.getArguments();
List<TypeProjection> type2Arguments = type2.getArguments();
if (type1Arguments.size() != type2Arguments.size()) {
return false;
}
for (int i = 0; i < type1Arguments.size(); i++) {
TypeProjection typeProjection1 = type1Arguments.get(i);
TypeProjection typeProjection2 = type2Arguments.get(i);
if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) {
return false;
}
if (!constraints.assertEqualTypes(typeProjection1.getType(), typeProjection2.getType(), this)) {
return false;
}
}
return true;
}
public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) {
if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) {
return true;
}
if (!supertype.isNullable() && subtype.isNullable()) {
return false;
}
subtype = TypeUtils.makeNotNullable(subtype);
supertype = TypeUtils.makeNotNullable(supertype);
if (JetStandardClasses.isNothingOrNullableNothing(subtype)) {
return true;
}
@Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype);
if (closestSupertype == null) {
return constraints.noCorrespondingSupertype(subtype, supertype); // if this returns true, there still isn't any supertype to continue with
}
return checkSubtypeForTheSameConstructor(closestSupertype, supertype);
}
private boolean checkSubtypeForTheSameConstructor(@NotNull JetType subtype, @NotNull JetType supertype) {
TypeConstructor constructor = subtype.getConstructor();
assert constructor.equals(supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor();
List<TypeProjection> subArguments = subtype.getArguments();
List<TypeProjection> superArguments = supertype.getArguments();
List<TypeParameterDescriptor> parameters = constructor.getParameters();
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
TypeParameterDescriptor parameter = parameters.get(i);
TypeProjection subArgument = subArguments.get(i);
JetType subIn = getInType(parameter, subArgument);
JetType subOut = getOutType(parameter, subArgument);
TypeProjection superArgument = superArguments.get(i);
JetType superIn = getInType(parameter, superArgument);
JetType superOut = getOutType(parameter, superArgument);
if (parameter.getVariance() == INVARIANT && subArgument.getProjectionKind() == INVARIANT && superArgument.getProjectionKind() == INVARIANT) {
if (!constraints.assertEqualTypes(subArgument.getType(), superArgument.getType(), this)) return false;
}
else {
if (!constraints.assertSubtype(subOut, superOut, this)) return false;
if (!constraints.assertSubtype(superIn, subIn, this)) return false;
}
}
return true;
}
}
@@ -0,0 +1,18 @@
package org.jetbrains.jet.lang.types.checker;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeConstructor;
/**
* Methods of this class return true to continue type checking and false to fail
*/
public interface TypingConstraints {
boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure);
boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b);
boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure);
boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype);
}
@@ -8,6 +8,8 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
import org.jetbrains.jet.lang.types.checker.TypingConstraints;
import java.util.Collection;
import java.util.Collections;
@@ -224,7 +226,7 @@ public class ConstraintSystemImpl implements ConstraintSystem {
public boolean equate(TypeValue other) {
if (other instanceof KnownType) {
KnownType knownType = (KnownType) other;
return TypeUtils.equalTypes(type, knownType.getType());
return constraintExpander.equalTypes(type, knownType.getType());
}
return other.equate(this);
}
@@ -234,16 +236,16 @@ public class ConstraintSystemImpl implements ConstraintSystem {
private final Map<TypeParameterDescriptor, UnknownType> unknownTypes = Maps.newHashMap();
private final JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
private static final class TypeConstraintBuilderAdapter implements JetTypeChecker.TypingConstraintBuilder {
private final JetTypeChecker.TypingConstraintBuilder delegate;
private static final class TypeConstraintBuilderAdapter implements TypingConstraints {
private final TypingConstraints delegate;
private TypeConstraintBuilderAdapter(JetTypeChecker.TypingConstraintBuilder delegate) {
private TypeConstraintBuilderAdapter(TypingConstraints delegate) {
this.delegate = delegate;
}
@Override
public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b) {
boolean result = delegate.assertEqualTypes(a, b);
public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) {
boolean result = delegate.assertEqualTypes(a, b, typeCheckingProcedure);
if (!result) {
println("-- Failed to equate " + a + " and " + b);
}
@@ -251,8 +253,17 @@ public class ConstraintSystemImpl implements ConstraintSystem {
}
@Override
public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype) {
boolean result = delegate.assertSubtype(subtype, supertype);
public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
boolean result = delegate.assertEqualTypeConstructors(a, b);
if (!result) {
println("-- Type constructors are not equal: " + a + " and " + b);
}
return result;
}
@Override
public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) {
boolean result = delegate.assertSubtype(subtype, supertype, typeCheckingProcedure);
if (!result) {
println("-- " + subtype + " can't be a subtype of " + supertype);
}
@@ -269,9 +280,9 @@ public class ConstraintSystemImpl implements ConstraintSystem {
}
}
private final JetTypeChecker.TypeCheckingProcedure constraintExpander = new JetTypeChecker.TypeCheckingProcedure(new TypeConstraintBuilderAdapter(new JetTypeChecker.TypingConstraintBuilder() {
private final TypeCheckingProcedure constraintExpander = new TypeCheckingProcedure(new TypeConstraintBuilderAdapter(new TypingConstraints() {
@Override
public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b) {
public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) {
TypeValue aValue = getTypeValueFor(a);
TypeValue bValue = getTypeValueFor(b);
@@ -279,7 +290,14 @@ public class ConstraintSystemImpl implements ConstraintSystem {
}
@Override
public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype) {
public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
return a.equals(b)
|| unknownTypes.containsKey(a.getDeclarationDescriptor())
|| unknownTypes.containsKey(b.getDeclarationDescriptor());
}
@Override
public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) {
TypeValue subtypeValue = getTypeValueFor(subtype);
TypeValue supertypeValue = getTypeValueFor(supertype);
@@ -306,7 +324,8 @@ public class ConstraintSystemImpl implements ConstraintSystem {
return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType;
}
}));
}))
;
public ConstraintSystemImpl() {}
@@ -427,6 +446,10 @@ public class ConstraintSystemImpl implements ConstraintSystem {
// TODO : check that all bounds are respected by solutions:
// we have set some of them from equality constraints with known types
// and thus the bounds may be violated if some of the constraints conflict
println("====================================");
println("");
println("");
return solution;
}
@@ -566,6 +589,6 @@ public class ConstraintSystemImpl implements ConstraintSystem {
}
private static void println(String message) {
System.out.println(message);
// System.out.println(message);
}
}
@@ -4,7 +4,7 @@ fun <T> Array<out T>.safeGet(index : Int) : T? {
return if (index < size) this[index] else null
}
val args : Array<String> = Array<String>(1)
val args : Array<String> = Array<String>(1, {""})
val name : String = <!TYPE_MISMATCH!>args.safeGet<String>(0)<!> // No error, must be type mismatch
val name1 : String? = args.safeGet(0)
@@ -0,0 +1,18 @@
// KT-702 Type inference failed
// +JDK
fun getJavaClass<T>() : java.lang.Class<T> { return "" <!CAST_NEVER_SUCCEEDS!>as<!> Class<T> }
public class Throwables() {
class object {
public fun propagateIfInstanceOf<X : Throwable?>(throwable : Throwable?, declaredType : Class<X?>?) {
if (((throwable != null) && declaredType?.isInstance(throwable).sure()))
{
throw declaredType?.cast(throwable)
}
}
public fun propagateIfPossible(throwable : Throwable?) {
propagateIfInstanceOf(throwable, getJavaClass<Error?>) //; Type inference failed: Mismatch while expanding constraints
propagateIfInstanceOf(throwable, getJavaClass<RuntimeException?>) // Type inference failed: Mismatch while expanding constraints
}
}
}