From 308ab1c4ebf15ffb81485c2d52bd522b3f8e9c2e Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 25 Jan 2011 18:54:26 +0300 Subject: [PATCH] Subtyping for type constructor applications with projections --- .../jet/lang/types/JetStandardClasses.java | 1 - .../jet/lang/types/JetTypeChecker.java | 80 ++++++++++++++++++- .../jet/lang/types/ProjectionKind.java | 22 +++++ .../jetbrains/jet/lang/types/TupleType.java | 2 +- .../jet/lang/types/TypeProjection.java | 8 +- .../jet/types/JetTypeCheckerTest.java | 19 +++-- 6 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/lang/types/ProjectionKind.java diff --git a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java index 6c4b25783f5..98509e3a723 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java @@ -28,7 +28,6 @@ public class JetStandardClasses { private static final ClassDescriptor DOUBLE = new ClassDescriptor("Double"); private static final ClassDescriptor BOOLEAN = new ClassDescriptor("Boolean"); private static final ClassDescriptor STRING = new ClassDescriptor("String"); - private static final ClassDescriptor UNIT = new ClassDescriptor("Unit"); public static final int TUPLE_COUNT = 22; private static final ClassDescriptor[] TUPLE = new ClassDescriptor[TUPLE_COUNT]; diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index 8940e51dcf0..c4c6927a41f 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -110,7 +110,7 @@ public class JetTypeChecker { private Type substitute(Map parameterValues, Type subject) { List newArguments = new ArrayList(); for (TypeProjection argument : subject.getArguments()) { - newArguments.add(new TypeProjection(argument.getProjection(), substitute(parameterValues, argument.getType()))); + newArguments.add(new TypeProjection(argument.getProjectionKind(), substitute(parameterValues, argument.getType()))); } return specializeType(subject, newArguments); } @@ -121,10 +121,82 @@ public class JetTypeChecker { } private boolean checkSubtypeForTheSameConstructor(Type subtype, Type supertype) { - assert subtype.getConstructor().equals(supertype.getConstructor()); + TypeConstructor constructor = subtype.getConstructor(); + assert constructor.equals(supertype.getConstructor()); + + List subArguments = subtype.getArguments(); + List superArguments = supertype.getArguments(); + List parameters = constructor.getParameters(); + for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) { + TypeParameterDescriptor parameter = parameters.get(i); + TypeProjection subArgument = subArguments.get(i); + TypeProjection superArgument = superArguments.get(i); + + Type subArgumentType = subArgument.getType(); + Type superArgumentType = superArgument.getType(); + switch (parameter.getVariance()) { + case INVARIANT: + switch (superArgument.getProjectionKind()) { + case NO_PROJECTION: + if (!subArgumentType.equals(superArgumentType)) { + return false; + } + break; + case NEITHER_OUT_NOR_IN: + if (!isSubtypeOf(subArgumentType, superArgumentType)) { + return false; + } + break; + case OUT_ONLY: + if (subArgument.getProjectionKind() != ProjectionKind.OUT_ONLY) { + return false; + } + if (!isSubtypeOf(subArgumentType, superArgumentType)) { + return false; + } + break; + case IN_ONLY: + if (subArgument.getProjectionKind() != ProjectionKind.IN_ONLY) { + return false; + } + if (!isSubtypeOf(superArgumentType, subArgumentType)) { + return false; + } + break; + } + break; + case IN_VARIANCE: + switch (superArgument.getProjectionKind()) { + case NO_PROJECTION: + case IN_ONLY: + if (!isSubtypeOf(superArgumentType, subArgumentType)) { + return false; + } + break; + case NEITHER_OUT_NOR_IN: + case OUT_ONLY: + if (!isSubtypeOf(subArgumentType, superArgumentType)) { + return false; + } + break; + } + break; + case OUT_VARIANCE: + switch (superArgument.getProjectionKind()) { + case NO_PROJECTION: + case OUT_ONLY: + case NEITHER_OUT_NOR_IN: + case IN_ONLY: + if (!isSubtypeOf(subArgumentType, superArgumentType)) { + return false; + } + break; + } + break; + } + } - // TODO return true; } -} +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/lang/types/ProjectionKind.java b/idea/src/org/jetbrains/jet/lang/types/ProjectionKind.java new file mode 100644 index 00000000000..ea5d5b2c26a --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/types/ProjectionKind.java @@ -0,0 +1,22 @@ +package org.jetbrains.jet.lang.types; + +/** + * @author abreslav + */ +public enum ProjectionKind { + OUT_ONLY("out"), + IN_ONLY("in"), + NEITHER_OUT_NOR_IN("*"), + NO_PROJECTION(""); + + private final String text; + + ProjectionKind(String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } +} diff --git a/idea/src/org/jetbrains/jet/lang/types/TupleType.java b/idea/src/org/jetbrains/jet/lang/types/TupleType.java index 61a5922b741..eb00810e090 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TupleType.java +++ b/idea/src/org/jetbrains/jet/lang/types/TupleType.java @@ -51,7 +51,7 @@ public class TupleType extends TypeImpl { private static List toProjections(List arguments) { List result = new ArrayList(); for (Type argument : arguments) { - result.add(new TypeProjection(Variance.OUT_VARIANCE, argument)); + result.add(new TypeProjection(ProjectionKind.OUT_ONLY, argument)); } return result; } diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java b/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java index 7a11f8fad4c..c505da0d357 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeProjection.java @@ -4,19 +4,19 @@ package org.jetbrains.jet.lang.types; * @author abreslav */ public class TypeProjection { - private final Variance projection; + private final ProjectionKind projection; private final Type type; - public TypeProjection(Variance projection, Type type) { + public TypeProjection(ProjectionKind projection, Type type) { this.projection = projection; this.type = type; } public TypeProjection(Type type) { - this(Variance.INVARIANT, type); + this(ProjectionKind.NO_PROJECTION, type); } - public Variance getProjection() { + public ProjectionKind getProjectionKind() { return projection; } diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index d63b5f99fa4..4d5d621bb30 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -128,6 +128,10 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { assertSubtype("(Double)", "(Double)"); assertSubtype("(Unit)", "(Unit)"); + assertNotSubtype("(Unit)", "(Int)"); + + assertSubtype("(Unit)", "(Any)"); + } private void assertSubtype(String type1, String type2) { @@ -153,10 +157,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { JetTypeElement typeElement = typeNode.getTypeElement(); List argumentElements = typeNode.getTypeArguments(); - final List arguments = new ArrayList(); - for (JetTypeReference argumentElement : argumentElements) { - arguments.add(toType(argumentElement)); - } + final List arguments = toTypes(argumentElements); // TODO annotations final Type[] result = new Type[1]; @@ -169,7 +170,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { @Override public void visitTupleType(JetTupleType type) { // TODO labels - result[0] = TupleType.getTupleType(arguments); + result[0] = TupleType.getTupleType(toTypes(type.getComponentTypeRefs())); } @Override @@ -181,6 +182,14 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { return result[0]; } + private List toTypes(List argumentElements) { + final List arguments = new ArrayList(); + for (JetTypeReference argumentElement : argumentElements) { + arguments.add(toType(argumentElement)); + } + return arguments; + } + public void testImplicitConversions() throws Exception { assertConvertibleTo("1", JetStandardTypes.getByte()); }