JET-25: isOverridable implemented

This commit is contained in:
Andrey Breslav
2011-05-05 21:58:57 +04:00
parent f84bdf0d54
commit 32f5e3c5b9
8 changed files with 311 additions and 16 deletions
@@ -68,13 +68,7 @@ public class JavaTypeTransformer {
if (psiClass instanceof PsiTypeParameter) {
PsiTypeParameter typeParameter = (PsiTypeParameter) psiClass;
TypeParameterDescriptor typeParameterDescriptor = resolver.resolveTypeParameter(typeParameter);
return new JetTypeImpl(
Collections.<Annotation>emptyList(),
typeParameterDescriptor.getTypeConstructor(),
!TypeUtils.hasNullableBound(typeParameterDescriptor),
Collections.<TypeProjection>emptyList(),
typeParameterDescriptor.getBoundsAsType().getMemberScope());
return typeParameterDescriptor.getDefaultType();
}
else {
JetType jetAnalog = getClassTypesMap().get(psiClass.getQualifiedName());
@@ -8,4 +8,7 @@ import org.jetbrains.annotations.NotNull;
public interface ClassifierDescriptor extends DeclarationDescriptor {
@NotNull
TypeConstructor getTypeConstructor();
@NotNull
JetType getDefaultType();
}
@@ -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<TypeParameterDescriptor> superTypeParameters = superFunction.getTypeParameters();
List<TypeParameterDescriptor> subTypeParameters = subFunction.getTypeParameters();
Map<TypeConstructor, TypeProjection> substitutionContext = Maps.newHashMap();
BiMap<TypeConstructor, TypeConstructor> 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<ValueParameterDescriptor> superValueParameters = superFunction.getUnsubstitutedValueParameters();
List<ValueParameterDescriptor> 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();
}
}
@@ -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;
@@ -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<TypeConstructor,TypeConstructor> EMPTY_AXIOMS = HashBiMap.<TypeConstructor, TypeConstructor>create();
private final TypeConstructor constructor;
private final List<TypeProjection> 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<TypeConstructor, TypeConstructor> 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<TypeProjection> type1Arguments = type1.getArguments();
List<TypeProjection> type2Arguments = type2.getArguments();
if (type1Arguments.size() != type2Arguments.size()) {
return false;
}
for (int i = 0; i < type1Arguments.size(); i++) {
TypeProjection typeProjection1 = type1Arguments.get(i);
TypeProjection typeProjection2 = type2Arguments.get(i);
if (typeProjection1.getProjectionKind() != typeProjection2.getProjectionKind()) {
return false;
}
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType())) {
if (!equalTypes(typeProjection1.getType(), typeProjection2.getType(), equalityAxioms)) {
return false;
}
}
@@ -14,6 +14,7 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement
private final Set<JetType> 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, D> R accept(DeclarationDescriptorVisitor<R, D> visitor, D data) {
return visitor.visitTypeParameterDescriptor(this, data);
}
}
@NotNull
@Override
public JetType getDefaultType() {
if (type == null) {
type = new JetTypeImpl(
Collections.<Annotation>emptyList(),
getTypeConstructor(),
TypeUtils.hasNullableBound(this),
Collections.<TypeProjection>emptyList(),
getBoundsAsType().getMemberScope());
}
return type;
}
}
@@ -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>() : T1",
"fun a<T>() : T");
assertOverridable(
"fun a<T1>(a : T1) : T1",
"fun a<T>(a : T) : T");
assertOverridable(
"fun a<T1, X : T1>(a : T1) : T1",
"fun a<T, Y : T>(a : T) : T");
assertOverridable(
"fun a<T1, X : T1>(a : T1) : T1",
"fun a<T, Y : T>(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<T>(a : Int) : Int",
"fun a(a : Int) : Int");
assertNotOverridable(
"fun a<T1, X : T1>(a : T1) : T1",
"fun a<T, Y>(a : T) : T");
assertNotOverridable(
"fun a<T1, X : T1>(a : T1) : T1",
"fun a<T, Y : T>(a : Y) : T");
assertNotOverridable(
"fun a<T1, X : T1>(a : T1) : X",
"fun a<T, Y : T>(a : T) : T");
assertOverridable(
"fun a<T1, X : Array<out T1>>(a : Array<in T1>) : T1",
"fun a<T, Y : Array<out T>>(a : Array<in T>) : T");
assertNotOverridable(
"fun a<T1, X : Array<T1>>(a : Array<in T1>) : T1",
"fun a<T, Y : Array<out T>>(a : Array<in T>) : T");
assertNotOverridable(
"fun a<T1, X : Array<out T1>>(a : Array<in T1>) : T1",
"fun a<T, Y : Array<in T>>(a : Array<in T>) : T");
assertNotOverridable(
"fun a<T1, X : Array<out T1>>(a : Array<in T1>) : T1",
"fun a<T, Y : Array<*>>(a : Array<in T>) : T");
assertNotOverridable(
"fun a<T1, X : Array<out T1>>(a : Array<in T1>) : T1",
"fun a<T, Y : Array<out T>>(a : Array<out T>) : T");
assertNotOverridable(
"fun a<T1, X : Array<out T1>>(a : Array<*>) : T1",
"fun a<T, Y : Array<out T>>(a : Array<in T>) : 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);
}
}
@@ -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) {