Nullability propagation with NotNull as default for type arguments

This commit is contained in:
Andrey Breslav
2012-12-07 20:05:14 +04:00
parent 3b39257f13
commit becd68b98d
6 changed files with 163 additions and 40 deletions
@@ -44,6 +44,8 @@ import java.util.*;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFQName; import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFQName;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getVarargParameterType; import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getVarargParameterType;
import static org.jetbrains.jet.lang.resolve.java.TypeUsage.*;
import static org.jetbrains.jet.lang.types.Variance.INVARIANT;
public class SignaturesPropagationData { public class SignaturesPropagationData {
private static final Logger LOG = Logger.getInstance(SignaturesPropagationData.class); private static final Logger LOG = Logger.getInstance(SignaturesPropagationData.class);
@@ -108,7 +110,7 @@ public class SignaturesPropagationData {
} }
}); });
return modifyTypeAccordingToSuperMethods(autoType, typesFromSuperMethods); return modifyTypeAccordingToSuperMethods(autoType, typesFromSuperMethods, MEMBER_SIGNATURE_COVARIANT);
} }
private List<TypeParameterDescriptor> modifyTypeParametersAccordingToSuperMethods(List<TypeParameterDescriptor> autoTypeParameters) { private List<TypeParameterDescriptor> modifyTypeParametersAccordingToSuperMethods(List<TypeParameterDescriptor> autoTypeParameters) {
@@ -128,10 +130,10 @@ public class SignaturesPropagationData {
for (Iterator<JetType> iterator : upperBoundFromSuperFunctionsIterators) { for (Iterator<JetType> iterator : upperBoundFromSuperFunctionsIterators) {
assert iterator.hasNext(); assert iterator.hasNext();
upperBoundsFromSuperFunctions.add(new TypeAndVariance(iterator.next(), Variance.INVARIANT)); upperBoundsFromSuperFunctions.add(new TypeAndVariance(iterator.next(), INVARIANT));
} }
JetType modifiedUpperBound = modifyTypeAccordingToSuperMethods(autoUpperBound, upperBoundsFromSuperFunctions); JetType modifiedUpperBound = modifyTypeAccordingToSuperMethods(autoUpperBound, upperBoundsFromSuperFunctions, UPPER_BOUND);
modifiedTypeParameter.addUpperBound(modifiedUpperBound); modifiedTypeParameter.addUpperBound(modifiedUpperBound);
} }
@@ -160,14 +162,14 @@ public class SignaturesPropagationData {
new Function<FunctionDescriptor, TypeAndVariance>() { new Function<FunctionDescriptor, TypeAndVariance>() {
@Override @Override
public TypeAndVariance fun(FunctionDescriptor superFunction) { public TypeAndVariance fun(FunctionDescriptor superFunction) {
return new TypeAndVariance(superFunction.getValueParameters().get(index).getType(), Variance.INVARIANT); return new TypeAndVariance(superFunction.getValueParameters().get(index).getType(), INVARIANT);
} }
}); });
VarargCheckResult varargCheckResult = VarargCheckResult varargCheckResult =
checkVarargInSuperFunctions(originalParam); checkVarargInSuperFunctions(originalParam);
JetType altType = modifyTypeAccordingToSuperMethods(varargCheckResult.parameterType, typesFromSuperMethods); JetType altType = modifyTypeAccordingToSuperMethods(varargCheckResult.parameterType, typesFromSuperMethods, MEMBER_SIGNATURE_CONTRAVARIANT);
resultParameters.add(new ValueParameterDescriptorImpl( resultParameters.add(new ValueParameterDescriptorImpl(
originalParam.getContainingDeclaration(), originalParam.getContainingDeclaration(),
@@ -184,7 +186,7 @@ public class SignaturesPropagationData {
JetType originalReceiverType = parameters.getReceiverType(); JetType originalReceiverType = parameters.getReceiverType();
if (originalReceiverType != null) { if (originalReceiverType != null) {
JetType substituted = SignaturesUtil.createSubstitutorForFunctionTypeParameters(autoTypeParameterToModified) JetType substituted = SignaturesUtil.createSubstitutorForFunctionTypeParameters(autoTypeParameterToModified)
.substitute(originalReceiverType, Variance.INVARIANT); .substitute(originalReceiverType, INVARIANT);
assert substituted != null; assert substituted != null;
return new JavaDescriptorResolver.ValueParameterDescriptors(substituted, resultParameters); return new JavaDescriptorResolver.ValueParameterDescriptors(substituted, resultParameters);
} }
@@ -289,13 +291,14 @@ public class SignaturesPropagationData {
@NotNull @NotNull
private JetType modifyTypeAccordingToSuperMethods( private JetType modifyTypeAccordingToSuperMethods(
@NotNull JetType autoType, @NotNull JetType autoType,
@NotNull List<TypeAndVariance> typesFromSuper @NotNull List<TypeAndVariance> typesFromSuper,
@NotNull TypeUsage howThisTypeIsUsed
) { ) {
if (ErrorUtils.isErrorType(autoType)) { if (ErrorUtils.isErrorType(autoType)) {
return autoType; return autoType;
} }
boolean resultNullable = typeMustBeNullable(autoType, typesFromSuper); boolean resultNullable = typeMustBeNullable(autoType, typesFromSuper, howThisTypeIsUsed);
ClassifierDescriptor resultClassifier = modifyTypeClassifier(autoType, typesFromSuper); ClassifierDescriptor resultClassifier = modifyTypeClassifier(autoType, typesFromSuper);
List<TypeProjection> resultArguments = getTypeArgsOfType(autoType, resultClassifier, typesFromSuper); List<TypeProjection> resultArguments = getTypeArgsOfType(autoType, resultClassifier, typesFromSuper);
JetScope resultScope; JetScope resultScope;
@@ -338,7 +341,7 @@ public class SignaturesPropagationData {
List<TypeProjectionAndVariance> projectionsFromSuper = typeArgumentsFromSuper.get(parameter.getIndex()); List<TypeProjectionAndVariance> projectionsFromSuper = typeArgumentsFromSuper.get(parameter.getIndex());
List<TypeAndVariance> argTypesFromSuper = getTypes(projectionsFromSuper); List<TypeAndVariance> argTypesFromSuper = getTypes(projectionsFromSuper);
JetType type = modifyTypeAccordingToSuperMethods(argumentType, argTypesFromSuper); JetType type = modifyTypeAccordingToSuperMethods(argumentType, argTypesFromSuper, TYPE_ARGUMENT);
Variance projectionKind = calculateArgumentProjectionKindFromSuper(argument, projectionsFromSuper); Variance projectionKind = calculateArgumentProjectionKindFromSuper(argument, projectionsFromSuper);
resultArguments.add(new TypeProjection(projectionKind, type)); resultArguments.add(new TypeProjection(projectionKind, type));
@@ -361,7 +364,7 @@ public class SignaturesPropagationData {
} }
else if (projectionKindsInSuper.size() == 1) { else if (projectionKindsInSuper.size() == 1) {
Variance projectionKindInSuper = projectionKindsInSuper.iterator().next(); Variance projectionKindInSuper = projectionKindsInSuper.iterator().next();
if (defaultProjectionKind == Variance.INVARIANT || defaultProjectionKind == projectionKindInSuper) { if (defaultProjectionKind == INVARIANT || defaultProjectionKind == projectionKindInSuper) {
return projectionKindInSuper; return projectionKindInSuper;
} }
else { else {
@@ -380,11 +383,24 @@ public class SignaturesPropagationData {
private static List<TypeAndVariance> getTypes(@NotNull List<TypeProjectionAndVariance> projections) { private static List<TypeAndVariance> getTypes(@NotNull List<TypeProjectionAndVariance> projections) {
List<TypeAndVariance> types = Lists.newArrayList(); List<TypeAndVariance> types = Lists.newArrayList();
for (TypeProjectionAndVariance projection : projections) { for (TypeProjectionAndVariance projection : projections) {
types.add(new TypeAndVariance(projection.typeProjection.getType(), projection.varianceOfParameter)); types.add(new TypeAndVariance(projection.typeProjection.getType(),
merge(projection.varianceOfPosition, projection.typeProjection.getProjectionKind())));
} }
return types; return types;
} }
private static Variance merge(Variance positionOfOuter, Variance projectionKind) {
// Inv<Inv<out X>>, X is in invariant position
if (positionOfOuter == INVARIANT) return INVARIANT;
// Out<X>, X is in out-position
if (projectionKind == INVARIANT) return positionOfOuter;
// Out<Out<X>>, X is in out-position
// In<In<X>>, X is in out-position
// Out<In<X>>, X is in in-position
// In<Out<X>>, X is in in-position
return positionOfOuter.superpose(projectionKind);
}
// Returns list with type arguments info from supertypes // Returns list with type arguments info from supertypes
// Example: // Example:
// - Foo<A, B> is a subtype of Bar<A, List<B>>, Baz<Boolean, A> // - Foo<A, B> is a subtype of Bar<A, List<B>>, Baz<Boolean, A>
@@ -432,7 +448,8 @@ public class SignaturesPropagationData {
// this condition is true for 1 and 4, false for 2 and 3 // this condition is true for 1 and 4, false for 2 and 3
if (classifier instanceof TypeParameterDescriptor && classifier.getContainingDeclaration() == klass) { if (classifier instanceof TypeParameterDescriptor && classifier.getContainingDeclaration() == klass) {
int parameterIndex = ((TypeParameterDescriptor) classifier).getIndex(); int parameterIndex = ((TypeParameterDescriptor) classifier).getIndex();
parameterToArgumentsFromSuper.get(parameterIndex).add(new TypeProjectionAndVariance(argument, parameter.getVariance())); Variance effectiveVariance = parameter.getVariance().superpose(typeFromSuper.varianceOfPosition);
parameterToArgumentsFromSuper.get(parameterIndex).add(new TypeProjectionAndVariance(argument, effectiveVariance));
} }
} }
} }
@@ -442,36 +459,44 @@ public class SignaturesPropagationData {
private boolean typeMustBeNullable( private boolean typeMustBeNullable(
@NotNull JetType autoType, @NotNull JetType autoType,
@NotNull List<TypeAndVariance> typesFromSuper @NotNull List<TypeAndVariance> typesFromSuper,
@NotNull TypeUsage howThisTypeIsUsed
) { ) {
boolean someSupersNotCovariantNullable = false; boolean someSupersNotCovariantNullable = false;
boolean someSupersCovariantNullable = false;
boolean someSupersNotNull = false; boolean someSupersNotNull = false;
for (TypeAndVariance typeFromSuper : typesFromSuper) { for (TypeAndVariance typeFromSuper : typesFromSuper) {
if (typeFromSuper.type.isNullable() && typeFromSuper.varianceOfParameter != Variance.OUT_VARIANCE) { if (!typeFromSuper.type.isNullable()) {
someSupersNotCovariantNullable = true;
}
else if (!typeFromSuper.type.isNullable()) {
someSupersNotNull = true; someSupersNotNull = true;
} }
else {
if (typeFromSuper.varianceOfPosition == Variance.OUT_VARIANCE) {
someSupersCovariantNullable = true;
}
else {
someSupersNotCovariantNullable = true;
}
}
} }
if (someSupersNotCovariantNullable == someSupersNotNull) { if (someSupersNotNull && someSupersNotCovariantNullable) {
if (someSupersNotCovariantNullable) { reportError("Incompatible types in superclasses: " + typesFromSuper);
reportError("Incompatible types in superclasses: " + typesFromSuper);
}
return autoType.isNullable(); return autoType.isNullable();
} }
else { else if (someSupersNotNull) {
if (someSupersNotCovariantNullable) { return false;
if (!autoType.isNullable()) { }
reportError("In superclass type is nullable: " + typesFromSuper + ", in subclass it is not: " + autoType); else if (someSupersNotCovariantNullable || someSupersCovariantNullable) {
} boolean annotatedAsNotNull = howThisTypeIsUsed != TYPE_ARGUMENT && !autoType.isNullable();
if (annotatedAsNotNull && someSupersNotCovariantNullable) {
reportError("In superclass type is nullable: " + typesFromSuper + ", in subclass it is not: " + autoType);
return true; return true;
} }
else { // someSupersNotNull is true here
return false; return !annotatedAsNotNull;
}
} }
return autoType.isNullable();
} }
@NotNull @NotNull
@@ -504,7 +529,7 @@ public class SignaturesPropagationData {
someSupersMutable = true; someSupersMutable = true;
} }
else if (collectionMapping.isReadOnlyCollection(classFromSuper)) { else if (collectionMapping.isReadOnlyCollection(classFromSuper)) {
if (typeFromSuper.varianceOfParameter == Variance.OUT_VARIANCE) { if (typeFromSuper.varianceOfPosition == Variance.OUT_VARIANCE) {
someSupersCovariantReadOnly = true; someSupersCovariantReadOnly = true;
} }
else { else {
@@ -549,11 +574,11 @@ public class SignaturesPropagationData {
private static class TypeProjectionAndVariance { private static class TypeProjectionAndVariance {
public final TypeProjection typeProjection; public final TypeProjection typeProjection;
public final Variance varianceOfParameter; public final Variance varianceOfPosition;
public TypeProjectionAndVariance(TypeProjection typeProjection, Variance varianceOfParameter) { public TypeProjectionAndVariance(TypeProjection typeProjection, Variance varianceOfPosition) {
this.typeProjection = typeProjection; this.typeProjection = typeProjection;
this.varianceOfParameter = varianceOfParameter; this.varianceOfPosition = varianceOfPosition;
} }
public String toString() { public String toString() {
@@ -563,11 +588,11 @@ public class SignaturesPropagationData {
private static class TypeAndVariance { private static class TypeAndVariance {
public final JetType type; public final JetType type;
public final Variance varianceOfParameter; public final Variance varianceOfPosition;
public TypeAndVariance(JetType type, Variance varianceOfParameter) { public TypeAndVariance(JetType type, Variance varianceOfPosition) {
this.type = type; this.type = type;
this.varianceOfParameter = varianceOfParameter; this.varianceOfPosition = varianceOfPosition;
} }
public String toString() { public String toString() {
@@ -0,0 +1,42 @@
package test;
import java.util.List;
import jet.runtime.typeinfo.KotlinSignature;
public interface PropagateTypeArgumentNullable {
public interface Super {
@KotlinSignature("fun outS(p : List<String?>)")
void outS(List<String> p);
@KotlinSignature("fun invOutS(p : MutableList<List<String?>>)")
void invOutS(List<List<String>> p);
@KotlinSignature("fun outOutS(p : List<List<String?>>)")
void outOutS(List<List<String>> p);
@KotlinSignature("fun outR() : List<String?>")
List<String> outR();
@KotlinSignature("fun invR() : MutableList<String?>")
List<String> invR();
@KotlinSignature("fun invOutR() : MutableList<List<String?>>")
List<List<String>> invOutR();
}
public interface Sub extends Super {
void outS(List<String> p);
void invOutS(List<List<String>> p);
void outOutS(List<List<String>> p);
List<String> outR();
List<String> invR();
List<List<String>> invOutR();
}
}
@@ -0,0 +1,29 @@
package test
public trait PropagateTypeArgumentNullable: Object {
public trait Super: Object {
public fun outS(p0: List<String?>)
public fun invOutS(p0 : MutableList<List<String?>>)
public fun outOutS(p0 : List<List<String?>>)
public fun outR() : List<String?>
public fun invR() : MutableList<String?>
public fun invOutR() : MutableList<List<String?>>
}
public trait Sub: Super {
override fun outS(p0: List<String?>)
override fun invOutS(p0 : MutableList<List<String?>>)
override fun outOutS(p0 : List<List<String?>>)
override fun outR() : List<String?>
override fun invR() : MutableList<String?>
override fun invOutR() : MutableList<List<String?>>
}
}
@@ -0,0 +1,20 @@
namespace test
public abstract trait test.PropagateTypeArgumentNullable : java.lang.Object {
public abstract trait test.PropagateTypeArgumentNullable.Sub : test.PropagateTypeArgumentNullable.Super {
public abstract override /*1*/ fun invOutR(): jet.MutableList<jet.List<jet.String?>>
public abstract override /*1*/ fun invOutS(/*0*/ p0: jet.MutableList<jet.List<jet.String?>>): jet.Tuple0
public abstract override /*1*/ fun invR(): jet.MutableList<jet.String?>
public abstract override /*1*/ fun outOutS(/*0*/ p0: jet.List<jet.List<jet.String?>>): jet.Tuple0
public abstract override /*1*/ fun outR(): jet.List<jet.String?>
public abstract override /*1*/ fun outS(/*0*/ p0: jet.List<jet.String?>): jet.Tuple0
}
public abstract trait test.PropagateTypeArgumentNullable.Super : java.lang.Object {
public abstract fun invOutR(): jet.MutableList<jet.List<jet.String?>>
public abstract fun invOutS(/*0*/ p0: jet.MutableList<jet.List<jet.String?>>): jet.Tuple0
public abstract fun invR(): jet.MutableList<jet.String?>
public abstract fun outOutS(/*0*/ p0: jet.List<jet.List<jet.String?>>): jet.Tuple0
public abstract fun outR(): jet.List<jet.String?>
public abstract fun outS(/*0*/ p0: jet.List<jet.String?>): jet.Tuple0
}
}
@@ -15,16 +15,13 @@
*/ */
package org.jetbrains.jet.jvm.compiler; package org.jetbrains.jet.jvm.compiler;
import junit.framework.Assert;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestSuite;
import java.io.File;
import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.InnerTestClasses; import org.jetbrains.jet.test.InnerTestClasses;
import org.jetbrains.jet.test.TestMetadata; import org.jetbrains.jet.test.TestMetadata;
import org.jetbrains.jet.jvm.compiler.AbstractLoadJavaTest; import java.io.File;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */ /** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */
@TestMetadata("compiler/testData/loadJava") @TestMetadata("compiler/testData/loadJava")
@@ -460,6 +457,11 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadJava/kotlinSignature/propagation"), "java", true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadJava/kotlinSignature/propagation"), "java", true);
} }
@TestMetadata("PropagateTypeArgumentNullable.java")
public void testPropagateTypeArgumentNullable() throws Exception {
doTest("compiler/testData/loadJava/kotlinSignature/propagation/PropagateTypeArgumentNullable.java");
}
@TestMetadata("compiler/testData/loadJava/kotlinSignature/propagation/parameter") @TestMetadata("compiler/testData/loadJava/kotlinSignature/propagation/parameter")
public static class Parameter extends AbstractLoadJavaTest { public static class Parameter extends AbstractLoadJavaTest {
public void testAllFilesPresentInParameter() throws Exception { public void testAllFilesPresentInParameter() throws Exception {
@@ -1355,6 +1355,11 @@ public class LazyResolveNamespaceComparingTestGenerated extends AbstractLazyReso
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadJava/kotlinSignature/propagation"), "kt", true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadJava/kotlinSignature/propagation"), "kt", true);
} }
@TestMetadata("PropagateTypeArgumentNullable.kt")
public void testPropagateTypeArgumentNullable() throws Exception {
doTestSinglePackage("compiler/testData/loadJava/kotlinSignature/propagation/PropagateTypeArgumentNullable.kt");
}
@TestMetadata("compiler/testData/loadJava/kotlinSignature/propagation/parameter") @TestMetadata("compiler/testData/loadJava/kotlinSignature/propagation/parameter")
public static class Parameter extends AbstractLazyResolveNamespaceComparingTest { public static class Parameter extends AbstractLazyResolveNamespaceComparingTest {
public void testAllFilesPresentInParameter() throws Exception { public void testAllFilesPresentInParameter() throws Exception {