From b1df4a00451b070d159e447f8155839dccef73a3 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Wed, 9 Nov 2011 14:51:04 +0400 Subject: [PATCH 01/14] KT-419 Strange 'unresolved' bug when using constructor parameters that aren't properties === class A(w: Int) { var c = w { c = 17 } } === --- .../org/jetbrains/jet/lang/resolve/BodyResolver.java | 2 +- .../quick/AnonymousInitializerVarAndConstructor.jet | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/checkerWithErrorTypes/quick/AnonymousInitializerVarAndConstructor.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index 8f45d29fc0e..b5f70ccfbe2 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -76,8 +76,8 @@ public class BodyResolver { resolveDelegationSpecifierLists(); resolveClassAnnotations(); - resolveAnonymousInitializers(); resolvePropertyDeclarationBodies(); + resolveAnonymousInitializers(); resolveSecondaryConstructorBodies(); resolveFunctionBodies(); diff --git a/compiler/testData/checkerWithErrorTypes/quick/AnonymousInitializerVarAndConstructor.jet b/compiler/testData/checkerWithErrorTypes/quick/AnonymousInitializerVarAndConstructor.jet new file mode 100644 index 00000000000..a84fbeee48f --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/quick/AnonymousInitializerVarAndConstructor.jet @@ -0,0 +1,10 @@ +// http://youtrack.jetbrains.net/issue/KT-419 + +class A(w: Int) { + var c = w + + { + c = 81 + } +} + From a4768eda982ed6fb175327a5e698b7185fc1af5f Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Nov 2011 15:21:17 +0300 Subject: [PATCH 02/14] TypeInfo should be covariant. Consider the common case in Java when something wants, say, an annotation class. They write Class. We can write just TypeInfo --- compiler/frontend/src/jet/Library.jet | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/frontend/src/jet/Library.jet b/compiler/frontend/src/jet/Library.jet index 4012dbc5795..67df91c00bd 100644 --- a/compiler/frontend/src/jet/Library.jet +++ b/compiler/frontend/src/jet/Library.jet @@ -1,13 +1,13 @@ namespace jet namespace typeinfo { - class TypeInfo { + class TypeInfo { fun isSubtypeOf(other : TypeInfo<*>) : Boolean fun isInstance(obj : Any?) : Boolean } fun typeinfo() : TypeInfo - fun typeinfo(expression : T) : TypeInfo + fun typeinfo(expression : T) : TypeInfo } namespace io { From df79fa2f3dd23d5913990a6a7cbb44fe72838f8e Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Nov 2011 15:22:25 +0300 Subject: [PATCH 03/14] Type checking procedure simplified (dramatically!) --- .../jet/lang/types/JetTypeChecker.java | 235 +++++++++--------- .../quick/regressions/OutProjections.jet | 21 ++ 2 files changed, 134 insertions(+), 122 deletions(-) create mode 100644 compiler/testData/checkerWithErrorTypes/quick/regressions/OutProjections.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index d3a36c2652c..3b8ea6843e6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -7,6 +7,9 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import java.util.*; +import static org.jetbrains.jet.lang.types.Variance.IN_VARIANCE; +import static org.jetbrains.jet.lang.types.Variance.OUT_VARIANCE; + /** * @author abreslav */ @@ -76,17 +79,20 @@ public class JetTypeChecker { public JetType commonSupertype(@NotNull Collection types) { Collection typeSet = new HashSet(types); assert !typeSet.isEmpty(); + + // If any of the types is nullable, the result must be nullable + // This also removed Nothing and Nothing? because they are subtypes of everything else boolean nullable = false; for (Iterator iterator = typeSet.iterator(); iterator.hasNext();) { JetType type = iterator.next(); assert type != null; - // TODO : This admits 'Nothing?'. Review if (JetStandardClasses.isNothingOrNullableNothing(type)) { iterator.remove(); } nullable |= type.isNullable(); } + // Everything deleted => it's Nothing or Nothing? if (typeSet.isEmpty()) { // TODO : attributes return nullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType(); @@ -96,6 +102,7 @@ public class JetTypeChecker { return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable); } + // constructor of the supertype -> all of its instantiations occurring as supertypes Map> commonSupertypes = computeCommonRawSupertypes(typeSet); while (commonSupertypes.size() > 1) { Set merge = new HashSet(); @@ -105,12 +112,81 @@ public class JetTypeChecker { commonSupertypes = computeCommonRawSupertypes(merge); } assert !commonSupertypes.isEmpty() : commonSupertypes + " <- " + types; - Map.Entry> entry = commonSupertypes.entrySet().iterator().next(); - JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue()); + // constructor of the supertype -> all of its instantiations occurring as supertypes + Map.Entry> entry = commonSupertypes.entrySet().iterator().next(); + + // Reconstructing type arguments if possible + JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue()); return TypeUtils.makeNullableIfNeeded(result, nullable); } + // Raw supertypes are superclasses w/o type arguments + // @return TypeConstructor -> all instantiations of this constructor occurring as supertypes + @NotNull + private Map> computeCommonRawSupertypes(@NotNull Collection types) { + assert !types.isEmpty(); + + final Map> constructorToAllInstances = new HashMap>(); + Set commonSuperclasses = null; + + List order = null; + for (JetType type : types) { + Set visited = new HashSet(); + + order = dfs(type, visited, new DfsNodeHandler>() { + public LinkedList list = new LinkedList(); + + @Override + public void beforeChildren(JetType current) { + TypeConstructor constructor = current.getConstructor(); + + Set instances = constructorToAllInstances.get(constructor); + if (instances == null) { + instances = new HashSet(); + constructorToAllInstances.put(constructor, instances); + } + instances.add(current); + } + + @Override + public void afterChildren(JetType current) { + list.addFirst(current.getConstructor()); + } + + @Override + public List result() { + return list; + } + }); + + if (commonSuperclasses == null) { + commonSuperclasses = visited; + } + else { + commonSuperclasses.retainAll(visited); + } + } + assert order != null; + + Set notSource = new HashSet(); + Map> result = new HashMap>(); + for (TypeConstructor superConstructor : order) { + if (!commonSuperclasses.contains(superConstructor)) { + continue; + } + + if (!notSource.contains(superConstructor)) { + result.put(superConstructor, constructorToAllInstances.get(superConstructor)); + markAll(superConstructor, notSource); + } + } + + return result; + } + + // constructor - type constructor of a supertype to be instantiated + // types - instantiations of constructor occurring as supertypes of classes we are trying to intersect @NotNull private JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set types) { // we assume that all the given types are applications of the same type constructor @@ -186,83 +262,21 @@ public class JetTypeChecker { JetType intersection = TypeUtils.intersect(this, ins); if (intersection == null) { if (outs != null) { - return new TypeProjection(Variance.OUT_VARIANCE, commonSupertype(outs)); + return new TypeProjection(OUT_VARIANCE, commonSupertype(outs)); } - return new TypeProjection(Variance.OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds())); + return new TypeProjection(OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds())); } - Variance projectionKind = variance == Variance.IN_VARIANCE ? Variance.INVARIANT : Variance.IN_VARIANCE; + Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE; return new TypeProjection(projectionKind, intersection); } else if (outs != null) { - Variance projectionKind = variance == Variance.OUT_VARIANCE ? Variance.INVARIANT : Variance.OUT_VARIANCE; + Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE; return new TypeProjection(projectionKind, commonSupertype(outs)); } else { - Variance projectionKind = variance == Variance.OUT_VARIANCE ? Variance.INVARIANT : Variance.OUT_VARIANCE; + Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE; return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds())); } } - @NotNull - private Map> computeCommonRawSupertypes(@NotNull Collection types) { - assert !types.isEmpty(); - - final Map> constructorToAllInstances = new HashMap>(); - Set commonSuperclasses = null; - - List order = null; - for (JetType type : types) { - Set visited = new HashSet(); - - order = dfs(type, visited, new DfsNodeHandler>() { - public LinkedList list = new LinkedList(); - - @Override - public void beforeChildren(JetType current) { - TypeConstructor constructor = current.getConstructor(); - - Set instances = constructorToAllInstances.get(constructor); - if (instances == null) { - instances = new HashSet(); - constructorToAllInstances.put(constructor, instances); - } - instances.add(current); - } - - @Override - public void afterChildren(JetType current) { - list.addFirst(current.getConstructor()); - } - - @Override - public List result() { - return list; - } - }); - - if (commonSuperclasses == null) { - commonSuperclasses = visited; - } - else { - commonSuperclasses.retainAll(visited); - } - } - assert order != null; - - Set notSource = new HashSet(); - Map> result = new HashMap>(); - for (TypeConstructor superConstructor : order) { - if (!commonSuperclasses.contains(superConstructor)) { - continue; - } - - if (!notSource.contains(superConstructor)) { - result.put(superConstructor, constructorToAllInstances.get(superConstructor)); - markAll(superConstructor, notSource); - } - } - - return result; - } - private void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set markerSet) { markerSet.add(typeConstructor); for (JetType type : typeConstructor.getSupertypes()) { @@ -331,22 +345,28 @@ public class JetTypeChecker { } public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) { - return new TypeCheckingProcedure().run(subtype, supertype); +// return new TypeCheckingProcedure().run(subtype, supertype); + return new ExplicitInOutTypeCheckingProcedure().run(subtype, supertype); } public boolean equalTypes(@NotNull JetType a, @NotNull JetType b) { return isSubtypeOf(a, b) && isSubtypeOf(b, a); } - private static class OldProcedure { - public static boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) { + private static class ExplicitInOutTypeCheckingProcedure { + + public boolean run(@NotNull JetType subtype, @NotNull JetType supertype) { + return isSubtypeOf(subtype, supertype); + } + + 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; } - if (JetStandardClasses.isNothing(subtype)) { + if (JetStandardClasses.isNothingOrNullableNothing(subtype)) { return true; } @Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype); @@ -357,70 +377,41 @@ public class JetTypeChecker { return checkSubtypeForTheSameConstructor(closestSupertype, supertype); } - private static boolean checkSubtypeForTheSameConstructor(@NotNull JetType subtype, @NotNull JetType 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 subArguments = subtype.getArguments(); List superArguments = supertype.getArguments(); List parameters = constructor.getParameters(); - boolean status = true; 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 subArgumentType = subArgument.getType(); - JetType superArgumentType = superArgument.getType(); - switch (parameter.getVariance()) { - case INVARIANT: - switch (superArgument.getProjectionKind()) { - case INVARIANT: - status = subArgumentType.equals(superArgumentType); - break; - case OUT_VARIANCE: - if (!subArgument.getProjectionKind().allowsOutPosition()) { - status = false; - } else { - status = !isSubtypeOf(subArgumentType, superArgumentType); - } - break; - case IN_VARIANCE: - if (!subArgument.getProjectionKind().allowsInPosition()) { - status = false; - } else { - status = isSubtypeOf(superArgumentType, subArgumentType); - } - break; - } - break; - case IN_VARIANCE: - switch (superArgument.getProjectionKind()) { - case INVARIANT: - case IN_VARIANCE: - status = isSubtypeOf(superArgumentType, subArgumentType); - break; - case OUT_VARIANCE: - status = isSubtypeOf(subArgumentType, superArgumentType); - break; - } - break; - case OUT_VARIANCE: - switch (superArgument.getProjectionKind()) { - case INVARIANT: - case OUT_VARIANCE: - case IN_VARIANCE: - status = isSubtypeOf(subArgumentType, superArgumentType); - break; - } - break; - } - if (!status) { + JetType superIn = getInType(parameter, superArgument); + JetType superOut = getOutType(parameter, superArgument); + + if (!isSubtypeOf(subOut, superOut) || !isSubtypeOf(superIn, subIn)) { return false; } } return true; } + + private JetType getOutType(TypeParameterDescriptor parameter, TypeProjection subArgument) { + boolean isOutProjected = subArgument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE; + return isOutProjected ? parameter.getBoundsAsType() : subArgument.getType(); + } + + private JetType getInType(TypeParameterDescriptor parameter, TypeProjection subArgument) { + boolean isOutProjected = subArgument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE; + return isOutProjected ? JetStandardClasses.getNothingType() : subArgument.getType(); + } } public static abstract class AbstractTypeCheckingProcedure { diff --git a/compiler/testData/checkerWithErrorTypes/quick/regressions/OutProjections.jet b/compiler/testData/checkerWithErrorTypes/quick/regressions/OutProjections.jet new file mode 100644 index 00000000000..f6c32d8f776 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/quick/regressions/OutProjections.jet @@ -0,0 +1,21 @@ +class Point() { +} + +class G() {} + +fun f(expression : T) : G = G + + +fun foo() : G { + val p = Point() + return f(p) +} + +class Out() {} + +fun fout(expression : T) : Out = Out + +fun fooout() : Out { + val p = Point(); + return fout(p); +} \ No newline at end of file From a6898c7c64f5ece2efc28353852ecacb22002bfc Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Wed, 9 Nov 2011 16:21:11 +0400 Subject: [PATCH 04/14] simplify KT-249 for KT-449 --- .../testData/codegen/regressions/kt249.jet | 29 +++---------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/compiler/testData/codegen/regressions/kt249.jet b/compiler/testData/codegen/regressions/kt249.jet index d4f61650fd6..d0c2e520999 100644 --- a/compiler/testData/codegen/regressions/kt249.jet +++ b/compiler/testData/codegen/regressions/kt249.jet @@ -1,35 +1,14 @@ namespace x -class Outer(val name: String) { - class Inner() { - val me = name - - class object { - fun bar() = "bar" - } - } +class Outer() { class object { - class StaticInner() { - class object { - fun f() = "oo" - } + class Inner() { } } } fun box (): String { - val inner = Outer("mama").Inner() //verify error - if(inner.me != "mama") - return "fail" - - val outer = Outer ("papa") - val inner2 = outer.Inner () - if(inner2.me != "papa") - return "fail" - - if(Outer.StaticInner.f() != "oo" ) - return "fail" - + val inner = Outer.Inner() return "OK" -} \ No newline at end of file +} From 099725da7f073852824c882e184103665b50caa0 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Wed, 9 Nov 2011 16:21:19 +0400 Subject: [PATCH 05/14] KT-449 class object in inner class must be an error === class A { class B { class object { } } } === --- .../jet/lang/descriptors/MutableClassDescriptor.java | 3 +++ .../checkerWithErrorTypes/quick/InnerClassClassObject.jet | 7 +++++++ 2 files changed, 10 insertions(+) create mode 100644 compiler/testData/checkerWithErrorTypes/quick/InnerClassClassObject.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java index d9a993b4155..3e500172016 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java @@ -61,6 +61,9 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme @Override public ClassObjectStatus setClassObjectDescriptor(@NotNull MutableClassDescriptor classObjectDescriptor) { if (this.classObjectDescriptor != null) return ClassObjectStatus.DUPLICATE; + if (!(this.getContainingDeclaration() instanceof NamespaceDescriptor)) { + return ClassObjectStatus.NOT_ALLOWED; + } assert classObjectDescriptor.getKind() == ClassKind.OBJECT; this.classObjectDescriptor = classObjectDescriptor; return ClassObjectStatus.OK; diff --git a/compiler/testData/checkerWithErrorTypes/quick/InnerClassClassObject.jet b/compiler/testData/checkerWithErrorTypes/quick/InnerClassClassObject.jet new file mode 100644 index 00000000000..0d8f5da30f2 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/quick/InnerClassClassObject.jet @@ -0,0 +1,7 @@ +// http://youtrack.jetbrains.net/issue/KT-449 + +class A { + class B { + class object { } + } +} From 0a2ad80234371c3614ed7ebd11110010ba14656a Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Nov 2011 16:53:33 +0300 Subject: [PATCH 06/14] Unused parameter removed --- .../src/org/jetbrains/jet/lang/JetSemanticServices.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java b/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java index 2aff00b8719..ec95ba62e98 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java @@ -2,7 +2,6 @@ package org.jetbrains.jet.lang; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.ClassDescriptorResolver; import org.jetbrains.jet.lang.types.JetStandardLibrary; @@ -14,17 +13,17 @@ import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices; */ public class JetSemanticServices { public static JetSemanticServices createSemanticServices(JetStandardLibrary standardLibrary) { - return new JetSemanticServices(standardLibrary, JetControlFlowDataTraceFactory.EMPTY); + return new JetSemanticServices(standardLibrary); } public static JetSemanticServices createSemanticServices(Project project) { - return new JetSemanticServices(JetStandardLibrary.getJetStandardLibrary(project), JetControlFlowDataTraceFactory.EMPTY); + return new JetSemanticServices(JetStandardLibrary.getJetStandardLibrary(project)); } private final JetStandardLibrary standardLibrary; private final JetTypeChecker typeChecker; - private JetSemanticServices(JetStandardLibrary standardLibrary, JetControlFlowDataTraceFactory flowDataTraceFactory) { + private JetSemanticServices(JetStandardLibrary standardLibrary) { this.standardLibrary = standardLibrary; this.typeChecker = new JetTypeChecker(standardLibrary); } From d4ea1d42c8e5972ba6107cdc038b529c0fcfe46f Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Nov 2011 16:56:05 +0300 Subject: [PATCH 07/14] CommonSupertypes factored out equalTypes() moved to TypeUtils Lower bounds (stub implementation) introduced to type parameters --- .../jetbrains/jet/codegen/JetTypeMapper.java | 2 +- .../jet/lang/JetSemanticServices.java | 2 +- .../descriptors/TypeParameterDescriptor.java | 42 +- .../lang/resolve/ClassDescriptorResolver.java | 6 +- .../jet/lang/resolve/OverridingUtil.java | 2 +- .../jet/lang/resolve/TypeResolver.java | 4 +- .../jet/lang/resolve/calls/CallResolver.java | 9 +- .../calls/OverloadingConflictResolver.java | 13 +- .../calls/autocasts/DataFlowValueFactory.java | 5 +- .../jet/lang/types/CommonSupertypes.java | 262 +++++++++++ .../jet/lang/types/JetTypeChecker.java | 351 +-------------- .../jet/lang/types/TypeSubstitutor.java | 2 +- .../jetbrains/jet/lang/types/TypeUtils.java | 5 +- .../BasicExpressionTypingVisitor.java | 2 +- .../ControlStructureTypingVisitor.java | 14 +- .../expressions/ExpressionTypingServices.java | 2 +- .../expressions/ExpressionTypingUtils.java | 3 +- .../PatternMatchingTypingVisitor.java | 2 +- .../types/inference/ConstraintSystem.java | 409 +---------------- .../inference/ConstraintSystemSolution.java | 18 + .../EqualityBasedConstraintSystem.java | 55 +++ .../SubtypingOnlyConstraintSystem.java | 415 ++++++++++++++++++ .../jet/types/JetTypeCheckerTest.java | 2 +- 23 files changed, 832 insertions(+), 795 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/types/CommonSupertypes.java create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemSolution.java create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/types/inference/EqualityBasedConstraintSystem.java create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/types/inference/SubtypingOnlyConstraintSystem.java diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java index 8a767ebdb45..6b32d78a506 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java @@ -248,7 +248,7 @@ public class JetTypeMapper { } if (descriptor instanceof TypeParameterDescriptor) { - return mapType(((TypeParameterDescriptor) descriptor).getBoundsAsType(), kind); + return mapType(((TypeParameterDescriptor) descriptor).getUpperBoundsAsType(), kind); } throw new UnsupportedOperationException("Unknown type " + jetType); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java b/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java index ec95ba62e98..5a73728db37 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/JetSemanticServices.java @@ -25,7 +25,7 @@ public class JetSemanticServices { private JetSemanticServices(JetStandardLibrary standardLibrary) { this.standardLibrary = standardLibrary; - this.typeChecker = new JetTypeChecker(standardLibrary); + this.typeChecker = JetTypeChecker.INSTANCE; } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java index 695686b10b0..536d0e11600 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java @@ -43,7 +43,7 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement private final int index; private final Variance variance; private final Set upperBounds; - private JetType boundsAsType; + private JetType upperBoundsAsType; private final TypeConstructor typeConstructor; private JetType defaultType; private final Set classObjectUpperBounds = Sets.newLinkedHashSet(); @@ -85,10 +85,35 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement upperBounds.add(bound); // TODO : Duplicates? } + @NotNull public Set getUpperBounds() { return upperBounds; } + @NotNull + public JetType getUpperBoundsAsType() { + if (upperBoundsAsType == null) { + assert upperBounds != null : "Upper bound list is null in " + getName(); + assert upperBounds.size() > 0 : "Upper bound list is empty in " + getName(); + upperBoundsAsType = TypeUtils.intersect(JetTypeChecker.INSTANCE, upperBounds); + if (upperBoundsAsType == null) { + upperBoundsAsType = JetStandardClasses.getNothingType(); + } + } + return upperBoundsAsType; + } + + @NotNull + public Set getLowerBounds() { + return Collections.singleton(JetStandardClasses.getNothingType()); + } + + @NotNull + public JetType getLowerBoundsAsType() { + return JetStandardClasses.getNothingType(); + } + + @NotNull @Override public TypeConstructor getTypeConstructor() { @@ -100,19 +125,6 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement return DescriptorRenderer.TEXT.render(this); } - @NotNull - public JetType getBoundsAsType() { - if (boundsAsType == null) { - assert upperBounds != null : "Upper bound list is null in " + getName(); - assert upperBounds.size() > 0 : "Upper bound list is empty in " + getName(); - boundsAsType = TypeUtils.intersect(JetTypeChecker.INSTANCE, upperBounds); - if (boundsAsType == null) { - boundsAsType = JetStandardClasses.getNothingType(); - } - } - return boundsAsType; - } - @NotNull @Override @Deprecated // Use the static method TypeParameterDescriptor.substitute() @@ -137,7 +149,7 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement new LazyScopeAdapter(new LazyValue() { @Override protected JetScope compute() { - return getBoundsAsType().getMemberScope(); + return getUpperBoundsAsType().getMemberScope(); } })); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index c5ded6ed64e..f5af5a3b92a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -379,7 +379,7 @@ public class ClassDescriptorResolver { parameter.addUpperBound(JetStandardClasses.getDefaultBound()); } - if (JetStandardClasses.isNothing(parameter.getBoundsAsType())) { + if (JetStandardClasses.isNothing(parameter.getUpperBoundsAsType())) { PsiElement nameIdentifier = typeParameters.get(parameter.getIndex()).getNameIdentifier(); if (nameIdentifier != null) { // trace.getErrorHandler().genericError(nameIdentifier.getNode(), "Upper bounds of " + parameter.getName() + " have empty intersection"); @@ -666,7 +666,7 @@ public class ClassDescriptorResolver { type = typeResolver.resolveType(scope, typeReference); JetType inType = propertyDescriptor.getInType(); if (inType != null) { - if (!semanticServices.getTypeChecker().equalTypes(type, inType)) { + if (!TypeUtils.equalTypes(type, inType)) { // trace.getErrorHandler().genericError(typeReference.getNode(), "Setter parameter type must be equal to the type of the property, i.e. " + inType); trace.report(WRONG_SETTER_PARAMETER_TYPE.on(setter, typeReference, inType)); } @@ -709,7 +709,7 @@ public class ClassDescriptorResolver { JetTypeReference returnTypeReference = getter.getReturnTypeReference(); if (returnTypeReference != null) { returnType = typeResolver.resolveType(scope, returnTypeReference); - if (outType != null && !semanticServices.getTypeChecker().equalTypes(returnType, outType)) { + if (outType != null && !TypeUtils.equalTypes(returnType, outType)) { // trace.getErrorHandler().genericError(returnTypeReference.getNode(), "Getter return type must be equal to the type of the property, i.e. " + propertyDescriptor.getReturnType()); trace.report(WRONG_GETTER_RETURN_TYPE.on(getter, returnTypeReference, propertyDescriptor.getReturnType())); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverridingUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverridingUtil.java index bc91949cc9b..53b95a441f4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverridingUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverridingUtil.java @@ -127,7 +127,7 @@ public class OverridingUtil { TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i); TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i); - if (!JetTypeImpl.equalTypes(superTypeParameter.getBoundsAsType(), subTypeParameter.getBoundsAsType(), axioms)) { + if (!JetTypeImpl.equalTypes(superTypeParameter.getUpperBoundsAsType(), subTypeParameter.getUpperBoundsAsType(), axioms)) { return OverrideCompatibilityInfo.boundsMismatch(superTypeParameter, subTypeParameter); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeResolver.java index 0ca6b450603..012932b4653 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeResolver.java @@ -181,13 +181,13 @@ public class TypeResolver { private JetScope getScopeForTypeParameter(final TypeParameterDescriptor typeParameterDescriptor) { if (checkBounds) { - return typeParameterDescriptor.getBoundsAsType().getMemberScope(); + return typeParameterDescriptor.getUpperBoundsAsType().getMemberScope(); } else { return new LazyScopeAdapter(new LazyValue() { @Override protected JetScope compute() { - return typeParameterDescriptor.getBoundsAsType().getMemberScope(); + return typeParameterDescriptor.getUpperBoundsAsType().getMemberScope(); } }); } 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 3817321a726..1d95508cfe9 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 @@ -9,7 +9,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.JetSemanticServices; import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl; @@ -21,6 +20,8 @@ import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices; import org.jetbrains.jet.lang.types.expressions.OperatorConventions; import org.jetbrains.jet.lang.types.inference.ConstraintSystem; +import org.jetbrains.jet.lang.types.inference.ConstraintSystemSolution; +import org.jetbrains.jet.lang.types.inference.SubtypingOnlyConstraintSystem; import org.jetbrains.jet.lexer.JetTokens; import java.util.*; @@ -413,7 +414,7 @@ public class CallResolver { if (!candidate.getTypeParameters().isEmpty()) { // Type argument inference - ConstraintSystem constraintSystem = new ConstraintSystem(); + ConstraintSystem constraintSystem = new SubtypingOnlyConstraintSystem(); for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) { constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO } @@ -451,7 +452,7 @@ public class CallResolver { constraintSystem.addSubtypingConstraint(candidate.getReturnType(), expectedType); } - ConstraintSystem.Solution solution = constraintSystem.solve(); + ConstraintSystemSolution solution = constraintSystem.solve(); // solutions.put(candidate, solution); if (solution.isSuccessful()) { D substitute = (D) candidate.substitute(solution.getSubstitutor()); @@ -884,7 +885,7 @@ public class CallResolver { for (int i = 0; i < valueParameters.size(); i++) { ValueParameterDescriptor valueParameter = valueParameters.get(i); JetType expectedType = parameterTypes.get(i); - if (!semanticServices.getTypeChecker().equalTypes(expectedType, valueParameter.getOutType())) return false; + if (!TypeUtils.equalTypes(expectedType, valueParameter.getOutType())) return false; } return true; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/OverloadingConflictResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/OverloadingConflictResolver.java index 4758dfd54af..aa0f1c3ad95 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/OverloadingConflictResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/OverloadingConflictResolver.java @@ -12,6 +12,7 @@ import org.jetbrains.jet.lang.resolve.OverridingUtil; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.JetStandardLibrary; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeUtils; import java.util.List; import java.util.Set; @@ -123,13 +124,13 @@ public class OverloadingConflictResolver { JetType _byte = standardLibrary.getByteType(); JetType _short = standardLibrary.getShortType(); - if (semanticServices.getTypeChecker().equalTypes(specific, _double) && semanticServices.getTypeChecker().equalTypes(general, _float)) return true; - if (semanticServices.getTypeChecker().equalTypes(specific, _int)) { - if (semanticServices.getTypeChecker().equalTypes(general, _long)) return true; - if (semanticServices.getTypeChecker().equalTypes(general, _byte)) return true; - if (semanticServices.getTypeChecker().equalTypes(general, _short)) return true; + if (TypeUtils.equalTypes(specific, _double) && TypeUtils.equalTypes(general, _float)) return true; + if (TypeUtils.equalTypes(specific, _int)) { + if (TypeUtils.equalTypes(general, _long)) return true; + if (TypeUtils.equalTypes(general, _byte)) return true; + if (TypeUtils.equalTypes(general, _short)) return true; } - if (semanticServices.getTypeChecker().equalTypes(specific, _short) && semanticServices.getTypeChecker().equalTypes(general, _byte)) return true; + if (TypeUtils.equalTypes(specific, _short) && TypeUtils.equalTypes(general, _byte)) return true; return false; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java index 4b7a7b77b4e..6c634b3f81c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java @@ -2,7 +2,6 @@ package org.jetbrains.jet.lang.resolve.calls.autocasts; import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.JetNodeTypes; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; @@ -11,7 +10,7 @@ import org.jetbrains.jet.lang.resolve.JetModuleUtil; import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiverDescriptor; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.lang.types.JetTypeChecker; +import org.jetbrains.jet.lang.types.TypeUtils; import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET; @@ -29,7 +28,7 @@ public class DataFlowValueFactory { JetConstantExpression constantExpression = (JetConstantExpression) expression; if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL; } - if (JetTypeChecker.INSTANCE.equalTypes(type, JetStandardClasses.getNullableNothingType())) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?' + if (TypeUtils.equalTypes(type, JetStandardClasses.getNullableNothingType())) return DataFlowValue.NULL; // 'null' is the only inhabitant of 'Nothing?' Pair result = getIdForStableIdentifier(expression, bindingContext, false); return new DataFlowValue(result.first == null ? expression : result.first, type, result.second, getImmanentNullability(type)); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/CommonSupertypes.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/CommonSupertypes.java new file mode 100644 index 00000000000..b656e962bd5 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/CommonSupertypes.java @@ -0,0 +1,262 @@ +package org.jetbrains.jet.lang.types; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; + +import java.util.*; + +import static org.jetbrains.jet.lang.types.Variance.IN_VARIANCE; +import static org.jetbrains.jet.lang.types.Variance.OUT_VARIANCE; + +/** +* @author abreslav +*/ +public class CommonSupertypes { + @NotNull + public static JetType commonSupertype(@NotNull Collection types) { + Collection typeSet = new HashSet(types); + assert !typeSet.isEmpty(); + + // If any of the types is nullable, the result must be nullable + // This also removed Nothing and Nothing? because they are subtypes of everything else + boolean nullable = false; + for (Iterator iterator = typeSet.iterator(); iterator.hasNext();) { + JetType type = iterator.next(); + assert type != null; + if (JetStandardClasses.isNothingOrNullableNothing(type)) { + iterator.remove(); + } + nullable |= type.isNullable(); + } + + // Everything deleted => it's Nothing or Nothing? + if (typeSet.isEmpty()) { + // TODO : attributes + return nullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType(); + } + + if (typeSet.size() == 1) { + return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable); + } + + // constructor of the supertype -> all of its instantiations occurring as supertypes + Map> commonSupertypes = computeCommonRawSupertypes(typeSet); + while (commonSupertypes.size() > 1) { + Set merge = new HashSet(); + for (Set supertypes : commonSupertypes.values()) { + merge.addAll(supertypes); + } + commonSupertypes = computeCommonRawSupertypes(merge); + } + assert !commonSupertypes.isEmpty() : commonSupertypes + " <- " + types; + + // constructor of the supertype -> all of its instantiations occurring as supertypes + Map.Entry> entry = commonSupertypes.entrySet().iterator().next(); + + // Reconstructing type arguments if possible + JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue()); + return TypeUtils.makeNullableIfNeeded(result, nullable); + } + + // Raw supertypes are superclasses w/o type arguments + // @return TypeConstructor -> all instantiations of this constructor occurring as supertypes + @NotNull + private static Map> computeCommonRawSupertypes(@NotNull Collection types) { + assert !types.isEmpty(); + + final Map> constructorToAllInstances = new HashMap>(); + Set commonSuperclasses = null; + + List order = null; + for (JetType type : types) { + Set visited = new HashSet(); + + order = dfs(type, visited, new DfsNodeHandler>() { + public LinkedList list = new LinkedList(); + + @Override + public void beforeChildren(JetType current) { + TypeConstructor constructor = current.getConstructor(); + + Set instances = constructorToAllInstances.get(constructor); + if (instances == null) { + instances = new HashSet(); + constructorToAllInstances.put(constructor, instances); + } + instances.add(current); + } + + @Override + public void afterChildren(JetType current) { + list.addFirst(current.getConstructor()); + } + + @Override + public List result() { + return list; + } + }); + + if (commonSuperclasses == null) { + commonSuperclasses = visited; + } + else { + commonSuperclasses.retainAll(visited); + } + } + assert order != null; + + Set notSource = new HashSet(); + Map> result = new HashMap>(); + for (TypeConstructor superConstructor : order) { + if (!commonSuperclasses.contains(superConstructor)) { + continue; + } + + if (!notSource.contains(superConstructor)) { + result.put(superConstructor, constructorToAllInstances.get(superConstructor)); + markAll(superConstructor, notSource); + } + } + + return result; + } + + // constructor - type constructor of a supertype to be instantiated + // types - instantiations of constructor occurring as supertypes of classes we are trying to intersect + @NotNull + private static JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set types) { + // we assume that all the given types are applications of the same type constructor + + assert !types.isEmpty(); + + if (types.size() == 1) { + return types.iterator().next(); + } + + List parameters = constructor.getParameters(); + List newProjections = new ArrayList(); + for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) { + TypeParameterDescriptor parameterDescriptor = parameters.get(i); + Set typeProjections = new HashSet(); + for (JetType type : types) { + typeProjections.add(type.getArguments().get(i)); + } + newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections)); + } + + boolean nullable = false; + for (JetType type : types) { + nullable |= type.isNullable(); + } + + // TODO : attributes? + return new JetTypeImpl(Collections.emptyList(), constructor, nullable, newProjections, JetStandardClasses.STUB); // TODO : scope + } + + @NotNull + private static TypeProjection computeSupertypeProjection(@NotNull TypeParameterDescriptor parameterDescriptor, @NotNull Set typeProjections) { + if (typeProjections.size() == 1) { + return typeProjections.iterator().next(); + } + + Set ins = new HashSet(); + Set outs = new HashSet(); + + Variance variance = parameterDescriptor.getVariance(); + switch (variance) { + case INVARIANT: + // Nothing + break; + case IN_VARIANCE: + outs = null; + break; + case OUT_VARIANCE: + ins = null; + break; + } + + for (TypeProjection projection : typeProjections) { + Variance projectionKind = projection.getProjectionKind(); + if (projectionKind.allowsInPosition()) { + if (ins != null) { + ins.add(projection.getType()); + } + } else { + ins = null; + } + + if (projectionKind.allowsOutPosition()) { + if (outs != null) { + outs.add(projection.getType()); + } + } else { + outs = null; + } + } + + if (ins != null) { + JetType intersection = TypeUtils.intersect(JetTypeChecker.INSTANCE, ins); + if (intersection == null) { + if (outs != null) { + return new TypeProjection(OUT_VARIANCE, commonSupertype(outs)); + } + return new TypeProjection(OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds())); + } + Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE; + return new TypeProjection(projectionKind, intersection); + } else if (outs != null) { + Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE; + return new TypeProjection(projectionKind, commonSupertype(outs)); + } else { + Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE; + return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds())); + } + } + + private static void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set markerSet) { + markerSet.add(typeConstructor); + for (JetType type : typeConstructor.getSupertypes()) { + markAll(type.getConstructor(), markerSet); + } + } + + private static R dfs(@NotNull JetType current, @NotNull Set visited, @NotNull DfsNodeHandler handler) { + doDfs(current, visited, handler); + return handler.result(); + } + + private static void doDfs(@NotNull JetType current, @NotNull Set visited, @NotNull DfsNodeHandler handler) { + if (!visited.add(current.getConstructor())) { + return; + } + handler.beforeChildren(current); +// Map substitutionContext = TypeUtils.buildSubstitutionContext(current); + TypeSubstitutor substitutor = TypeSubstitutor.create(current); + for (JetType supertype : current.getConstructor().getSupertypes()) { + TypeConstructor supertypeConstructor = supertype.getConstructor(); + if (visited.contains(supertypeConstructor)) { + continue; + } + JetType substitutedSupertype = substitutor.safeSubstitute(supertype, Variance.INVARIANT); + dfs(substitutedSupertype, visited, handler); + } + handler.afterChildren(current); + } + + private static class DfsNodeHandler { + + public void beforeChildren(JetType current) { + + } + + public void afterChildren(JetType current) { + + } + + public R result() { + return null; + } + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index 3b8ea6843e6..38ee84c2f52 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -2,7 +2,6 @@ package org.jetbrains.jet.lang.types; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import java.util.*; @@ -15,316 +14,14 @@ import static org.jetbrains.jet.lang.types.Variance.OUT_VARIANCE; */ public class JetTypeChecker { - public static final JetTypeChecker INSTANCE = new JetTypeChecker(null); + public static final JetTypeChecker INSTANCE = new JetTypeChecker(); - private final Map> conversionMap = new HashMap>(); - private final JetStandardLibrary standardLibrary; - - public JetTypeChecker(JetStandardLibrary standardLibrary) { - this.standardLibrary = standardLibrary; + private JetTypeChecker() { } - @NotNull - private Map> getConversionMap() { -// if (conversionMap.size() == 0) { -// addConversion(standardLibrary.getByte(), -// standardLibrary.getShort(), -// standardLibrary.getInt(), -// standardLibrary.getLong(), -// standardLibrary.getFloat(), -// standardLibrary.getDouble()); -// -// addConversion(standardLibrary.getShort(), -// standardLibrary.getInt(), -// standardLibrary.getLong(), -// standardLibrary.getFloat(), -// standardLibrary.getDouble()); -// -// addConversion(standardLibrary.getChar(), -// standardLibrary.getInt(), -// standardLibrary.getLong(), -// standardLibrary.getFloat(), -// standardLibrary.getDouble()); -// -// addConversion(standardLibrary.getInt(), -// standardLibrary.getLong(), -// standardLibrary.getFloat(), -// standardLibrary.getDouble()); -// -// addConversion(standardLibrary.getLong(), -// standardLibrary.getFloat(), -// standardLibrary.getDouble()); -// -// addConversion(standardLibrary.getFloat(), -// standardLibrary.getDouble()); -// } - return conversionMap; - } - -// private void addConversion(ClassDescriptor actual, ClassDescriptor... convertedTo) { -// TypeConstructor[] constructors = new TypeConstructor[convertedTo.length]; -// for (int i = 0, convertedToLength = convertedTo.length; i < convertedToLength; i++) { -// ClassDescriptor classDescriptor = convertedTo[i]; -// constructors[i] = classDescriptor.getTypeConstructor(); -// } -// conversionMap.put(actual.getTypeConstructor(), new HashSet(Arrays.asList(constructors))); -// } -// - @NotNull - public JetType commonSupertype(@NotNull JetType... types) { - return commonSupertype(Arrays.asList(types)); - } - - @NotNull - public JetType commonSupertype(@NotNull Collection types) { - Collection typeSet = new HashSet(types); - assert !typeSet.isEmpty(); - - // If any of the types is nullable, the result must be nullable - // This also removed Nothing and Nothing? because they are subtypes of everything else - boolean nullable = false; - for (Iterator iterator = typeSet.iterator(); iterator.hasNext();) { - JetType type = iterator.next(); - assert type != null; - if (JetStandardClasses.isNothingOrNullableNothing(type)) { - iterator.remove(); - } - nullable |= type.isNullable(); - } - - // Everything deleted => it's Nothing or Nothing? - if (typeSet.isEmpty()) { - // TODO : attributes - return nullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType(); - } - - if (typeSet.size() == 1) { - return TypeUtils.makeNullableIfNeeded(typeSet.iterator().next(), nullable); - } - - // constructor of the supertype -> all of its instantiations occurring as supertypes - Map> commonSupertypes = computeCommonRawSupertypes(typeSet); - while (commonSupertypes.size() > 1) { - Set merge = new HashSet(); - for (Set supertypes : commonSupertypes.values()) { - merge.addAll(supertypes); - } - commonSupertypes = computeCommonRawSupertypes(merge); - } - assert !commonSupertypes.isEmpty() : commonSupertypes + " <- " + types; - - // constructor of the supertype -> all of its instantiations occurring as supertypes - Map.Entry> entry = commonSupertypes.entrySet().iterator().next(); - - // Reconstructing type arguments if possible - JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue()); - return TypeUtils.makeNullableIfNeeded(result, nullable); - } - - // Raw supertypes are superclasses w/o type arguments - // @return TypeConstructor -> all instantiations of this constructor occurring as supertypes - @NotNull - private Map> computeCommonRawSupertypes(@NotNull Collection types) { - assert !types.isEmpty(); - - final Map> constructorToAllInstances = new HashMap>(); - Set commonSuperclasses = null; - - List order = null; - for (JetType type : types) { - Set visited = new HashSet(); - - order = dfs(type, visited, new DfsNodeHandler>() { - public LinkedList list = new LinkedList(); - - @Override - public void beforeChildren(JetType current) { - TypeConstructor constructor = current.getConstructor(); - - Set instances = constructorToAllInstances.get(constructor); - if (instances == null) { - instances = new HashSet(); - constructorToAllInstances.put(constructor, instances); - } - instances.add(current); - } - - @Override - public void afterChildren(JetType current) { - list.addFirst(current.getConstructor()); - } - - @Override - public List result() { - return list; - } - }); - - if (commonSuperclasses == null) { - commonSuperclasses = visited; - } - else { - commonSuperclasses.retainAll(visited); - } - } - assert order != null; - - Set notSource = new HashSet(); - Map> result = new HashMap>(); - for (TypeConstructor superConstructor : order) { - if (!commonSuperclasses.contains(superConstructor)) { - continue; - } - - if (!notSource.contains(superConstructor)) { - result.put(superConstructor, constructorToAllInstances.get(superConstructor)); - markAll(superConstructor, notSource); - } - } - - return result; - } - - // constructor - type constructor of a supertype to be instantiated - // types - instantiations of constructor occurring as supertypes of classes we are trying to intersect - @NotNull - private JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set types) { - // we assume that all the given types are applications of the same type constructor - - assert !types.isEmpty(); - - if (types.size() == 1) { - return types.iterator().next(); - } - - List parameters = constructor.getParameters(); - List newProjections = new ArrayList(); - for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) { - TypeParameterDescriptor parameterDescriptor = parameters.get(i); - Set typeProjections = new HashSet(); - for (JetType type : types) { - typeProjections.add(type.getArguments().get(i)); - } - newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections)); - } - - boolean nullable = false; - for (JetType type : types) { - nullable |= type.isNullable(); - } - - // TODO : attributes? - return new JetTypeImpl(Collections.emptyList(), constructor, nullable, newProjections, JetStandardClasses.STUB); // TODO : scope - } - - @NotNull - private TypeProjection computeSupertypeProjection(@NotNull TypeParameterDescriptor parameterDescriptor, @NotNull Set typeProjections) { - if (typeProjections.size() == 1) { - return typeProjections.iterator().next(); - } - - Set ins = new HashSet(); - Set outs = new HashSet(); - - Variance variance = parameterDescriptor.getVariance(); - switch (variance) { - case INVARIANT: - // Nothing - break; - case IN_VARIANCE: - outs = null; - break; - case OUT_VARIANCE: - ins = null; - break; - } - - for (TypeProjection projection : typeProjections) { - Variance projectionKind = projection.getProjectionKind(); - if (projectionKind.allowsInPosition()) { - if (ins != null) { - ins.add(projection.getType()); - } - } else { - ins = null; - } - - if (projectionKind.allowsOutPosition()) { - if (outs != null) { - outs.add(projection.getType()); - } - } else { - outs = null; - } - } - - if (ins != null) { - JetType intersection = TypeUtils.intersect(this, ins); - if (intersection == null) { - if (outs != null) { - return new TypeProjection(OUT_VARIANCE, commonSupertype(outs)); - } - return new TypeProjection(OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds())); - } - Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE; - return new TypeProjection(projectionKind, intersection); - } else if (outs != null) { - Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE; - return new TypeProjection(projectionKind, commonSupertype(outs)); - } else { - Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE; - return new TypeProjection(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds())); - } - } - - private void markAll(@NotNull TypeConstructor typeConstructor, @NotNull Set markerSet) { - markerSet.add(typeConstructor); - for (JetType type : typeConstructor.getSupertypes()) { - markAll(type.getConstructor(), markerSet); - } - } - - private R dfs(@NotNull JetType current, @NotNull Set visited, @NotNull DfsNodeHandler handler) { - doDfs(current, visited, handler); - return handler.result(); - } - - private void doDfs(@NotNull JetType current, @NotNull Set visited, @NotNull DfsNodeHandler handler) { - if (!visited.add(current.getConstructor())) { - return; - } - handler.beforeChildren(current); -// Map substitutionContext = TypeUtils.buildSubstitutionContext(current); - TypeSubstitutor substitutor = TypeSubstitutor.create(current); - for (JetType supertype : current.getConstructor().getSupertypes()) { - TypeConstructor supertypeConstructor = supertype.getConstructor(); - if (visited.contains(supertypeConstructor)) { - continue; - } - JetType substitutedSupertype = substitutor.safeSubstitute(supertype, Variance.INVARIANT); - dfs(substitutedSupertype, visited, handler); - } - handler.afterChildren(current); - } - - public boolean isConvertibleTo(@NotNull JetType actual, @NotNull JetType expected) { - return isSubtypeOf(actual, expected) || - isConvertibleBySpecialConversion(actual, expected); - } - - public boolean isConvertibleBySpecialConversion(@NotNull JetType actual, @NotNull JetType expected) { - if (expected.getConstructor().equals(JetStandardClasses.getTuple(0).getTypeConstructor())) { - return true; - } -// if (actual.getValueArguments().isEmpty()) { -// TypeConstructor actualConstructor = actual.getConstructor(); -// TypeConstructor constructor = expected.getConstructor(); -// Set convertibleTo = getConversionMap().get(actualConstructor); -// if (convertibleTo != null) { -// return convertibleTo.contains(constructor); -// } -// } - return false; + public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) { +// return new TypeCheckingProcedure().run(subtype, supertype); + return new ExplicitInOutTypeCheckingProcedure().run(subtype, supertype); } // This method returns the supertype of the first parameter that has the same constructor @@ -344,13 +41,14 @@ public class JetTypeChecker { return null; } - public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) { -// return new TypeCheckingProcedure().run(subtype, supertype); - return new ExplicitInOutTypeCheckingProcedure().run(subtype, supertype); + private static JetType getOutType(TypeParameterDescriptor parameter, TypeProjection argument) { + boolean isOutProjected = argument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE; + return isOutProjected ? parameter.getUpperBoundsAsType() : argument.getType(); } - public boolean equalTypes(@NotNull JetType a, @NotNull JetType b) { - return isSubtypeOf(a, b) && isSubtypeOf(b, a); + 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 static class ExplicitInOutTypeCheckingProcedure { @@ -386,13 +84,12 @@ public class JetTypeChecker { List 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); @@ -402,16 +99,6 @@ public class JetTypeChecker { } return true; } - - private JetType getOutType(TypeParameterDescriptor parameter, TypeProjection subArgument) { - boolean isOutProjected = subArgument.getProjectionKind() == IN_VARIANCE || parameter.getVariance() == IN_VARIANCE; - return isOutProjected ? parameter.getBoundsAsType() : subArgument.getType(); - } - - private JetType getInType(TypeParameterDescriptor parameter, TypeProjection subArgument) { - boolean isOutProjected = subArgument.getProjectionKind() == OUT_VARIANCE || parameter.getVariance() == OUT_VARIANCE; - return isOutProjected ? JetStandardClasses.getNothingType() : subArgument.getType(); - } } public static abstract class AbstractTypeCheckingProcedure { @@ -590,19 +277,5 @@ public class JetTypeChecker { } } - private static class DfsNodeHandler { - - public void beforeChildren(JetType current) { - - } - - public void afterChildren(JetType current) { - - } - - public R result() { - return null; - } - } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java index d6a5094cacc..0093d9536df 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java @@ -216,7 +216,7 @@ public class TypeSubstitutor { case OUT_VARIANCE: if (projectionKindValue == Variance.IN_VARIANCE) { effectiveProjectionKindValue = Variance.INVARIANT; - effectiveTypeValue = correspondingTypeParameter.getBoundsAsType(); + effectiveTypeValue = correspondingTypeParameter.getUpperBoundsAsType(); } else { effectiveTypeValue = typeValue; 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 130e9b08eb1..cb6360ab21d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -352,7 +352,7 @@ public class TypeUtils { @NotNull public static TypeProjection makeStarProjection(@NotNull TypeParameterDescriptor parameterDescriptor) { - return new TypeProjection(Variance.OUT_VARIANCE, parameterDescriptor.getBoundsAsType()); + return new TypeProjection(Variance.OUT_VARIANCE, parameterDescriptor.getUpperBoundsAsType()); } private static void collectImmediateSupertypes(@NotNull JetType type, @NotNull Collection result) { @@ -424,4 +424,7 @@ public class TypeUtils { return false; } + public static boolean equalTypes(@NotNull JetType a, @NotNull JetType b) { + return JetTypeChecker.INSTANCE.isSubtypeOf(a, b) && JetTypeChecker.INSTANCE.isSubtypeOf(b, a); + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index f7d692635d4..1939b0192d0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -712,7 +712,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } if (rightType != null) { DataFlowUtils.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType); - return TypeUtils.makeNullableAsSpecified(context.semanticServices.getTypeChecker().commonSupertype(leftType, rightType), rightType.isNullable()); + return TypeUtils.makeNullableAsSpecified(CommonSupertypes.commonSupertype(Arrays.asList(leftType, rightType)), rightType.isNullable()); } } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java index c2ca77a049e..4bf50a63007 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java @@ -14,15 +14,9 @@ import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver; -import org.jetbrains.jet.lang.types.ErrorUtils; -import org.jetbrains.jet.lang.types.JetStandardClasses; -import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.lang.types.TypeUtils; +import org.jetbrains.jet.lang.types.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.resolve.BindingContext.*; @@ -98,7 +92,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { result = thenType; } else { - result = context.semanticServices.getTypeChecker().commonSupertype(Arrays.asList(thenType, elseType)); + result = CommonSupertypes.commonSupertype(Arrays.asList(thenType, elseType)); } boolean jumpInThen = thenType != null && JetStandardClasses.isNothing(thenType); @@ -361,7 +355,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { return null; } else { - return context.semanticServices.getTypeChecker().commonSupertype(types); + return CommonSupertypes.commonSupertype(types); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java index f47a527d75b..93a98b4ddae 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java @@ -86,7 +86,7 @@ public class ExpressionTypingServices { Collection types = typeMap.values(); return types.isEmpty() ? JetStandardClasses.getNothingType() - : semanticServices.getTypeChecker().commonSupertype(types); + : CommonSupertypes.commonSupertype(types); } public void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, @NotNull final JetType expectedReturnType) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java index 129f63bbf09..f9cdac37fcb 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java @@ -13,7 +13,6 @@ import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.TraceBasedRedeclarationHandler; -import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory; import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.types.JetStandardClasses; @@ -47,7 +46,7 @@ public class ExpressionTypingUtils { } public static boolean isBoolean(@NotNull JetSemanticServices semanticServices, @NotNull JetType type) { - return semanticServices.getTypeChecker().isConvertibleTo(type, semanticServices.getStandardLibrary().getBooleanType()); + return semanticServices.getTypeChecker().isSubtypeOf(type, semanticServices.getStandardLibrary().getBooleanType()); } public static boolean ensureBooleanResult(JetExpression operationSign, String name, JetType resultType, ExpressionTypingContext context) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java index 970198fe48d..6b8b8d0630c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java @@ -100,7 +100,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { } if (!expressionTypes.isEmpty()) { - return context.semanticServices.getTypeChecker().commonSupertype(expressionTypes); + return CommonSupertypes.commonSupertype(expressionTypes); } else if (expression.getEntries().isEmpty()) { // context.trace.getErrorHandler().genericError(expression.getNode(), "Entries required for when-expression"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystem.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystem.java index 175f6647891..20b47986c0b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystem.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystem.java @@ -1,413 +1,18 @@ package org.jetbrains.jet.lang.types.inference; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; -import org.jetbrains.jet.lang.types.*; - -import java.util.Map; -import java.util.Set; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.Variance; /** * @author abreslav */ -public class ConstraintSystem { +public interface ConstraintSystem { + void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance); -// private static final Supplier> SET_SUPPLIER = new Supplier>() { -// @Override -// public Set get() { -// return Sets.newHashSet(); -// } -// }; - - private static class LoopInTypeVariableConstraintsException extends RuntimeException { - private LoopInTypeVariableConstraintsException() { - } - - private LoopInTypeVariableConstraintsException(String message) { - super(message); - } - - private LoopInTypeVariableConstraintsException(String message, Throwable cause) { - super(message, cause); - } - - private LoopInTypeVariableConstraintsException(Throwable cause) { - super(cause); - } - } - - public static abstract class TypeValue { - private final Set upperBounds = Sets.newHashSet(); - private final Set lowerBounds = Sets.newHashSet(); - - @NotNull - public Set getUpperBounds() { - return upperBounds; - } - - @NotNull - public Set getLowerBounds() { - return lowerBounds; - } - - @Nullable - public abstract KnownType getValue(); - } - - private static class UnknownType extends TypeValue { - - private final TypeParameterDescriptor typeParameterDescriptor; - private final Variance positionVariance; - private KnownType value; - private boolean beingComputed = false; - - private UnknownType(TypeParameterDescriptor typeParameterDescriptor, Variance positionVariance) { - this.typeParameterDescriptor = typeParameterDescriptor; - this.positionVariance = positionVariance; - } - - @NotNull - public TypeParameterDescriptor getTypeParameterDescriptor() { - return typeParameterDescriptor; - } - - @Override - public KnownType getValue() { - if (beingComputed) { - throw new LoopInTypeVariableConstraintsException(); - } - if (value == null) { - JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; - beingComputed = true; - try { - if (positionVariance == Variance.IN_VARIANCE) { - // maximal solution - throw new UnsupportedOperationException(); - } - else { - // minimal solution - - Set lowerBounds = getLowerBounds(); - if (!lowerBounds.isEmpty()) { - Set types = getTypes(lowerBounds); - - JetType commonSupertype = typeChecker.commonSupertype(types); - for (TypeValue upperBound : getUpperBounds()) { - if (!typeChecker.isSubtypeOf(commonSupertype, upperBound.getValue().getType())) { - value = null; - } - } - - println("minimal solution from lowerbounds for " + this + " is " + commonSupertype); - value = new KnownType(commonSupertype); - } - else { - Set upperBounds = getUpperBounds(); - Set types = getTypes(upperBounds); - JetType intersect = TypeUtils.intersect(typeChecker, types); - - value = new KnownType(intersect); - } - } - } - finally { - beingComputed = false; - } - } - - return value; - } - - private Set getTypes(Set lowerBounds) { - Set types = Sets.newHashSet(); - for (TypeValue lowerBound : lowerBounds) { - types.add(lowerBound.getValue().getType()); - } - return types; - } - - @Override - public String toString() { - return "?" + typeParameterDescriptor; - } - - } - - private static class KnownType extends TypeValue { - - private final JetType type; - - public KnownType(@NotNull JetType type) { - this.type = type; - } - - @NotNull - public JetType getType() { - return type; - } - - @Override - public KnownType getValue() { - return this; - } - - @Override - public String toString() { - return type.toString(); - } - } - - private final Map knownTypes = Maps.newHashMap(); - private final Map unknownTypes = Maps.newHashMap(); - private final JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; + void addSubtypingConstraint(@NotNull JetType lower, @NotNull JetType upper); @NotNull - private TypeValue getTypeValueFor(@NotNull JetType type) { - DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); - if (declarationDescriptor instanceof TypeParameterDescriptor) { - TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor; - UnknownType unknownType = unknownTypes.get(typeParameterDescriptor); - if (unknownType != null) { - return unknownType; - } - } - - KnownType typeValue = knownTypes.get(type); - if (typeValue == null) { - typeValue = new KnownType(type); - knownTypes.put(type, typeValue); - } - return typeValue; - } - - public void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance) { - assert !unknownTypes.containsKey(typeParameterDescriptor); - UnknownType typeValue = new UnknownType(typeParameterDescriptor, positionVariance); - unknownTypes.put(typeParameterDescriptor, typeValue); - } - - @NotNull - private UnknownType getTypeVariable(TypeParameterDescriptor typeParameterDescriptor) { - UnknownType unknownType = unknownTypes.get(typeParameterDescriptor); - if (unknownType == null) { - throw new IllegalArgumentException("This type parameter is not an unknown in this constraint system: " + typeParameterDescriptor); - } - return unknownType; - } - - public void addSubtypingConstraint(@NotNull JetType lower, @NotNull JetType upper) { - TypeValue typeValueForLower = getTypeValueFor(lower); - TypeValue typeValueForUpper = getTypeValueFor(upper); - addSubtypingConstraintOnTypeValues(typeValueForLower, typeValueForUpper); - } - - private void addSubtypingConstraintOnTypeValues(TypeValue typeValueForLower, TypeValue typeValueForUpper) { - println(typeValueForLower + " :< " + typeValueForUpper); - typeValueForLower.getUpperBounds().add(typeValueForUpper); - typeValueForUpper.getLowerBounds().add(typeValueForLower); - } - - @NotNull - public Solution solve() { - // Expand custom bounds, e.g. List <: List - for (Map.Entry entry : Sets.newHashSet(knownTypes.entrySet())) { - JetType jetType = entry.getKey(); - KnownType typeValue = entry.getValue(); - - for (TypeValue upperBound : typeValue.getUpperBounds()) { - if (upperBound instanceof KnownType) { - KnownType knownBoundType = (KnownType) upperBound; - boolean ok = new TypeConstraintExpander().run(jetType, knownBoundType.getType()); - if (!ok) { - return new Solution(true); - } - } - } - - // Lower bounds? - - } - - // Fill in upper bounds from type parameter bounds - for (Map.Entry entry : Sets.newHashSet(unknownTypes.entrySet())) { - TypeParameterDescriptor typeParameterDescriptor = entry.getKey(); - UnknownType typeValue = entry.getValue(); - for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) { - addSubtypingConstraintOnTypeValues(typeValue, getTypeValueFor(upperBound)); - } - } - - // 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); - } - - // Find inconsistencies - Solution solution = new Solution(false); - - for (UnknownType unknownType : unknownTypes.values()) { - check(unknownType, solution); - } - for (KnownType knownType : knownTypes.values()) { - check(knownType, solution); - } - - return solution; - } - - private void check(TypeValue typeValue, Solution solution) { - try { - KnownType resultingValue = typeValue.getValue(); - JetType type = solution.getSubstitutor().substitute(resultingValue.getType(), Variance.INVARIANT); // TODO - for (TypeValue upperBound : typeValue.getUpperBounds()) { - JetType boundingType = solution.getSubstitutor().substitute(upperBound.getValue().getType(), Variance.INVARIANT); - if (!typeChecker.isSubtypeOf(type, boundingType)) { // TODO - solution.registerError(); - println("Constraint violation: " + type + " :< " + boundingType); - } - } - for (TypeValue lowerBound : typeValue.getLowerBounds()) { - JetType boundingType = solution.getSubstitutor().substitute(lowerBound.getValue().getType(), Variance.INVARIANT); - if (!typeChecker.isSubtypeOf(boundingType, type)) { - solution.registerError(); - println("Constraint violation: " + boundingType + " :< " + type); - } - } - } - catch (LoopInTypeVariableConstraintsException e) { - solution.registerError(); - e.printStackTrace(); - } - } - - private void transitiveClosure(TypeValue current, Set visited) { - if (!visited.add(current)) { - return; - } - - for (TypeValue upperBound : Sets.newHashSet(current.getUpperBounds())) { - transitiveClosure(upperBound, visited); - Set upperBounds = upperBound.getUpperBounds(); - for (TypeValue transitiveBound : upperBounds) { - addSubtypingConstraintOnTypeValues(current, transitiveBound); - } - } - } - - public class Solution { - private final TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(new TypeSubstitutor.TypeSubstitution() { - @Override - public TypeProjection get(TypeConstructor key) { - DeclarationDescriptor declarationDescriptor = key.getDeclarationDescriptor(); - if (declarationDescriptor instanceof TypeParameterDescriptor) { - TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor; - println(descriptor + " |-> " + getValue(descriptor)); - return new TypeProjection(getValue(descriptor)); - } - return null; - } - - @Override - public boolean isEmpty() { - return false; - } - }); - private boolean failed; - - public Solution(boolean failed) { - this.failed = failed; - } - - public void registerError() { - failed = true; - } - - public boolean isSuccessful() { - return !failed; - } - - @Nullable - public JetType getValue(TypeParameterDescriptor typeParameterDescriptor) { - KnownType value = getTypeVariable(typeParameterDescriptor).getValue(); - return value == null ? null : value.getType(); - } - - public TypeSubstitutor getSubstitutor() { - return typeSubstitutor; - } - - } - - private class TypeConstraintExpander extends JetTypeChecker.AbstractTypeCheckingProcedure { - - private boolean error = false; - - private StatusAction fail() { - error = true; - return StatusAction.ABORT_ALL; - } - - @Override - protected StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { - return tryToAddConstraint(subtype, supertype); - } - - private StatusAction tryToAddConstraint(@NotNull JetType subtype, @NotNull JetType supertype) { - TypeValue subtypeValue = getTypeValueFor(subtype); - TypeValue supertypeValue = getTypeValueFor(supertype); - - if (someUnknown(subtypeValue, supertypeValue)) { - addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue); - } - return StatusAction.PROCEED; - } - - private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) { - return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType; - } - - @Override - protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { - if (someUnknown(getTypeValueFor(subtype), getTypeValueFor(supertype))) { - return StatusAction.PROCEED; - } - return fail(); - } - - @Override - protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) { - if (!subArgumentType.equals(superArgumentType)) { - return fail(); - } - return StatusAction.PROCEED; - } - - @Override - protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) { - return fail(); - } - - @Override - protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { - return StatusAction.PROCEED; - } - - @Override - protected Boolean result() { - return !error; - } - } - - private static void println(String message) { -// System.out.println(message); - } - -} \ No newline at end of file + ConstraintSystemSolution solve(); +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemSolution.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemSolution.java new file mode 100644 index 00000000000..9a78a00adda --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemSolution.java @@ -0,0 +1,18 @@ +package org.jetbrains.jet.lang.types.inference; + +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeSubstitutor; + +/** + * @author abreslav + */ +public interface ConstraintSystemSolution { + boolean isSuccessful(); + + TypeSubstitutor getSubstitutor(); + + @Nullable + JetType getValue(TypeParameterDescriptor typeParameterDescriptor); +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/EqualityBasedConstraintSystem.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/EqualityBasedConstraintSystem.java new file mode 100644 index 00000000000..3a4b25cf0a4 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/EqualityBasedConstraintSystem.java @@ -0,0 +1,55 @@ +package org.jetbrains.jet.lang.types.inference; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.Variance; + +import java.util.List; +import java.util.Map; + +/** + * @author abreslav + */ +public class EqualityBasedConstraintSystem implements ConstraintSystem { + + private abstract class Constraint { + private final TypeParameterDescriptor typeParameterDescriptor; + + protected Constraint(@NotNull TypeParameterDescriptor typeParameterDescriptor) { + this.typeParameterDescriptor = typeParameterDescriptor; + } + + @NotNull + public TypeParameterDescriptor getTypeParameterDescriptor() { + return typeParameterDescriptor; + } + } + + private class Unknown { + // T -> variance of its position + private final Map typeParameters = Maps.newHashMap(); + private final List constraints = Lists.newArrayList(); + + } + + private final Map unknowns = Maps.newHashMap(); + + @Override + public void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance) { + throw new UnsupportedOperationException(); // TODO + } + + @Override + public void addSubtypingConstraint(@NotNull JetType lower, @NotNull JetType upper) { + throw new UnsupportedOperationException(); // TODO + } + + @NotNull + @Override + public ConstraintSystemSolution solve() { + throw new UnsupportedOperationException(); // TODO + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/SubtypingOnlyConstraintSystem.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/SubtypingOnlyConstraintSystem.java new file mode 100644 index 00000000000..5ebe51309f8 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/SubtypingOnlyConstraintSystem.java @@ -0,0 +1,415 @@ +package org.jetbrains.jet.lang.types.inference; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.types.*; + +import java.util.Map; +import java.util.Set; + +/** + * @author abreslav + */ +public class SubtypingOnlyConstraintSystem implements ConstraintSystem { + + private static class LoopInTypeVariableConstraintsException extends RuntimeException { + private LoopInTypeVariableConstraintsException() { + } + + private LoopInTypeVariableConstraintsException(String message) { + super(message); + } + + private LoopInTypeVariableConstraintsException(String message, Throwable cause) { + super(message, cause); + } + + private LoopInTypeVariableConstraintsException(Throwable cause) { + super(cause); + } + } + + public static abstract class TypeValue { + private final Set upperBounds = Sets.newHashSet(); + private final Set lowerBounds = Sets.newHashSet(); + + @NotNull + public Set getUpperBounds() { + return upperBounds; + } + + @NotNull + public Set getLowerBounds() { + return lowerBounds; + } + + @Nullable + public abstract KnownType getValue(); + } + + private static class UnknownType extends TypeValue { + + private final TypeParameterDescriptor typeParameterDescriptor; + private final Variance positionVariance; + private KnownType value; + private boolean beingComputed = false; + + private UnknownType(TypeParameterDescriptor typeParameterDescriptor, Variance positionVariance) { + this.typeParameterDescriptor = typeParameterDescriptor; + this.positionVariance = positionVariance; + } + + @NotNull + public TypeParameterDescriptor getTypeParameterDescriptor() { + return typeParameterDescriptor; + } + + @Override + public KnownType getValue() { + if (beingComputed) { + throw new LoopInTypeVariableConstraintsException(); + } + if (value == null) { + JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; + beingComputed = true; + try { + if (positionVariance == Variance.IN_VARIANCE) { + // maximal solution + throw new UnsupportedOperationException(); + } + else { + // minimal solution + + Set lowerBounds = getLowerBounds(); + if (!lowerBounds.isEmpty()) { + Set types = getTypes(lowerBounds); + + JetType commonSupertype = CommonSupertypes.commonSupertype(types); + for (TypeValue upperBound : getUpperBounds()) { + if (!typeChecker.isSubtypeOf(commonSupertype, upperBound.getValue().getType())) { + value = null; + } + } + + println("minimal solution from lowerbounds for " + this + " is " + commonSupertype); + value = new KnownType(commonSupertype); + } + else { + Set upperBounds = getUpperBounds(); + Set types = getTypes(upperBounds); + JetType intersect = TypeUtils.intersect(typeChecker, types); + + value = new KnownType(intersect); + } + } + } + finally { + beingComputed = false; + } + } + + return value; + } + + private Set getTypes(Set lowerBounds) { + Set types = Sets.newHashSet(); + for (TypeValue lowerBound : lowerBounds) { + types.add(lowerBound.getValue().getType()); + } + return types; + } + + @Override + public String toString() { + return "?" + typeParameterDescriptor; + } + + } + + private static class KnownType extends TypeValue { + + private final JetType type; + + public KnownType(@NotNull JetType type) { + this.type = type; + } + + @NotNull + public JetType getType() { + return type; + } + + @Override + public KnownType getValue() { + return this; + } + + @Override + public String toString() { + return type.toString(); + } + } + + private final Map knownTypes = Maps.newHashMap(); + private final Map unknownTypes = Maps.newHashMap(); + private final JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; + + @NotNull + private TypeValue getTypeValueFor(@NotNull JetType type) { + DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); + if (declarationDescriptor instanceof TypeParameterDescriptor) { + TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor; + UnknownType unknownType = unknownTypes.get(typeParameterDescriptor); + if (unknownType != null) { + return unknownType; + } + } + + KnownType typeValue = knownTypes.get(type); + if (typeValue == null) { + typeValue = new KnownType(type); + knownTypes.put(type, typeValue); + } + return typeValue; + } + + @Override + public void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance) { + assert !unknownTypes.containsKey(typeParameterDescriptor); + UnknownType typeValue = new UnknownType(typeParameterDescriptor, positionVariance); + unknownTypes.put(typeParameterDescriptor, typeValue); + } + + @NotNull + private UnknownType getTypeVariable(TypeParameterDescriptor typeParameterDescriptor) { + UnknownType unknownType = unknownTypes.get(typeParameterDescriptor); + if (unknownType == null) { + throw new IllegalArgumentException("This type parameter is not an unknown in this constraint system: " + typeParameterDescriptor); + } + return unknownType; + } + + @Override + public void addSubtypingConstraint(@NotNull JetType lower, @NotNull JetType upper) { + TypeValue typeValueForLower = getTypeValueFor(lower); + TypeValue typeValueForUpper = getTypeValueFor(upper); + addSubtypingConstraintOnTypeValues(typeValueForLower, typeValueForUpper); + } + + private void addSubtypingConstraintOnTypeValues(TypeValue typeValueForLower, TypeValue typeValueForUpper) { + println(typeValueForLower + " :< " + typeValueForUpper); + typeValueForLower.getUpperBounds().add(typeValueForUpper); + typeValueForUpper.getLowerBounds().add(typeValueForLower); + } + + @Override + @NotNull + public ConstraintSystemSolution solve() { + // Expand custom bounds, e.g. List <: List + for (Map.Entry entry : Sets.newHashSet(knownTypes.entrySet())) { + JetType jetType = entry.getKey(); + KnownType typeValue = entry.getValue(); + + for (TypeValue upperBound : typeValue.getUpperBounds()) { + if (upperBound instanceof KnownType) { + KnownType knownBoundType = (KnownType) upperBound; + boolean ok = new SubtypingConstraintExpander().run(jetType, knownBoundType.getType()); + if (!ok) { + return new Solution(true); + } + } + } + + // Lower bounds? + + } + + // Fill in upper bounds from type parameter bounds + for (Map.Entry entry : Sets.newHashSet(unknownTypes.entrySet())) { + TypeParameterDescriptor typeParameterDescriptor = entry.getKey(); + UnknownType typeValue = entry.getValue(); + for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) { + addSubtypingConstraintOnTypeValues(typeValue, getTypeValueFor(upperBound)); + } + } + + // 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); + } + + // Find inconsistencies + Solution solution = new Solution(false); + + for (UnknownType unknownType : unknownTypes.values()) { + check(unknownType, solution); + } + for (KnownType knownType : knownTypes.values()) { + check(knownType, solution); + } + + return solution; + } + + private void check(TypeValue typeValue, Solution solution) { + try { + KnownType resultingValue = typeValue.getValue(); + JetType type = solution.getSubstitutor().substitute(resultingValue.getType(), Variance.INVARIANT); // TODO + for (TypeValue upperBound : typeValue.getUpperBounds()) { + JetType boundingType = solution.getSubstitutor().substitute(upperBound.getValue().getType(), Variance.INVARIANT); + if (!typeChecker.isSubtypeOf(type, boundingType)) { // TODO + solution.registerError(); + println("Constraint violation: " + type + " :< " + boundingType); + } + } + for (TypeValue lowerBound : typeValue.getLowerBounds()) { + JetType boundingType = solution.getSubstitutor().substitute(lowerBound.getValue().getType(), Variance.INVARIANT); + if (!typeChecker.isSubtypeOf(boundingType, type)) { + solution.registerError(); + println("Constraint violation: " + boundingType + " :< " + type); + } + } + } + catch (LoopInTypeVariableConstraintsException e) { + solution.registerError(); + e.printStackTrace(); + } + } + + private void transitiveClosure(TypeValue current, Set visited) { + if (!visited.add(current)) { + return; + } + + for (TypeValue upperBound : Sets.newHashSet(current.getUpperBounds())) { + transitiveClosure(upperBound, visited); + Set upperBounds = upperBound.getUpperBounds(); + for (TypeValue transitiveBound : upperBounds) { + addSubtypingConstraintOnTypeValues(current, transitiveBound); + } + } + } + + public class Solution implements ConstraintSystemSolution { + private final TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(new TypeSubstitutor.TypeSubstitution() { + @Override + public TypeProjection get(TypeConstructor key) { + DeclarationDescriptor declarationDescriptor = key.getDeclarationDescriptor(); + if (declarationDescriptor instanceof TypeParameterDescriptor) { + TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor; + println(descriptor + " |-> " + getValue(descriptor)); + return new TypeProjection(getValue(descriptor)); + } + return null; + } + + @Override + public boolean isEmpty() { + return false; + } + }); + private boolean failed; + + public Solution(boolean failed) { + this.failed = failed; + } + + private void registerError() { + failed = true; + } + + @Override + public boolean isSuccessful() { + return !failed; + } + + @Override + public JetType getValue(TypeParameterDescriptor typeParameterDescriptor) { + KnownType value = getTypeVariable(typeParameterDescriptor).getValue(); + return value == null ? null : value.getType(); + } + + @Override + public TypeSubstitutor getSubstitutor() { + return typeSubstitutor; + } + + } + +// private class EqualityConstraintExpander extends JetTypeChecker.AbstractTypeCheckingProcedure { +// +// } + + private class SubtypingConstraintExpander extends JetTypeChecker.AbstractTypeCheckingProcedure { + + private boolean error = false; + + private StatusAction fail() { + error = true; + return StatusAction.ABORT_ALL; + } + + @Override + protected StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { + return tryToAddConstraint(subtype, supertype); + } + + private StatusAction tryToAddConstraint(@NotNull JetType subtype, @NotNull JetType supertype) { + TypeValue subtypeValue = getTypeValueFor(subtype); + TypeValue supertypeValue = getTypeValueFor(supertype); + + if (someUnknown(subtypeValue, supertypeValue)) { + addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue); + } + return StatusAction.PROCEED; + } + + private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) { + return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType; + } + + @Override + protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { + if (someUnknown(getTypeValueFor(subtype), getTypeValueFor(supertype))) { + return StatusAction.PROCEED; + } + return fail(); + } + + @Override + protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) { + if (!subArgumentType.equals(superArgumentType)) { + return fail(); + } + return StatusAction.PROCEED; + } + + @Override + protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) { + return fail(); + } + + @Override + protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { + return StatusAction.PROCEED; + } + + @Override + protected Boolean result() { + return !error; + } + } + + private static void println(String message) { +// System.out.println(message); + } + +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index aa914ef6bb3..0cd1450159b 100644 --- a/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -471,7 +471,7 @@ public class JetTypeCheckerTest extends JetLiteFixture { for (String type : types) { subtypes.add(makeType(type)); } - JetType result = semanticServices.getTypeChecker().commonSupertype(subtypes); + JetType result = CommonSupertypes.commonSupertype(subtypes); assertTrue(result + " != " + expected, result.equals(makeType(expected))); } From 787d3aa450008643fb9183362aecd057ef6e2043 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Nov 2011 18:54:23 +0300 Subject: [PATCH 08/14] New type checking algorithm is used for subtyping constraint generation --- .../jet/lang/resolve/calls/CallResolver.java | 4 +- .../jet/lang/types/JetTypeChecker.java | 396 ++++++++++-------- ...tSystem.java => ConstraintSystemImpl.java} | 102 ++--- .../EqualityBasedConstraintSystem.java | 55 --- 4 files changed, 254 insertions(+), 303 deletions(-) rename compiler/frontend/src/org/jetbrains/jet/lang/types/inference/{SubtypingOnlyConstraintSystem.java => ConstraintSystemImpl.java} (87%) delete mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/types/inference/EqualityBasedConstraintSystem.java 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 1d95508cfe9..741424efbab 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 @@ -21,7 +21,7 @@ import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices; import org.jetbrains.jet.lang.types.expressions.OperatorConventions; import org.jetbrains.jet.lang.types.inference.ConstraintSystem; import org.jetbrains.jet.lang.types.inference.ConstraintSystemSolution; -import org.jetbrains.jet.lang.types.inference.SubtypingOnlyConstraintSystem; +import org.jetbrains.jet.lang.types.inference.ConstraintSystemImpl; import org.jetbrains.jet.lexer.JetTokens; import java.util.*; @@ -414,7 +414,7 @@ public class CallResolver { if (!candidate.getTypeParameters().isEmpty()) { // Type argument inference - ConstraintSystem constraintSystem = new SubtypingOnlyConstraintSystem(); + ConstraintSystem constraintSystem = new ConstraintSystemImpl(); for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) { constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index 38ee84c2f52..6573f54fc19 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -4,8 +4,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; -import java.util.*; +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; @@ -21,7 +22,7 @@ public class JetTypeChecker { public boolean isSubtypeOf(@NotNull JetType subtype, @NotNull JetType supertype) { // return new TypeCheckingProcedure().run(subtype, supertype); - return new ExplicitInOutTypeCheckingProcedure().run(subtype, supertype); + return TYPE_CHECKER.run(subtype, supertype); } // This method returns the supertype of the first parameter that has the same constructor @@ -51,7 +52,39 @@ public class JetTypeChecker { return isOutProjected ? JetStandardClasses.getNothingType() : argument.getType(); } - private static class ExplicitInOutTypeCheckingProcedure { + /** + * 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() { + @Override + public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b) { + return TypeUtils.equalTypes(a, b); + } + + @Override + public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype) { + return INSTANCE.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 run(@NotNull JetType subtype, @NotNull JetType supertype) { return isSubtypeOf(subtype, supertype); @@ -69,7 +102,7 @@ public class JetTypeChecker { } @Nullable JetType closestSupertype = findCorrespondingSupertype(subtype, supertype); if (closestSupertype == null) { - return false; + if (!constraintBuilder.noCorrespondingSupertype(subtype, supertype)) return false; } return checkSubtypeForTheSameConstructor(closestSupertype, supertype); @@ -84,6 +117,7 @@ public class JetTypeChecker { List 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); @@ -93,189 +127,193 @@ public class JetTypeChecker { JetType superIn = getInType(parameter, superArgument); JetType superOut = getOutType(parameter, superArgument); - if (!isSubtypeOf(subOut, superOut) || !isSubtypeOf(superIn, subIn)) { - return false; + 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; } } - public static abstract class AbstractTypeCheckingProcedure { - - protected enum StatusAction { - PROCEED(false), - DONE_WITH_CURRENT_TYPE(true), - ABORT_ALL(true); - - private final boolean abort; - - private StatusAction(boolean abort) { - this.abort = abort; - } - - public boolean isAbort() { - return abort; - } - } - - public final T run(@NotNull JetType subtype, @NotNull JetType supertype) { - proceedOrStop(subtype, supertype); - return result(); - } - - protected abstract StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype); - - protected abstract StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype); - - protected abstract StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType); - - protected abstract StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument); - - protected abstract StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype); - - protected abstract T result(); - - private StatusAction proceedOrStop(@NotNull JetType subtype, @NotNull JetType supertype) { - StatusAction statusAction = startForPairOfTypes(subtype, supertype); - if (statusAction.isAbort()) { - return statusAction; - } - - JetType closestSupertype = findCorrespondingSupertype(subtype, supertype); - if (closestSupertype == null) { - return noCorrespondingSupertype(subtype, supertype); - } - - proceed(closestSupertype, supertype); - return doneForPairOfTypes(subtype, supertype); - } - - private void proceed(@NotNull JetType subtype, @NotNull JetType supertype) { - TypeConstructor constructor = subtype.getConstructor(); - assert constructor.equals(supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor(); - - List subArguments = subtype.getArguments(); - List superArguments = supertype.getArguments(); - List parameters = constructor.getParameters(); - - loop: - for (int i = 0; i < parameters.size(); i++) { - TypeParameterDescriptor parameter = parameters.get(i); - TypeProjection subArgument = subArguments.get(i); - TypeProjection superArgument = superArguments.get(i); - - JetType subArgumentType = subArgument.getType(); - JetType superArgumentType = superArgument.getType(); - - StatusAction action = null; - switch (parameter.getVariance()) { - case INVARIANT: - switch (superArgument.getProjectionKind()) { - case INVARIANT: - action = equalTypesRequired(subArgumentType, superArgumentType); - break; - case OUT_VARIANCE: - if (!subArgument.getProjectionKind().allowsOutPosition()) { - action = varianceConflictFound(subArgument, superArgument); - } - else { - action = proceedOrStop(subArgumentType, superArgumentType); - } - break; - case IN_VARIANCE: - if (!subArgument.getProjectionKind().allowsInPosition()) { - action = varianceConflictFound(subArgument, superArgument); - } - else { - action = proceedOrStop(superArgumentType, subArgumentType); - } - break; - } - break; - case IN_VARIANCE: - switch (superArgument.getProjectionKind()) { - case INVARIANT: - case IN_VARIANCE: - action = proceedOrStop(superArgumentType, subArgumentType); - break; - case OUT_VARIANCE: - action = proceedOrStop(subArgumentType, superArgumentType); - break; - } - break; - case OUT_VARIANCE: - switch (superArgument.getProjectionKind()) { - case INVARIANT: - case OUT_VARIANCE: - case IN_VARIANCE: - action = proceedOrStop(subArgumentType, superArgumentType); - break; - } - break; - } - switch (action) { - case ABORT_ALL: break loop; - case DONE_WITH_CURRENT_TYPE: - default: - } - } - } - - } +// public static abstract class AbstractTypeCheckingProcedure { +// +// protected enum StatusAction { +// PROCEED(false), +// DONE_WITH_CURRENT_TYPE(true), +// ABORT_ALL(true); +// +// private final boolean abort; +// +// private StatusAction(boolean abort) { +// this.abort = abort; +// } +// +// public boolean isAbort() { +// return abort; +// } +// } +// +// public final T run(@NotNull JetType subtype, @NotNull JetType supertype) { +// proceedOrStop(subtype, supertype); +// return result(); +// } +// +// protected abstract StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype); +// +// protected abstract StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype); +// +// protected abstract StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType); +// +// protected abstract StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument); +// +// protected abstract StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype); +// +// protected abstract T result(); +// +// private StatusAction proceedOrStop(@NotNull JetType subtype, @NotNull JetType supertype) { +// StatusAction statusAction = startForPairOfTypes(subtype, supertype); +// if (statusAction.isAbort()) { +// return statusAction; +// } +// +// JetType closestSupertype = findCorrespondingSupertype(subtype, supertype); +// if (closestSupertype == null) { +// return noCorrespondingSupertype(subtype, supertype); +// } +// +// proceed(closestSupertype, supertype); +// return doneForPairOfTypes(subtype, supertype); +// } +// +// private void proceed(@NotNull JetType subtype, @NotNull JetType supertype) { +// TypeConstructor constructor = subtype.getConstructor(); +// assert constructor.equals(supertype.getConstructor()) : constructor + " is not " + supertype.getConstructor(); +// +// List subArguments = subtype.getArguments(); +// List superArguments = supertype.getArguments(); +// List parameters = constructor.getParameters(); +// +// loop: +// for (int i = 0; i < parameters.size(); i++) { +// TypeParameterDescriptor parameter = parameters.get(i); +// TypeProjection subArgument = subArguments.get(i); +// TypeProjection superArgument = superArguments.get(i); +// +// JetType subArgumentType = subArgument.getType(); +// JetType superArgumentType = superArgument.getType(); +// +// StatusAction action = null; +// switch (parameter.getVariance()) { +// case INVARIANT: +// switch (superArgument.getProjectionKind()) { +// case INVARIANT: +// action = equalTypesRequired(subArgumentType, superArgumentType); +// break; +// case OUT_VARIANCE: +// if (!subArgument.getProjectionKind().allowsOutPosition()) { +// action = varianceConflictFound(subArgument, superArgument); +// } +// else { +// action = proceedOrStop(subArgumentType, superArgumentType); +// } +// break; +// case IN_VARIANCE: +// if (!subArgument.getProjectionKind().allowsInPosition()) { +// action = varianceConflictFound(subArgument, superArgument); +// } +// else { +// action = proceedOrStop(superArgumentType, subArgumentType); +// } +// break; +// } +// break; +// case IN_VARIANCE: +// switch (superArgument.getProjectionKind()) { +// case INVARIANT: +// case IN_VARIANCE: +// action = proceedOrStop(superArgumentType, subArgumentType); +// break; +// case OUT_VARIANCE: +// action = proceedOrStop(subArgumentType, superArgumentType); +// break; +// } +// break; +// case OUT_VARIANCE: +// switch (superArgument.getProjectionKind()) { +// case INVARIANT: +// case OUT_VARIANCE: +// case IN_VARIANCE: +// action = proceedOrStop(subArgumentType, superArgumentType); +// break; +// } +// break; +// } +// switch (action) { +// case ABORT_ALL: break loop; +// case DONE_WITH_CURRENT_TYPE: +// default: +// } +// } +// } +// +// } - private static class TypeCheckingProcedure extends AbstractTypeCheckingProcedure { - - private boolean result = true; - - private StatusAction fail() { - result = false; - return StatusAction.ABORT_ALL; - } - - @Override - public StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { - if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) { - return StatusAction.DONE_WITH_CURRENT_TYPE; - } - if (!supertype.isNullable() && subtype.isNullable()) { - return fail(); - } - if (JetStandardClasses.isNothingOrNullableNothing(subtype)) { - return StatusAction.DONE_WITH_CURRENT_TYPE; - } - return StatusAction.PROCEED; - } - - @Override - protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { - return fail(); - } - - @Override - protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) { - if (!subArgumentType.equals(superArgumentType)) { - return fail(); - } - return StatusAction.PROCEED; - } - - @Override - protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) { - return fail(); - } - - @Override - protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { - return StatusAction.PROCEED; - } - - @Override - protected Boolean result() { - return result; - } - } +// private static class TypeCheckingProcedure extends AbstractTypeCheckingProcedure { +// +// private boolean result = true; +// +// private StatusAction fail() { +// result = false; +// return StatusAction.ABORT_ALL; +// } +// +// @Override +// public StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { +// if (ErrorUtils.isErrorType(subtype) || ErrorUtils.isErrorType(supertype)) { +// return StatusAction.DONE_WITH_CURRENT_TYPE; +// } +// if (!supertype.isNullable() && subtype.isNullable()) { +// return fail(); +// } +// if (JetStandardClasses.isNothingOrNullableNothing(subtype)) { +// return StatusAction.DONE_WITH_CURRENT_TYPE; +// } +// return StatusAction.PROCEED; +// } +// +// @Override +// protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { +// return fail(); +// } +// +// @Override +// protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) { +// if (!subArgumentType.equals(superArgumentType)) { +// return fail(); +// } +// return StatusAction.PROCEED; +// } +// +// @Override +// protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) { +// return fail(); +// } +// +// @Override +// protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { +// return StatusAction.PROCEED; +// } +// +// @Override +// protected Boolean result() { +// return result; +// } +// } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/SubtypingOnlyConstraintSystem.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemImpl.java similarity index 87% rename from compiler/frontend/src/org/jetbrains/jet/lang/types/inference/SubtypingOnlyConstraintSystem.java rename to compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemImpl.java index 5ebe51309f8..fc7e403dd5b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/SubtypingOnlyConstraintSystem.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemImpl.java @@ -14,7 +14,7 @@ import java.util.Set; /** * @author abreslav */ -public class SubtypingOnlyConstraintSystem implements ConstraintSystem { +public class ConstraintSystemImpl implements ConstraintSystem { private static class LoopInTypeVariableConstraintsException extends RuntimeException { private LoopInTypeVariableConstraintsException() { @@ -158,6 +158,39 @@ public class SubtypingOnlyConstraintSystem implements ConstraintSystem { private final Map unknownTypes = Maps.newHashMap(); private final JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; + private final JetTypeChecker.TypeCheckingProcedure constraintExpander = new JetTypeChecker.TypeCheckingProcedure(new JetTypeChecker.TypingConstraintBuilder() { + @Override + public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b) { + return TypeUtils.equalTypes(a, b); + } + + @Override + public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype) { + TypeValue subtypeValue = getTypeValueFor(subtype); + TypeValue supertypeValue = getTypeValueFor(supertype); + + if (someUnknown(subtypeValue, supertypeValue)) { + addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue); + } + return true; + } + + @Override + public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { + // If some of the types is an unknown, the constraint is already generated, and we should carry on + // otherwise there can be no solution, and we should fail + return someUnknown(getTypeValueFor(subtype), getTypeValueFor(supertype)); + } + + private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) { + return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType; + } + + }); + + public ConstraintSystemImpl() { + } + @NotNull private TypeValue getTypeValueFor(@NotNull JetType type) { DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); @@ -217,7 +250,7 @@ public class SubtypingOnlyConstraintSystem implements ConstraintSystem { for (TypeValue upperBound : typeValue.getUpperBounds()) { if (upperBound instanceof KnownType) { KnownType knownBoundType = (KnownType) upperBound; - boolean ok = new SubtypingConstraintExpander().run(jetType, knownBoundType.getType()); + boolean ok = constraintExpander.run(jetType, knownBoundType.getType()); if (!ok) { return new Solution(true); } @@ -344,72 +377,7 @@ public class SubtypingOnlyConstraintSystem implements ConstraintSystem { } -// private class EqualityConstraintExpander extends JetTypeChecker.AbstractTypeCheckingProcedure { -// -// } - - private class SubtypingConstraintExpander extends JetTypeChecker.AbstractTypeCheckingProcedure { - - private boolean error = false; - - private StatusAction fail() { - error = true; - return StatusAction.ABORT_ALL; - } - - @Override - protected StatusAction startForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { - return tryToAddConstraint(subtype, supertype); - } - - private StatusAction tryToAddConstraint(@NotNull JetType subtype, @NotNull JetType supertype) { - TypeValue subtypeValue = getTypeValueFor(subtype); - TypeValue supertypeValue = getTypeValueFor(supertype); - - if (someUnknown(subtypeValue, supertypeValue)) { - addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue); - } - return StatusAction.PROCEED; - } - - private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) { - return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType; - } - - @Override - protected StatusAction noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) { - if (someUnknown(getTypeValueFor(subtype), getTypeValueFor(supertype))) { - return StatusAction.PROCEED; - } - return fail(); - } - - @Override - protected StatusAction equalTypesRequired(@NotNull JetType subArgumentType, @NotNull JetType superArgumentType) { - if (!subArgumentType.equals(superArgumentType)) { - return fail(); - } - return StatusAction.PROCEED; - } - - @Override - protected StatusAction varianceConflictFound(@NotNull TypeProjection subArgument, @NotNull TypeProjection superArgument) { - return fail(); - } - - @Override - protected StatusAction doneForPairOfTypes(@NotNull JetType subtype, @NotNull JetType supertype) { - return StatusAction.PROCEED; - } - - @Override - protected Boolean result() { - return !error; - } - } - private static void println(String message) { // System.out.println(message); } - } \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/EqualityBasedConstraintSystem.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/EqualityBasedConstraintSystem.java deleted file mode 100644 index 3a4b25cf0a4..00000000000 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/EqualityBasedConstraintSystem.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.jetbrains.jet.lang.types.inference; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; -import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.lang.types.Variance; - -import java.util.List; -import java.util.Map; - -/** - * @author abreslav - */ -public class EqualityBasedConstraintSystem implements ConstraintSystem { - - private abstract class Constraint { - private final TypeParameterDescriptor typeParameterDescriptor; - - protected Constraint(@NotNull TypeParameterDescriptor typeParameterDescriptor) { - this.typeParameterDescriptor = typeParameterDescriptor; - } - - @NotNull - public TypeParameterDescriptor getTypeParameterDescriptor() { - return typeParameterDescriptor; - } - } - - private class Unknown { - // T -> variance of its position - private final Map typeParameters = Maps.newHashMap(); - private final List constraints = Lists.newArrayList(); - - } - - private final Map unknowns = Maps.newHashMap(); - - @Override - public void registerTypeVariable(@NotNull TypeParameterDescriptor typeParameterDescriptor, @NotNull Variance positionVariance) { - throw new UnsupportedOperationException(); // TODO - } - - @Override - public void addSubtypingConstraint(@NotNull JetType lower, @NotNull JetType upper) { - throw new UnsupportedOperationException(); // TODO - } - - @NotNull - @Override - public ConstraintSystemSolution solve() { - throw new UnsupportedOperationException(); // TODO - } -} From a24e448973bacbe63d07dd41a854c014f9f01a3e Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Nov 2011 19:52:05 +0300 Subject: [PATCH 09/14] Simplistic type inference: KT-258 Support equality constraints in type inference KT-399 Type argument inference not implemented for CALL_EXPRESSION --- .../types/inference/ConstraintSystemImpl.java | 44 +++++++++++++++++-- .../full/regression/kt258.jet | 10 +++++ .../quick/TypeInference.jet | 18 ++++++++ .../quick/regressions/kt399.jet | 11 +++++ 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/checkerWithErrorTypes/full/regression/kt258.jet create mode 100644 compiler/testData/checkerWithErrorTypes/quick/TypeInference.jet create mode 100644 compiler/testData/checkerWithErrorTypes/quick/regressions/kt399.jet 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 fc7e403dd5b..a569a0beb1f 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 @@ -115,6 +115,16 @@ public class ConstraintSystemImpl implements ConstraintSystem { return value; } + public boolean setValue(@NotNull KnownType value) { + if (this.value != null) { + // If we have already assigned a value to this unknown, + // it is a conflict to assign another one, unless this new one is equal to the previous + return TypeUtils.equalTypes(this.value.getType(), value.getType()); + } + this.value = value; + return true; + } + private Set getTypes(Set lowerBounds) { Set types = Sets.newHashSet(); for (TypeValue lowerBound : lowerBounds) { @@ -161,7 +171,28 @@ public class ConstraintSystemImpl implements ConstraintSystem { private final JetTypeChecker.TypeCheckingProcedure constraintExpander = new JetTypeChecker.TypeCheckingProcedure(new JetTypeChecker.TypingConstraintBuilder() { @Override public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b) { - return TypeUtils.equalTypes(a, b); + TypeValue aValue = getTypeValueFor(a); + TypeValue bValue = getTypeValueFor(b); + + if (aValue instanceof UnknownType) { + UnknownType aUnknown = (UnknownType) aValue; + if (bValue instanceof UnknownType) { + UnknownType bUnknown = (UnknownType) bValue; + mergeUnknowns(aUnknown, bUnknown); + } + else { + if (!aUnknown.setValue((KnownType) bValue)) return false; + } + } + else if (bValue instanceof UnknownType) { + UnknownType bUnknown = (UnknownType) bValue; + if (!bUnknown.setValue((KnownType) aValue)) return false; + } + else { + return TypeUtils.equalTypes(a, b); + } + + return true; } @Override @@ -188,8 +219,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { }); - public ConstraintSystemImpl() { - } + public ConstraintSystemImpl() {} @NotNull private TypeValue getTypeValueFor(@NotNull JetType type) { @@ -226,6 +256,10 @@ public class ConstraintSystemImpl implements ConstraintSystem { return unknownType; } + private void mergeUnknowns(@NotNull UnknownType a, @NotNull UnknownType b) { + + } + @Override public void addSubtypingConstraint(@NotNull JetType lower, @NotNull JetType upper) { TypeValue typeValueForLower = getTypeValueFor(lower); @@ -289,6 +323,10 @@ public class ConstraintSystemImpl implements ConstraintSystem { check(knownType, solution); } + // 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 + return solution; } diff --git a/compiler/testData/checkerWithErrorTypes/full/regression/kt258.jet b/compiler/testData/checkerWithErrorTypes/full/regression/kt258.jet new file mode 100644 index 00000000000..257dfb12378 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/regression/kt258.jet @@ -0,0 +1,10 @@ +// KT-258 Support equality constraints in type inference + +import java.util.* + +fun test() { + val attributes : HashMap = HashMap() + attributes["href"] = "1" // inference fails, but it shouldn't +} + +fun java.util.Map.set(key : K, value : V) {}//= this.put(key, value) diff --git a/compiler/testData/checkerWithErrorTypes/quick/TypeInference.jet b/compiler/testData/checkerWithErrorTypes/quick/TypeInference.jet new file mode 100644 index 00000000000..ed8093219b0 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/quick/TypeInference.jet @@ -0,0 +1,18 @@ +class C() { + fun foo() : T {} +} + +fun foo(c: C) {} +fun bar() : C {} + +fun main(args : Array) { + val a : C = C(); + val x : C = C() + val y : C = C() + val z : C<*> = C() + + val ba : C = bar(); + val bx : C = bar() + val by : C = bar() + val bz : C<*> = bar() +} diff --git a/compiler/testData/checkerWithErrorTypes/quick/regressions/kt399.jet b/compiler/testData/checkerWithErrorTypes/quick/regressions/kt399.jet new file mode 100644 index 00000000000..9fd7e71c393 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/quick/regressions/kt399.jet @@ -0,0 +1,11 @@ +// KT-399 Type argument inference not implemented for CALL_EXPRESSION + +fun getSameTypeChecker(obj: T) : Function1 { + return { (a : Any) => a is T } +} + +fun box() : String { + if(getSameTypeChecker("lala")(10)) return "fail" + if(!getSameTypeChecker("mama")("lala")) return "fail" + return "OK" +} \ No newline at end of file From 0c492ae0eb0cf6aff212d0de3bfcaf8826a64115 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Nov 2011 21:48:36 +0300 Subject: [PATCH 10/14] KT-287 Infer constructor type arguments --- .../jet/lang/resolve/BodyResolver.java | 21 ++++++++++--------- .../jet/lang/resolve/calls/CallResolver.java | 15 +------------ .../CompileTimeConstantResolver.java | 2 +- .../full/regression/kt287.jet | 5 +++++ 4 files changed, 18 insertions(+), 25 deletions(-) create mode 100644 compiler/testData/checkerWithErrorTypes/full/regression/kt287.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index b5f70ccfbe2..b360e44faf3 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -484,16 +484,17 @@ public class BodyResolver { private void resolvePropertyInitializer(JetProperty property, PropertyDescriptor propertyDescriptor, JetExpression initializer, JetScope scope) { //JetFlowInformationProvider flowInformationProvider = context.getClassDescriptorResolver().computeFlowData(property, initializer); // TODO : flow JET-15 ExpressionTypingServices typeInferrer = context.getSemanticServices().getTypeInferrerServices(traceForConstructors); - JetType type = typeInferrer.getType(getPropertyDeclarationInnerScope(scope, propertyDescriptor), initializer, NO_EXPECTED_TYPE); - - JetType expectedType = propertyDescriptor.getInType(); - if (expectedType == null) { - expectedType = propertyDescriptor.getOutType(); - } - if (type != null && expectedType != null - && !context.getSemanticServices().getTypeChecker().isSubtypeOf(type, expectedType)) { - context.getTrace().report(TYPE_MISMATCH.on(initializer, expectedType, type)); - } + JetType expectedTypeForInitializer = property.getPropertyTypeRef() != null ? propertyDescriptor.getOutType() : NO_EXPECTED_TYPE; + JetType type = typeInferrer.getType(getPropertyDeclarationInnerScope(scope, propertyDescriptor), initializer, expectedTypeForInitializer); +// +// JetType expectedType = propertyDescriptor.getInType(); +// if (expectedType == null) { +// expectedType = propertyDescriptor.getOutType(); +// } +// if (type != null && expectedType != null +// && !context.getSemanticServices().getTypeChecker().isSubtypeOf(type, expectedType)) { +//// context.getTrace().report(TYPE_MISMATCH.on(initializer, expectedType, type)); +// } } private void resolveFunctionBodies() { 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 741424efbab..6047be04453 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 @@ -52,11 +52,8 @@ public class CallResolver { public VariableDescriptor resolveSimpleProperty( @NotNull BindingTrace trace, @NotNull JetScope scope, -// @NotNull ReceiverDescriptor receiver, -// @NotNull final JetSimpleNameExpression nameExpression, - @NotNull Call call, + @NotNull Call call, @NotNull JetType expectedType) { -// Call call = CallMaker.makePropertyCall(receiver, null, nameExpression); JetExpression calleeExpression = call.getCalleeExpression(); assert calleeExpression instanceof JetSimpleNameExpression; JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) calleeExpression; @@ -116,7 +113,6 @@ public class CallResolver { if (descriptor instanceof ConstructorDescriptor) { Modality modality = ((ConstructorDescriptor) descriptor).getContainingDeclaration().getModality(); if (modality == Modality.ABSTRACT) { -// tracing.reportOverallResolutionError(trace, "Can not create an instance of an abstract class"); tracing.instantiationOfAbstractClass(trace); return false; } @@ -149,14 +145,12 @@ public class CallResolver { ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; Set constructors = classDescriptor.getConstructors(); if (constructors.isEmpty()) { -// trace.getErrorHandler().genericError(reportAbsenceOn, "This class does not have a constructor"); trace.report(NO_CONSTRUCTOR.on(reportAbsenceOn)); return checkArgumentTypesAndFail(trace, scope, call); } prioritizedTasks.add(new ResolutionTask(TaskPrioritizer.convertWithImpliedThis(scope, Collections.singletonList(NO_RECEIVER), constructors), call, DataFlowInfo.EMPTY)); } else { -// trace.getErrorHandler().genericError(calleeExpression.getNode(), "Not a class"); trace.report(NOT_A_CLASS.on(calleeExpression)); return checkArgumentTypesAndFail(trace, scope, call); } @@ -169,7 +163,6 @@ public class CallResolver { Set constructors = classDescriptor.getConstructors(); if (constructors.isEmpty()) { -// trace.getErrorHandler().genericError(reportAbsenceOn, "This class does not have a constructor"); trace.report(NO_CONSTRUCTOR.on(reportAbsenceOn)); return checkArgumentTypesAndFail(trace, scope, call); } @@ -296,11 +289,9 @@ public class CallResolver { public void wrongNumberOfTypeArguments(@NotNull BindingTrace trace, int expectedTypeArgumentCount) { JetTypeArgumentList typeArgumentList = call.getTypeArgumentList(); if (typeArgumentList != null) { -// trace.getErrorHandler().genericError(typeArgumentList.getNode(), message); trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(typeArgumentList, expectedTypeArgumentCount)); } else { -// reportOverallResolutionError(trace, message); trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(reference, expectedTypeArgumentCount)); } } @@ -439,8 +430,6 @@ public class CallResolver { } } -// checkReceiverAbsence(candidateCall, tracing, candidate); - // Error is already reported if something is missing ReceiverDescriptor receiverParameter = candidateCall.getReceiverArgument(); ReceiverDescriptor candidateReceiver = candidate.getReceiverParameter(); @@ -453,7 +442,6 @@ public class CallResolver { } ConstraintSystemSolution solution = constraintSystem.solve(); -// solutions.put(candidate, solution); if (solution.isSuccessful()) { D substitute = (D) candidate.substitute(solution.getSubstitutor()); assert substitute != null; @@ -506,7 +494,6 @@ public class CallResolver { } else { candidateCall.setStatus(OTHER_ERROR); -// tracing.reportWrongTypeArguments(temporaryTrace, "Number of type arguments does not match " + DescriptorRenderer.TEXT.render(candidate)); tracing.wrongNumberOfTypeArguments(temporaryTrace, expectedTypeArgumentCount); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/CompileTimeConstantResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/CompileTimeConstantResolver.java index fb164fda388..526bb6d3db0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/CompileTimeConstantResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/constants/CompileTimeConstantResolver.java @@ -260,7 +260,7 @@ public class CompileTimeConstantResolver { } private boolean noExpectedType(JetType expectedType) { - return expectedType == TypeUtils.NO_EXPECTED_TYPE || JetStandardClasses.isUnit(expectedType); + return expectedType == TypeUtils.NO_EXPECTED_TYPE || JetStandardClasses.isUnit(expectedType) || ErrorUtils.isErrorType(expectedType); } } diff --git a/compiler/testData/checkerWithErrorTypes/full/regression/kt287.jet b/compiler/testData/checkerWithErrorTypes/full/regression/kt287.jet new file mode 100644 index 00000000000..e6f5ff1aeb0 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/regression/kt287.jet @@ -0,0 +1,5 @@ +// KT-287 Infer constructor type arguments +import java.util.* + +fun attributes() : Map = HashMap() // Should be inferred; +val attributes : Map = HashMap() // Should be inferred; From 8ab1f1b420d709080799c1dff2cc1f50baf1af31 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Nov 2011 21:51:01 +0300 Subject: [PATCH 11/14] KT-287 Infer constructor type arguments More test data --- .../checkerWithErrorTypes/full/regression/kt287.jet | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/testData/checkerWithErrorTypes/full/regression/kt287.jet b/compiler/testData/checkerWithErrorTypes/full/regression/kt287.jet index e6f5ff1aeb0..b0efe2e6460 100644 --- a/compiler/testData/checkerWithErrorTypes/full/regression/kt287.jet +++ b/compiler/testData/checkerWithErrorTypes/full/regression/kt287.jet @@ -3,3 +3,9 @@ import java.util.* fun attributes() : Map = HashMap() // Should be inferred; val attributes : Map = HashMap() // Should be inferred; + +fun foo(m : Map) {} + +fun test() { + foo(HashMap()) +} From c6f6f182b68cd16d7237004fddbc88628ec00d13 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Nov 2011 22:15:37 +0300 Subject: [PATCH 12/14] KT-353 Generic type argument inference sometimes doesn't work KT-459 Type argument inference fails when class names are fully qualified --- .../BasicExpressionTypingVisitor.java | 15 ++++----- .../full/regression/kt459.jet | 8 +++++ .../quick/regressions/kt353.jet | 32 +++++++++++++++++++ 3 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 compiler/testData/checkerWithErrorTypes/full/regression/kt459.jet create mode 100644 compiler/testData/checkerWithErrorTypes/quick/regressions/kt353.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index 1939b0192d0..4e19232a122 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -57,7 +57,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } } else { - return getSelectorReturnType(NO_RECEIVER, null, expression, context); // TODO : Extensions to this + return DataFlowUtils.checkType(getSelectorReturnType(NO_RECEIVER, null, expression, context), expression, context); // TODO : Extensions to this } return null; } @@ -419,14 +419,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } @Override - public JetType visitQualifiedExpression(JetQualifiedExpression expression, ExpressionTypingContext contextWithExpectedType) { - ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE); + public JetType visitQualifiedExpression(JetQualifiedExpression expression, ExpressionTypingContext context) { // TODO : functions as values JetExpression selectorExpression = expression.getSelectorExpression(); JetExpression receiverExpression = expression.getReceiverExpression(); + ExpressionTypingContext contextWithNoExpectedType = context.replaceExpectedType(NO_EXPECTED_TYPE); JetType receiverType = facade.getType(receiverExpression, - context - .replaceExpectedType(NO_EXPECTED_TYPE) + contextWithNoExpectedType .replaceExpectedReturnType(NO_EXPECTED_TYPE) .replaceNamespacesAllowed(true)); if (selectorExpression == null) return null; @@ -462,7 +461,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { if (result != null) { context.trace.record(BindingContext.EXPRESSION_TYPE, selectorExpression, result); } - return DataFlowUtils.checkType(result, expression, contextWithExpectedType); + return DataFlowUtils.checkType(result, expression, context); } private void propagateConstantValues(JetQualifiedExpression expression, ExpressionTypingContext context, JetSimpleNameExpression selectorExpression) { @@ -508,14 +507,14 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { VariableDescriptor variableDescriptor = context.replaceBindingTrace(temporaryTrace).resolveSimpleProperty(receiver, callOperationNode, nameExpression); if (variableDescriptor != null) { temporaryTrace.commit(); - return DataFlowUtils.checkType(variableDescriptor.getOutType(), nameExpression, context); + return variableDescriptor.getOutType(); } ExpressionTypingContext newContext = receiver.exists() ? context.replaceScope(receiver.getType().getMemberScope()) : context; JetType jetType = lookupNamespaceOrClassObject(nameExpression, nameExpression.getReferencedName(), newContext); if (jetType == null) { context.trace.report(UNRESOLVED_REFERENCE.on(nameExpression)); } - return DataFlowUtils.checkType(jetType, nameExpression, context); + return jetType; } else if (selectorExpression instanceof JetQualifiedExpression) { JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) selectorExpression; diff --git a/compiler/testData/checkerWithErrorTypes/full/regression/kt459.jet b/compiler/testData/checkerWithErrorTypes/full/regression/kt459.jet new file mode 100644 index 00000000000..e2dd54ef0da --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/regression/kt459.jet @@ -0,0 +1,8 @@ +// KT-459 Type argument inference fails when class names are fully qualified + +fun test() { + val attributes : java.util.HashMap = java.util.HashMap() // failure! + attributes["href"] = "1" // inference fails, but it shouldn't +} + +fun java.util.Map.set(key : K, value : V) {}//= this.put(key, value) \ No newline at end of file diff --git a/compiler/testData/checkerWithErrorTypes/quick/regressions/kt353.jet b/compiler/testData/checkerWithErrorTypes/quick/regressions/kt353.jet new file mode 100644 index 00000000000..5a7c9569795 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/quick/regressions/kt353.jet @@ -0,0 +1,32 @@ +// KT-353 Generic type argument inference sometimes doesn't work + +trait A { + fun gen() : T +} + +fun foo(a: A) { + val g : fun() : Unit = { + a.gen() //it works: Unit is derived + } + + val u: Unit = a.gen() //type mismatch, but Unit can be derived + + if (true) { + a.gen() //it works: Unit is derived + } + + val b : fun() : Unit = { + if (true) { + a.gen() //type mismatch, but Unit can be derived + } + else { + () + } + } + + val f : fun() : Int = { () : Int => + a.gen() //type mismatch, but Int can be derived + } + + a.gen() //it works: Unit is derived +} From a28716af1d207adfd3e1f764105b96068b22adf1 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Nov 2011 22:29:48 +0300 Subject: [PATCH 13/14] Constant propagation guarded by the class jet.Number where the properties are defined. NOTE: currently, local extension properties are not allowed, but they may be allowed in future versions, and the old propagation would break --- .../org/jetbrains/jet/lang/types/JetStandardLibrary.java | 8 ++++++++ .../types/expressions/BasicExpressionTypingVisitor.java | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java index 6c641719c65..62923713125 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardLibrary.java @@ -49,6 +49,7 @@ public class JetStandardLibrary { private JetScope libraryScope; + private ClassDescriptor numberClass; private ClassDescriptor byteClass; private ClassDescriptor charClass; private ClassDescriptor shortClass; @@ -152,6 +153,7 @@ public class JetStandardLibrary { private void initStdClasses() { if(libraryScope == null) { this.libraryScope = JetStandardClasses.STANDARD_CLASSES_NAMESPACE.getMemberScope(); + this.numberClass = (ClassDescriptor) libraryScope.getClassifier("Number"); this.byteClass = (ClassDescriptor) libraryScope.getClassifier("Byte"); this.charClass = (ClassDescriptor) libraryScope.getClassifier("Char"); this.shortClass = (ClassDescriptor) libraryScope.getClassifier("Short"); @@ -221,6 +223,12 @@ public class JetStandardLibrary { } } + @NotNull + public ClassDescriptor getNumber() { + initStdClasses(); + return numberClass; + } + @NotNull public ClassDescriptor getByte() { initStdClasses(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index 4e19232a122..f9ebc9d4a9c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -468,7 +468,9 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { JetExpression receiverExpression = expression.getReceiverExpression(); CompileTimeConstant receiverValue = context.trace.getBindingContext().get(BindingContext.COMPILE_TIME_VALUE, receiverExpression); CompileTimeConstant wholeExpressionValue = context.trace.getBindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression); - if (wholeExpressionValue == null && receiverValue != null && !(receiverValue instanceof ErrorValue) && receiverValue.getValue() instanceof Number) { + DeclarationDescriptor declarationDescriptor = context.trace.getBindingContext().get(BindingContext.REFERENCE_TARGET, selectorExpression); + if (wholeExpressionValue == null && receiverValue != null && !(receiverValue instanceof ErrorValue) && receiverValue.getValue() instanceof Number + && context.semanticServices.getStandardLibrary().getNumber() == declarationDescriptor) { Number value = (Number) receiverValue.getValue(); String referencedName = selectorExpression.getReferencedName(); if (OperatorConventions.NUMBER_CONVERSIONS.contains(referencedName)) { From abec6933cf5740da8d616c18c2b0083067c0e719 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Thu, 10 Nov 2011 12:59:29 +0400 Subject: [PATCH 14/14] KT-499 allow class object in static inner classes like this: === class A { class object { class B { class object { } } } } === --- .../lang/descriptors/MutableClassDescriptor.java | 13 ++++++++++++- .../quick/InnerClassClassObject.jet | 13 +++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java index 3e500172016..0a32795967a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptor.java @@ -61,7 +61,7 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme @Override public ClassObjectStatus setClassObjectDescriptor(@NotNull MutableClassDescriptor classObjectDescriptor) { if (this.classObjectDescriptor != null) return ClassObjectStatus.DUPLICATE; - if (!(this.getContainingDeclaration() instanceof NamespaceDescriptor)) { + if (!isStatic(this.getContainingDeclaration())) { return ClassObjectStatus.NOT_ALLOWED; } assert classObjectDescriptor.getKind() == ClassKind.OBJECT; @@ -69,6 +69,17 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme return ClassObjectStatus.OK; } + private static boolean isStatic(DeclarationDescriptor declarationDescriptor) { + if (declarationDescriptor instanceof NamespaceDescriptor) { + return true; + } else if (declarationDescriptor instanceof ClassDescriptor) { + ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; + return classDescriptor.getKind() == ClassKind.OBJECT || classDescriptor.getKind() == ClassKind.ENUM_CLASS; + } else { + return false; + } + } + @Nullable public MutableClassDescriptor getClassObjectDescriptor() { return classObjectDescriptor; diff --git a/compiler/testData/checkerWithErrorTypes/quick/InnerClassClassObject.jet b/compiler/testData/checkerWithErrorTypes/quick/InnerClassClassObject.jet index 0d8f5da30f2..39ff4413733 100644 --- a/compiler/testData/checkerWithErrorTypes/quick/InnerClassClassObject.jet +++ b/compiler/testData/checkerWithErrorTypes/quick/InnerClassClassObject.jet @@ -5,3 +5,16 @@ class A { class object { } } } + +class B { + class object { + class B { + class object { + class C { + class object { } + } + } + } + } +} +