JET-25: isOverridable implemented
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user