From 725ac373eed77f4b7a12aa61c6964ba9d63c6ab6 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 9 Feb 2011 17:26:11 +0300 Subject: [PATCH] Property resolve with substitutions --- .../lang/resolve/ClassDescriptorResolver.java | 23 ++++--- .../jetbrains/jet/lang/types/ErrorType.java | 65 ++++++++++++++++--- .../jet/lang/types/JetStandardClasses.java | 8 +-- .../jet/lang/types/JetTypeChecker.java | 21 ++++-- ...LazySubstitutedPropertyDescriptorImpl.java | 36 ++++++++++ .../org/jetbrains/jet/lang/types/Named.java | 8 +++ .../jet/lang/types/NamedAnnotatedImpl.java | 3 +- .../jet/lang/types/PropertyDescriptor.java | 19 +----- .../lang/types/PropertyDescriptorImpl.java | 24 +++++++ .../jet/lang/types/TypeMemberDomain.java | 16 ++--- .../jet/types/JetTypeCheckerTest.java | 13 +++- 11 files changed, 182 insertions(+), 54 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/lang/types/LazySubstitutedPropertyDescriptorImpl.java create mode 100644 idea/src/org/jetbrains/jet/lang/types/Named.java create mode 100644 idea/src/org/jetbrains/jet/lang/types/PropertyDescriptorImpl.java diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index 76e9ba00b31..4c53ae66617 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -48,7 +48,7 @@ public class ClassDescriptorResolver { // TODO : Cache!!! return new TypeMemberDomain() { @Override - public PropertyDescriptor getProperty(Type receiverType, @NotNull String name) { + public PropertyDescriptor getProperty(Type contextType, @NotNull String name) { // TODO : primary constructor parameters List declarations = classElement.getDeclarations(); for (JetDeclaration declaration : declarations) { @@ -59,7 +59,7 @@ public class ClassDescriptorResolver { JetProperty property = (JetProperty) declaration; if (property.getPropertyTypeRef() != null) { - return resolvePropertyDescriptor(outerScope, property); + return substituteInPropertyDescriptor(contextType, resolvePropertyDescriptor(typeParameterScope, property)); } else { // TODO : Caution: a cyclic dependency possible throw new UnsupportedOperationException(); @@ -68,7 +68,7 @@ public class ClassDescriptorResolver { } for (Type supertype : supertypes) { - PropertyDescriptor property = supertype.getMemberDomain().getProperty(JetTypeChecker.INSTANCE.substitute(receiverType, supertype), name); + PropertyDescriptor property = supertype.getMemberDomain().getProperty(JetTypeChecker.INSTANCE.substitute(contextType, supertype, Variance.INVARIANT), name); if (property != null) { return property; } @@ -77,23 +77,30 @@ public class ClassDescriptorResolver { } @Override - public ClassDescriptor getClassDescriptor(@NotNull Type type, String name) { + public ClassDescriptor getClassDescriptor(@NotNull Type contextType, String name) { throw new UnsupportedOperationException(); // TODO } @NotNull @Override - public Collection getMethods(Type receiverType, String name) { + public Collection getMethods(Type contextType, String name) { throw new UnsupportedOperationException(); // TODO } @Override - public ExtensionDescriptor getExtension(Type receiverType, String name) { + public ExtensionDescriptor getExtension(Type contextType, String name) { throw new UnsupportedOperationException(); // TODO } }; } + private PropertyDescriptor substituteInPropertyDescriptor(Type contextType, PropertyDescriptor propertyDescriptor) { + if (contextType.getConstructor().getParameters().isEmpty()) { + return propertyDescriptor; + } + return new LazySubstitutedPropertyDescriptorImpl(propertyDescriptor, contextType); + } + private static List resolveTypeParameters(TypeParameterExtensibleScope extensibleScope, List typeParameters) { // TODO : When-clause List result = new ArrayList(); @@ -135,7 +142,7 @@ public class ClassDescriptorResolver { @NotNull public PropertyDescriptor resolvePropertyDescriptor(@NotNull JetScope scope, @NotNull JetParameter parameter) { - return new PropertyDescriptor( + return new PropertyDescriptorImpl( AttributeResolver.INSTANCE.resolveAttributes(parameter.getModifierList()), parameter.getName(), TypeResolver.INSTANCE.resolveType(scope, parameter.getTypeReference())); @@ -155,7 +162,7 @@ public class ClassDescriptorResolver { type = TypeResolver.INSTANCE.resolveType(scope, propertyTypeRef); } - return new PropertyDescriptor( + return new PropertyDescriptorImpl( AttributeResolver.INSTANCE.resolveAttributes(property.getModifierList()), property.getName(), type); diff --git a/idea/src/org/jetbrains/jet/lang/types/ErrorType.java b/idea/src/org/jetbrains/jet/lang/types/ErrorType.java index 776008bba3d..4777883b2c5 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ErrorType.java +++ b/idea/src/org/jetbrains/jet/lang/types/ErrorType.java @@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.Collections; +import java.util.List; /** * @author abreslav @@ -11,33 +12,81 @@ import java.util.Collections; public class ErrorType { private static final TypeMemberDomain ERROR_DOMAIN = new TypeMemberDomain() { @Override - public ClassDescriptor getClassDescriptor(@NotNull Type type, String name) { + public ClassDescriptor getClassDescriptor(@NotNull Type contextType, String name) { throw new UnsupportedOperationException(); // TODO } @NotNull @Override - public Collection getMethods(Type receiverType, String name) { + public Collection getMethods(Type contextType, String name) { throw new UnsupportedOperationException(); // TODO } @Override - public PropertyDescriptor getProperty(Type receiverType, String name) { + public PropertyDescriptor getProperty(Type contextType, String name) { throw new UnsupportedOperationException(); // TODO } @Override - public ExtensionDescriptor getExtension(Type receiverType, String name) { + public ExtensionDescriptor getExtension(Type contextType, String name) { throw new UnsupportedOperationException(); // TODO } }; - private static final TypeConstructor ERROR = new TypeConstructor(Collections.emptyList(), false, "ERROR", Collections.emptyList(), Collections.emptyList()); + + private ErrorType() {} public static Type createErrorType(String debugMessage) { - return new TypeImpl(ERROR, ERROR_DOMAIN); + return createErrorType(debugMessage, ERROR_DOMAIN); } - public static boolean isError(Type type) { - return type.getConstructor() == ERROR; + private static Type createErrorType(String debugMessage, TypeMemberDomain memberDomain) { + return new ErrorTypeImpl(new TypeConstructor(Collections.emptyList(), false, "[ERROR : " + debugMessage + "]", Collections.emptyList(), Collections.emptyList()), memberDomain); + } + + public static Type createWrongVarianceErrorType(TypeProjection value) { + return createErrorType(value + " is not allowed here]", value.getType().getMemberDomain()); + } + + public static boolean isErrorType(Type type) { + return type instanceof ErrorTypeImpl; + } + + private static class ErrorTypeImpl implements Type { + + private final TypeConstructor constructor; + private final TypeMemberDomain memberDomain; + + private ErrorTypeImpl(TypeConstructor constructor, TypeMemberDomain memberDomain) { + this.constructor = constructor; + this.memberDomain = memberDomain; + } + + @NotNull + @Override + public TypeConstructor getConstructor() { + return constructor; + } + + @NotNull + @Override + public List getArguments() { + return Collections.emptyList(); + } + + @Override + public boolean isNullable() { + return false; + } + + @NotNull + @Override + public TypeMemberDomain getMemberDomain() { + return memberDomain; + } + + @Override + public List getAttributes() { + return Collections.emptyList(); + } } } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java index d99ff99216b..f562fbdb8a7 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java @@ -48,23 +48,23 @@ public class JetStandardClasses { public static final TypeMemberDomain STUB = new TypeMemberDomain() { @Override - public ClassDescriptor getClassDescriptor(@NotNull Type type, String name) { + public ClassDescriptor getClassDescriptor(@NotNull Type contextType, String name) { throw new UnsupportedOperationException(); // TODO } @NotNull @Override - public Collection getMethods(Type receiverType, String name) { + public Collection getMethods(Type contextType, String name) { throw new UnsupportedOperationException(); // TODO } @Override - public PropertyDescriptor getProperty(Type receiverType, String name) { + public PropertyDescriptor getProperty(Type contextType, String name) { throw new UnsupportedOperationException(); // TODO } @Override - public ExtensionDescriptor getExtension(Type receiverType, String name) { + public ExtensionDescriptor getExtension(Type contextType, String name) { throw new UnsupportedOperationException(); // TODO } }; diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java index 92d33bb6001..967017d547f 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeChecker.java @@ -235,7 +235,7 @@ public class JetTypeChecker { Collection supertypes = thisType.getConstructor().getSupertypes(); for (Type declaredSupertype : supertypes) { if (declaredSupertype.getConstructor().equals(superclass.getTypeConstructor())) { - result[0] = substituteInType(getSubstitutionContext(thisType), declaredSupertype); + result[0] = substituteInType(getSubstitutionContext(thisType), declaredSupertype, Variance.INVARIANT); break; } } @@ -521,7 +521,7 @@ public class JetTypeChecker { if (visited.contains(supertypeConstructor)) { continue; } - Type substitutedSupertype = substituteInType(substitutionContext, supertype); + Type substitutedSupertype = substituteInType(substitutionContext, supertype, Variance.INVARIANT); dfs(substitutedSupertype, visited, handler); } handler.afterChildren(current); @@ -557,7 +557,7 @@ public class JetTypeChecker { for (Type immediateSupertype : constructor.getSupertypes()) { Type correspondingSupertype = findCorrespondingSupertype(immediateSupertype, supertype); if (correspondingSupertype != null) { - return substituteInType(getSubstitutionContext(subtype), correspondingSupertype); + return substituteInType(getSubstitutionContext(subtype), correspondingSupertype, Variance.INVARIANT); } } return null; @@ -576,9 +576,16 @@ public class JetTypeChecker { return parameterValues; } - private Type substituteInType(Map substitutionContext, Type type) { + private Type substituteInType(Map substitutionContext, Type type, Variance howThisTypeIsUsed) { TypeProjection value = substitutionContext.get(type.getConstructor()); - assert value == null : "For now this is used only for supertypes, thus no variables"; + if (value != null) { + Variance projectionKind = value.getProjectionKind(); + if (howThisTypeIsUsed.allowsInPosition() && !projectionKind.allowsInPosition() + || howThisTypeIsUsed.allowsOutPosition() && !projectionKind.allowsOutPosition()) { + return ErrorType.createWrongVarianceErrorType(value); + } + return value.getType(); + } return specializeType(type, substituteInArguments(substitutionContext, type)); } @@ -603,8 +610,8 @@ public class JetTypeChecker { } @NotNull - public Type substitute(@NotNull Type context, @NotNull Type subject) { - return substituteInType(getSubstitutionContext(context), subject); + public Type substitute(@NotNull Type context, @NotNull Type subject, @NotNull Variance howThisTypeIsUsed) { + return substituteInType(getSubstitutionContext(context), subject, howThisTypeIsUsed); } private Type specializeType(Type type, List newArguments) { diff --git a/idea/src/org/jetbrains/jet/lang/types/LazySubstitutedPropertyDescriptorImpl.java b/idea/src/org/jetbrains/jet/lang/types/LazySubstitutedPropertyDescriptorImpl.java new file mode 100644 index 00000000000..9a6aceea415 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/types/LazySubstitutedPropertyDescriptorImpl.java @@ -0,0 +1,36 @@ +package org.jetbrains.jet.lang.types; + +import java.util.List; + +/** + * @author abreslav + */ +public class LazySubstitutedPropertyDescriptorImpl implements PropertyDescriptor { + private final PropertyDescriptor propertyDescriptor; + private final Type contextType; + private Type propertyType = null; + + public LazySubstitutedPropertyDescriptorImpl(PropertyDescriptor propertyDescriptor, Type contextType) { + this.propertyDescriptor = propertyDescriptor; + this.contextType = contextType; + } + + @Override + public Type getType() { + if (propertyType == null) { + propertyType = JetTypeChecker.INSTANCE.substitute(contextType, propertyDescriptor.getType(), Variance.OUT_VARIANCE); + } + return propertyType; + } + + @Override + public List getAttributes() { + // TODO : Substitute, lazily + return propertyDescriptor.getAttributes(); + } + + @Override + public String getName() { + return propertyDescriptor.getName(); + } +} diff --git a/idea/src/org/jetbrains/jet/lang/types/Named.java b/idea/src/org/jetbrains/jet/lang/types/Named.java new file mode 100644 index 00000000000..c8677b44757 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/types/Named.java @@ -0,0 +1,8 @@ +package org.jetbrains.jet.lang.types; + +/** + * @author abreslav + */ +public interface Named { + String getName(); +} diff --git a/idea/src/org/jetbrains/jet/lang/types/NamedAnnotatedImpl.java b/idea/src/org/jetbrains/jet/lang/types/NamedAnnotatedImpl.java index ca54d2db47d..ff9ee98d345 100644 --- a/idea/src/org/jetbrains/jet/lang/types/NamedAnnotatedImpl.java +++ b/idea/src/org/jetbrains/jet/lang/types/NamedAnnotatedImpl.java @@ -5,7 +5,7 @@ import java.util.List; /** * @author abreslav */ -public abstract class NamedAnnotatedImpl extends AnnotatedImpl { +public abstract class NamedAnnotatedImpl extends AnnotatedImpl implements Named { private final String name; @@ -14,6 +14,7 @@ public abstract class NamedAnnotatedImpl extends AnnotatedImpl { this.name = name; } + @Override public String getName() { return name; } diff --git a/idea/src/org/jetbrains/jet/lang/types/PropertyDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/PropertyDescriptor.java index eddcd3d6b04..4a14cc0df28 100644 --- a/idea/src/org/jetbrains/jet/lang/types/PropertyDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/types/PropertyDescriptor.java @@ -1,23 +1,8 @@ package org.jetbrains.jet.lang.types; -import java.util.List; - /** * @author abreslav */ -public class PropertyDescriptor extends MemberDescriptorImpl { - private Type type; - - public PropertyDescriptor(List attributes, String name, Type type) { - super(attributes, name); - this.type = type; - } - - public PropertyDescriptor(List attributes, String name) { - this(attributes, name, null); - } - - public Type getType() { - return type; - } +public interface PropertyDescriptor extends Annotated, Named { + Type getType(); } diff --git a/idea/src/org/jetbrains/jet/lang/types/PropertyDescriptorImpl.java b/idea/src/org/jetbrains/jet/lang/types/PropertyDescriptorImpl.java new file mode 100644 index 00000000000..9f04bd494cc --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/types/PropertyDescriptorImpl.java @@ -0,0 +1,24 @@ +package org.jetbrains.jet.lang.types; + +import java.util.List; + +/** + * @author abreslav + */ +public class PropertyDescriptorImpl extends MemberDescriptorImpl implements PropertyDescriptor { + private Type type; + + public PropertyDescriptorImpl(List attributes, String name, Type type) { + super(attributes, name); + this.type = type; + } + + public PropertyDescriptorImpl(List attributes, String name) { + this(attributes, name, null); + } + + @Override + public Type getType() { + return type; + } +} diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeMemberDomain.java b/idea/src/org/jetbrains/jet/lang/types/TypeMemberDomain.java index 25a95bd0f05..27150105664 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeMemberDomain.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeMemberDomain.java @@ -12,36 +12,36 @@ import java.util.Collections; public interface TypeMemberDomain { TypeMemberDomain EMPTY = new TypeMemberDomain() { @Override - public ClassDescriptor getClassDescriptor(@NotNull Type type, String name) { + public ClassDescriptor getClassDescriptor(@NotNull Type contextType, String name) { return null; } @NotNull @Override - public Collection getMethods(Type receiverType, String name) { + public Collection getMethods(Type contextType, String name) { return Collections.emptyList(); } @Override - public PropertyDescriptor getProperty(Type receiverType, String name) { + public PropertyDescriptor getProperty(Type contextType, String name) { return null; } @Override - public ExtensionDescriptor getExtension(Type receiverType, String name) { + public ExtensionDescriptor getExtension(Type contextType, String name) { return null; } }; @Nullable - ClassDescriptor getClassDescriptor(@NotNull Type type, String name); + ClassDescriptor getClassDescriptor(@NotNull Type contextType, String name); @NotNull - Collection getMethods(Type receiverType, String name); + Collection getMethods(Type contextType, String name); @Nullable - PropertyDescriptor getProperty(Type receiverType, String name); + PropertyDescriptor getProperty(Type contextType, String name); @Nullable - ExtensionDescriptor getExtension(Type receiverType, String name); + ExtensionDescriptor getExtension(Type contextType, String name); } diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index 4c20091a261..b27fddbde0a 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -310,6 +310,9 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { public void testPropertiesInClasses() throws Exception { assertType("new Properties().p", "Int"); + assertType("new Props().p", "Int"); + assertType("new Props().p", "Int"); + assertErrorType("new Props().p"); } // public void testImplicitConversions() throws Exception { @@ -364,6 +367,13 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { assertTrue(type + " != " + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType)); } + private void assertErrorType(String expression) { + Project project = getProject(); + JetExpression jetExpression = JetChangeUtil.createExpression(project, expression); + Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression, false); + assertTrue("Error type expected but " + type + " returned", ErrorType.isErrorType(type)); + } + private static void assertType(String contextType, String expression, String expectedType) { final Type thisType = makeType(contextType); JetScope scope = new JetScopeAdapter(ClassDefinitions.BASIC_SCOPE) { @@ -407,7 +417,8 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { "open class Derived_inT : Base_inT", "open class Base_outT", "open class Derived_outT : Base_outT", - "class Properties { val p : Int }" + "class Properties { val p : Int }", + "class Props { val p : T }", }; public static JetScope BASIC_SCOPE = new JetScopeAdapter(JetStandardClasses.STANDARD_CLASSES) {