diff --git a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaTypeTransformer.java b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaTypeTransformer.java index 86836d424a9..0a504f1afe7 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaTypeTransformer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaTypeTransformer.java @@ -68,13 +68,7 @@ public class JavaTypeTransformer { if (psiClass instanceof PsiTypeParameter) { PsiTypeParameter typeParameter = (PsiTypeParameter) psiClass; TypeParameterDescriptor typeParameterDescriptor = resolver.resolveTypeParameter(typeParameter); - return new JetTypeImpl( - Collections.emptyList(), - typeParameterDescriptor.getTypeConstructor(), - !TypeUtils.hasNullableBound(typeParameterDescriptor), - Collections.emptyList(), - typeParameterDescriptor.getBoundsAsType().getMemberScope()); - + return typeParameterDescriptor.getDefaultType(); } else { JetType jetAnalog = getClassTypesMap().get(psiClass.getQualifiedName()); diff --git a/idea/src/org/jetbrains/jet/lang/types/ClassifierDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/ClassifierDescriptor.java index 46e4a2b4173..4f382d98a53 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ClassifierDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/types/ClassifierDescriptor.java @@ -8,4 +8,7 @@ import org.jetbrains.annotations.NotNull; public interface ClassifierDescriptor extends DeclarationDescriptor { @NotNull TypeConstructor getTypeConstructor(); + + @NotNull + JetType getDefaultType(); } diff --git a/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorUtil.java b/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorUtil.java index d8d820477de..ab224271be5 100644 --- a/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorUtil.java +++ b/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorUtil.java @@ -1,5 +1,8 @@ package org.jetbrains.jet.lang.types; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Maps; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.JetSemanticServices; @@ -103,4 +106,125 @@ public class FunctionDescriptorUtil { parameterScope.addLabeledDeclaration(descriptor); return parameterScope; } + + public static class OverrideCompatibilityInfo { + + private static final OverrideCompatibilityInfo SUCCESS = new OverrideCompatibilityInfo(false, "SUCCESS"); + + @NotNull + public static OverrideCompatibilityInfo nameMismatch() { + return new OverrideCompatibilityInfo(true, "nameMismatch"); // TODO + } + + @NotNull + public static OverrideCompatibilityInfo typeParameterNumberMismatch() { + return new OverrideCompatibilityInfo(true, "typeParameterNumberMismatch"); // TODO + } + + @NotNull + public static OverrideCompatibilityInfo valueParameterNumberMismatch() { + return new OverrideCompatibilityInfo(true, "valueParameterNumberMismatch"); // TODO + } + + @NotNull + public static OverrideCompatibilityInfo boundsMismatch(TypeParameterDescriptor superTypeParameter, TypeParameterDescriptor subTypeParameter) { + return new OverrideCompatibilityInfo(true, "boundsMismatch"); // TODO + } + + @NotNull + public static OverrideCompatibilityInfo valueParameterTypeMismatch(ValueParameterDescriptor superValueParameter, ValueParameterDescriptor subValueParameter) { + return new OverrideCompatibilityInfo(true, "valueParameterTypeMismatch"); // TODO + } + + @NotNull + public static OverrideCompatibilityInfo returnTypeMismatch(JetType substitutedSuperReturnType, JetType unsubstitutedSubReturnType) { + return new OverrideCompatibilityInfo(true, "returnTypeMismatch: " + unsubstitutedSubReturnType + " >< " + substitutedSuperReturnType); // TODO + } + + @NotNull + public static OverrideCompatibilityInfo success() { + return SUCCESS; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + private final boolean isError; + private final String message; + + public OverrideCompatibilityInfo(boolean error, String message) { + isError = error; + this.message = message; + } + + public boolean isError() { + return isError; + } + + public String getMessage() { + return message; + } + } + + @NotNull + public static OverrideCompatibilityInfo isOverridableWith(@NotNull JetTypeChecker typeChecker, @NotNull FunctionDescriptor superFunction, @NotNull FunctionDescriptor subFunction) { + if (!superFunction.getName().equals(subFunction.getName())) { + return OverrideCompatibilityInfo.nameMismatch(); + } + + // TODO : Visibility + + if (superFunction.getTypeParameters().size() != subFunction.getTypeParameters().size()) { + return OverrideCompatibilityInfo.typeParameterNumberMismatch(); + } + + if (superFunction.getUnsubstitutedValueParameters().size() != subFunction.getUnsubstitutedValueParameters().size()) { + return OverrideCompatibilityInfo.valueParameterNumberMismatch(); + } + + List superTypeParameters = superFunction.getTypeParameters(); + List subTypeParameters = subFunction.getTypeParameters(); + + Map substitutionContext = Maps.newHashMap(); + BiMap axioms = HashBiMap.create(); + for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) { + TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i); + TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i); + substitutionContext.put( + superTypeParameter.getTypeConstructor(), + new TypeProjection(subTypeParameter.getDefaultType())); + axioms.put(superTypeParameter.getTypeConstructor(), subTypeParameter.getTypeConstructor()); + } + + for (int i = 0, typeParametersSize = superTypeParameters.size(); i < typeParametersSize; i++) { + TypeParameterDescriptor superTypeParameter = superTypeParameters.get(i); + TypeParameterDescriptor subTypeParameter = subTypeParameters.get(i); + + + if (!JetTypeImpl.equalTypes(superTypeParameter.getBoundsAsType(), subTypeParameter.getBoundsAsType(), axioms)) { + return OverrideCompatibilityInfo.boundsMismatch(superTypeParameter, subTypeParameter); + } + } + + List superValueParameters = superFunction.getUnsubstitutedValueParameters(); + List subValueParameters = subFunction.getUnsubstitutedValueParameters(); + for (int i = 0, unsubstitutedValueParametersSize = superValueParameters.size(); i < unsubstitutedValueParametersSize; i++) { + ValueParameterDescriptor superValueParameter = superValueParameters.get(i); + ValueParameterDescriptor subValueParameter = subValueParameters.get(i); + + if (!JetTypeImpl.equalTypes(superValueParameter.getOutType(), subValueParameter.getOutType(), axioms)) { + return OverrideCompatibilityInfo.valueParameterTypeMismatch(superValueParameter, subValueParameter); + } + } + + // TODO : Default values, varargs etc + + TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitutionContext); + JetType substitutedSuperReturnType = typeSubstitutor.substitute(superFunction.getUnsubstitutedReturnType(), Variance.OUT_VARIANCE); + assert substitutedSuperReturnType != null; + if (!typeChecker.isSubtypeOf(subFunction.getUnsubstitutedReturnType(), substitutedSuperReturnType)) { + return OverrideCompatibilityInfo.returnTypeMismatch(substitutedSuperReturnType, subFunction.getUnsubstitutedReturnType()); + } + + return OverrideCompatibilityInfo.success(); + } } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index 2bb2efe33d5..09ba03e0aba 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -362,7 +362,7 @@ public class JetTypeChecker { case INVARIANT: switch (superArgument.getProjectionKind()) { case INVARIANT: - if (!JetTypeImpl.equalTypes(subArgumentType, superArgumentType)) { + if (!subArgumentType.equals(superArgumentType)) { return false; } break; diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeImpl.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeImpl.java index 5332fd82f96..bac64ee6332 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeImpl.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeImpl.java @@ -1,5 +1,7 @@ package org.jetbrains.jet.lang.types; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.resolve.JetScope; @@ -12,6 +14,8 @@ import java.util.List; */ public final class JetTypeImpl extends AnnotatedImpl implements JetType { + private static final HashBiMap EMPTY_AXIOMS = HashBiMap.create(); + private final TypeConstructor constructor; private final List arguments; private final boolean nullable; @@ -86,7 +90,7 @@ public final class JetTypeImpl extends AnnotatedImpl implements JetType { JetTypeImpl type = (JetTypeImpl) o; // TODO - return equalTypes(this, type); + return equalTypes(this, type, EMPTY_AXIOMS); // if (nullable != type.nullable) return false; // if (arguments != null ? !arguments.equals(type.arguments) : type.arguments != null) return false; // if (constructor != null ? !constructor.equals(type.constructor) : type.constructor != null) return false; @@ -104,25 +108,33 @@ public final class JetTypeImpl extends AnnotatedImpl implements JetType { } - public static boolean equalTypes(@NotNull JetType type1, @NotNull JetType type2) { + public static boolean equalTypes(@NotNull JetType type1, @NotNull JetType type2, @NotNull BiMap equalityAxioms) { if (type1.isNullable() != type2.isNullable()) { return false; } - if (!type1.getConstructor().equals(type2.getConstructor())) { - return false; + TypeConstructor constructor1 = type1.getConstructor(); + TypeConstructor constructor2 = type2.getConstructor(); + if (!constructor1.equals(constructor2)) { + TypeConstructor img1 = equalityAxioms.get(constructor1); + TypeConstructor img2 = equalityAxioms.get(constructor2); + if (!(img1 != null && img1.equals(constructor2)) && + !(img2 != null && img2.equals(constructor1))) { + return false; + } } List type1Arguments = type1.getArguments(); List type2Arguments = type2.getArguments(); if (type1Arguments.size() != type2Arguments.size()) { return false; } + for (int i = 0; i < type1Arguments.size(); i++) { TypeProjection typeProjection1 = type1Arguments.get(i); TypeProjection typeProjection2 = type2Arguments.get(i); if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) { return false; } - if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) { + if (!equalTypes(typeProjection1.getType(), typeProjection2.getType(), equalityAxioms)) { return false; } } diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java index 8966b99ee24..03306212b51 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeParameterDescriptor.java @@ -14,6 +14,7 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement private final Set upperBounds; private final TypeConstructor typeConstructor; private final JetType boundsAsType; + private JetType type; public TypeParameterDescriptor( @NotNull DeclarationDescriptor containingDeclaration, @@ -84,4 +85,18 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement public R accept(DeclarationDescriptorVisitor visitor, D data) { return visitor.visitTypeParameterDescriptor(this, data); } -} + + @NotNull + @Override + public JetType getDefaultType() { + if (type == null) { + type = new JetTypeImpl( + Collections.emptyList(), + getTypeConstructor(), + TypeUtils.hasNullableBound(this), + Collections.emptyList(), + getBoundsAsType().getMemberScope()); + } + return type; + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/types/JetOverridingTest.java b/idea/tests/org/jetbrains/jet/types/JetOverridingTest.java new file mode 100644 index 00000000000..3d621203191 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/types/JetOverridingTest.java @@ -0,0 +1,147 @@ +package org.jetbrains.jet.types; + +import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; +import com.intellij.openapi.application.PathManager; +import org.jetbrains.jet.lang.ErrorHandler; +import org.jetbrains.jet.lang.JetSemanticServices; +import org.jetbrains.jet.lang.psi.JetChangeUtil; +import org.jetbrains.jet.lang.psi.JetFunction; +import org.jetbrains.jet.lang.resolve.ClassDescriptorResolver; +import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.parsing.JetParsingTest; + +import java.io.File; + +/** + * @author abreslav + */ +public class JetOverridingTest extends LightDaemonAnalyzerTestCase { + + private ModuleDescriptor root = new ModuleDescriptor("test_root"); + private JetStandardLibrary library; + private JetSemanticServices semanticServices; + private ClassDescriptorResolver classDescriptorResolver; + + @Override + public void setUp() throws Exception { + super.setUp(); + library = JetStandardLibrary.getJetStandardLibrary(getProject()); + semanticServices = JetSemanticServices.createSemanticServices(library, ErrorHandler.DO_NOTHING); + classDescriptorResolver = semanticServices.getClassDescriptorResolver(BindingTrace.DUMMY); + } + + @Override + protected String getTestDataPath() { + return getHomeDirectory() + "/idea/testData"; + } + + private static String getHomeDirectory() { + return new File(PathManager.getResourceRoot(JetParsingTest.class, "/org/jetbrains/jet/parsing/JetParsingTest.class")).getParentFile().getParentFile().getParent(); + } + + public void testBasic() throws Exception { + assertOverridable( + "fun a() : Int", + "fun a() : Int"); + + assertOverridable( + "fun a() : T1", + "fun a() : T"); + + assertOverridable( + "fun a(a : T1) : T1", + "fun a(a : T) : T"); + + assertOverridable( + "fun a(a : T1) : T1", + "fun a(a : T) : T"); + + assertOverridable( + "fun a(a : T1) : T1", + "fun a(a : T) : Y"); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + assertNotOverridable( + "fun ab() : Int", + "fun a() : Int"); + + assertNotOverridable( + "fun a() : Int", + "fun a() : Any"); + + assertNotOverridable( + "fun a(a : Int) : Int", + "fun a() : Int"); + + assertNotOverridable( + "fun a() : Int", + "fun a(a : Int) : Int"); + + assertNotOverridable( + "fun a(a : Int?) : Int", + "fun a(a : Int) : Int"); + + assertNotOverridable( + "fun a(a : Int) : Int", + "fun a(a : Int) : Int"); + + assertNotOverridable( + "fun a(a : T1) : T1", + "fun a(a : T) : T"); + + assertNotOverridable( + "fun a(a : T1) : T1", + "fun a(a : Y) : T"); + + assertNotOverridable( + "fun a(a : T1) : X", + "fun a(a : T) : T"); + + assertOverridable( + "fun a>(a : Array) : T1", + "fun a>(a : Array) : T"); + + assertNotOverridable( + "fun a>(a : Array) : T1", + "fun a>(a : Array) : T"); + + assertNotOverridable( + "fun a>(a : Array) : T1", + "fun a>(a : Array) : T"); + + assertNotOverridable( + "fun a>(a : Array) : T1", + "fun a>(a : Array) : T"); + + assertNotOverridable( + "fun a>(a : Array) : T1", + "fun a>(a : Array) : T"); + + assertNotOverridable( + "fun a>(a : Array<*>) : T1", + "fun a>(a : Array) : T"); + + } + + private void assertOverridable(String superFun, String subFun) { + assertOverridabilityRelation(superFun, subFun, false); + } + + private void assertNotOverridable(String superFun, String subFun) { + assertOverridabilityRelation(superFun, subFun, true); + } + + private void assertOverridabilityRelation(String superFun, String subFun, boolean expectedIsError) { + FunctionDescriptor a = makeFunction(superFun); + FunctionDescriptor b = makeFunction(subFun); + FunctionDescriptorUtil.OverrideCompatibilityInfo overridableWith = FunctionDescriptorUtil.isOverridableWith(semanticServices.getTypeChecker(), a, b); + assertEquals(overridableWith.getMessage(), expectedIsError, overridableWith.isError()); + } + + private FunctionDescriptor makeFunction(String funDecl) { + JetFunction function = JetChangeUtil.createFunction(getProject(), funDecl); + return classDescriptorResolver.resolveFunctionDescriptor(root, library.getLibraryScope(), function); + } +} diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index a81074ac0f1..3c5f0c47592 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -466,7 +466,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { subtypes.add(makeType(type)); } JetType result = semanticServices.getTypeChecker().commonSupertype(subtypes); - assertTrue(result + " != " + expected, JetTypeImpl.equalTypes(result, makeType(expected))); + assertTrue(result + " != " + expected, result.equals(makeType(expected))); } private void assertSubtypingRelation(String subtype, String supertype, boolean expected) { @@ -483,7 +483,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { Project project = getProject(); JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY, JetFlowInformationProvider.NONE).getType(classDefinitions.BASIC_SCOPE, jetExpression, false); - assertTrue(type + " != " + expectedType, JetTypeImpl.equalTypes(type, expectedType)); + assertTrue(type + " != " + expectedType, type.equals(expectedType)); } private void assertErrorType(String expression) {