From 97de457f796b1365ebba6ab4c414118c9f8c2f5f Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 1 Jun 2011 18:17:28 +0400 Subject: [PATCH] JET-72 Type inference doesn't work when iterating over ArrayList: substitutions fixed for methods inherited from generic superclasses --- .../descriptors/TypeParameterDescriptor.java | 2 +- .../resolve/java/JavaClassDescriptor.java | 5 +++ .../resolve/java/JavaClassMembersScope.java | 6 ++- .../resolve/java/JavaDescriptorResolver.java | 37 +++++++++++++------ .../resolve/java/JavaTypeTransformer.java | 2 +- .../jet/lang/types/TypeSubstitutor.java | 11 +++++- .../jetbrains/jet/lang/types/TypeUtils.java | 34 ++++++++++++++++- idea/testData/checker/regression/Jet72.jet | 17 +++++++++ 8 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 idea/testData/checker/regression/Jet72.jet diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java b/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java index 73ce42da79c..62ca17522a9 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java @@ -81,7 +81,7 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement @Override public String toString() { - return typeConstructor.toString(); + return getContainingDeclaration() + "::" + typeConstructor.toString(); } @NotNull diff --git a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaClassDescriptor.java b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaClassDescriptor.java index 1c1be399d68..f98b8a3eda6 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaClassDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaClassDescriptor.java @@ -91,4 +91,9 @@ public class JavaClassDescriptor extends MutableDeclarationDescriptor implements public R accept(DeclarationDescriptorVisitor visitor, D data) { return visitor.visitClassDescriptor(this, data); } + + @Override + public String toString() { + return "java class " + typeConstructor; + } } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaClassMembersScope.java b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaClassMembersScope.java index d8cbe10362f..a4aa9ed3534 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaClassMembersScope.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaClassMembersScope.java @@ -110,7 +110,11 @@ public class JavaClassMembersScope implements JetScope { public FunctionGroup getFunctionGroup(@NotNull String name) { FunctionGroup functionGroup = functionGroups.get(name); if (functionGroup == null) { - functionGroup = semanticServices.getDescriptorResolver().resolveFunctionGroup(psiClass, name, staticMembers); + functionGroup = semanticServices.getDescriptorResolver().resolveFunctionGroup( + psiClass, + staticMembers ? null : (ClassDescriptor) containingDeclaration, + name, + staticMembers); functionGroups.put(name, functionGroup); } return functionGroup; diff --git a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java index 43dcbea5c54..e3b6f444be7 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java @@ -26,7 +26,7 @@ public class JavaDescriptorResolver { @Override public R accept(DeclarationDescriptorVisitor visitor, D data) { - throw new UnsupportedOperationException(); // TODO + return visitor.visitDeclarationDescriptor(this, data); } }; @@ -73,7 +73,6 @@ public class JavaDescriptorResolver { classDescriptorCache.put(psiClass.getQualifiedName(), null); // TODO String name = psiClass.getName(); - PsiModifierList modifierList = psiClass.getModifierList(); JavaClassDescriptor classDescriptor = new JavaClassDescriptor( JAVA_ROOT ); @@ -86,7 +85,7 @@ public class JavaDescriptorResolver { // TODO psiClass.hasModifierProperty(PsiModifier.FINAL), name, - resolveTypeParameters(psiClass.getTypeParameters()), + resolveTypeParameters(classDescriptor, psiClass.getTypeParameters()), supertypes )); @@ -124,10 +123,10 @@ public class JavaDescriptorResolver { return classDescriptor; } - private List resolveTypeParameters(@NotNull PsiTypeParameter[] typeParameters) { + private List resolveTypeParameters(@NotNull DeclarationDescriptor containingDeclaration, @NotNull PsiTypeParameter[] typeParameters) { List result = Lists.newArrayList(); for (PsiTypeParameter typeParameter : typeParameters) { - TypeParameterDescriptor typeParameterDescriptor = resolveTypeParameter(typeParameter); + TypeParameterDescriptor typeParameterDescriptor = resolveTypeParameter(containingDeclaration, typeParameter); result.add(typeParameterDescriptor); } return result; @@ -158,11 +157,11 @@ public class JavaDescriptorResolver { } @NotNull - public TypeParameterDescriptor resolveTypeParameter(@NotNull PsiTypeParameter psiTypeParameter) { + public TypeParameterDescriptor resolveTypeParameter(@NotNull DeclarationDescriptor containingDeclaration, @NotNull PsiTypeParameter psiTypeParameter) { TypeParameterDescriptor typeParameterDescriptor = typeParameterDescriptorCache.get(psiTypeParameter); if (typeParameterDescriptor == null) { - typeParameterDescriptor = createJavaTypeParameterDescriptor(JAVA_ROOT, psiTypeParameter); -// Tis is done inside the method: typeParameterDescriptorCache.put(psiTypeParameter, typeParameterDescriptor); + typeParameterDescriptor = createJavaTypeParameterDescriptor(containingDeclaration, psiTypeParameter); +// This is done inside the method: typeParameterDescriptorCache.put(psiTypeParameter, typeParameterDescriptor); } return typeParameterDescriptor; } @@ -245,9 +244,16 @@ public class JavaDescriptorResolver { } @NotNull - public FunctionGroup resolveFunctionGroup(@NotNull PsiClass psiClass, @NotNull String methodName, boolean staticMembers) { + public FunctionGroup resolveFunctionGroup(@NotNull PsiClass psiClass, @Nullable ClassDescriptor classDescriptor, @NotNull String methodName, boolean staticMembers) { WritableFunctionGroup writableFunctionGroup = new WritableFunctionGroup(methodName); final Collection signatures = psiClass.getVisibleSignatures(); + TypeSubstitutor typeSubstitutor; + if (classDescriptor != null) { + typeSubstitutor = TypeUtils.buildDeepSubstitutor(classDescriptor.getDefaultType()); + } + else { + typeSubstitutor = TypeSubstitutor.EMPTY; + } for (HierarchicalMethodSignature signature: signatures) { PsiMethod method = signature.getMethod(); if (method.hasModifierProperty(PsiModifier.STATIC) != staticMembers) { @@ -258,6 +264,9 @@ public class JavaDescriptorResolver { } FunctionDescriptor functionDescriptor = methodDescriptorCache.get(method); if (functionDescriptor != null) { + if (method.getContainingClass() != psiClass) { + functionDescriptor = functionDescriptor.substitute(typeSubstitutor); + } writableFunctionGroup.addFunction(functionDescriptor); continue; } @@ -270,12 +279,18 @@ public class JavaDescriptorResolver { ); functionDescriptorImpl.initialize( null, - resolveTypeParameters(method.getTypeParameters()), + resolveTypeParameters(functionDescriptorImpl, method.getTypeParameters()), semanticServices.getDescriptorResolver().resolveParameterDescriptors(functionDescriptorImpl, parameters), semanticServices.getTypeTransformer().transformToType(method.getReturnType()) ); semanticServices.getTrace().recordDeclarationResolution(method, functionDescriptorImpl); - writableFunctionGroup.addFunction(functionDescriptorImpl); + FunctionDescriptor substitutedFunctionDescriptor = functionDescriptorImpl; + if (method.getContainingClass() != psiClass) { + substitutedFunctionDescriptor = functionDescriptorImpl.substitute(typeSubstitutor); + } + if (substitutedFunctionDescriptor != null) { + writableFunctionGroup.addFunction(substitutedFunctionDescriptor); + } methodDescriptorCache.put(method, functionDescriptorImpl); } return writableFunctionGroup; 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 e25ee8eb295..b3fd75ce2df 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/java/JavaTypeTransformer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/java/JavaTypeTransformer.java @@ -70,7 +70,7 @@ public class JavaTypeTransformer { if (psiClass instanceof PsiTypeParameter) { PsiTypeParameter typeParameter = (PsiTypeParameter) psiClass; - TypeParameterDescriptor typeParameterDescriptor = resolver.resolveTypeParameter(typeParameter); + TypeParameterDescriptor typeParameterDescriptor = resolver.resolveTypeParameter(JavaDescriptorResolver.JAVA_ROOT, typeParameter); return typeParameterDescriptor.getDefaultType(); } else { diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java b/idea/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java index 2ee2fbb843d..8894d010bfb 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java @@ -5,13 +5,18 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.resolve.SubstitutingScope; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; /** * @author abreslav */ public class TypeSubstitutor { + public static final TypeSubstitutor EMPTY = create(Collections.emptyMap()); + public static final class SubstitutionException extends Exception { public SubstitutionException(String message) { super(message); @@ -176,6 +181,10 @@ public class TypeSubstitutor { return new TypeProjection(effectiveProjectionKindValue, specializeType(effectiveTypeValue, effectiveContextVariance)); } + /*package*/ void addSubstitution(@NotNull TypeConstructor typeConstructor, @NotNull TypeProjection typeProjection) { + substitutionContext.put(typeConstructor, typeProjection); + } + private static Variance asymmetricOr(Variance a, Variance b) { return a == Variance.INVARIANT ? b : a; } diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java b/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java index 7b384eddd3d..6a4399d2ec3 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -1,6 +1,7 @@ package org.jetbrains.jet.lang.types; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -185,15 +186,46 @@ public class TypeUtils { return buildSubstitutionContext(context.getConstructor().getParameters(), context.getArguments()); } + /** + * Builds a context with all the supertypes' parameters substituted + */ + @NotNull + public static TypeSubstitutor buildDeepSubstitutor(@NotNull JetType type) { + TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(Maps.newHashMap()); + fillInDeepSubstitutor(type, typeSubstitutor); + return typeSubstitutor; + } + + private static void fillInDeepSubstitutor(JetType context, TypeSubstitutor substitutor) { + List parameters = context.getConstructor().getParameters(); + List arguments = context.getArguments(); + for (int i = 0; i < arguments.size(); i++) { + TypeProjection argument = arguments.get(i); + TypeParameterDescriptor typeParameterDescriptor = parameters.get(i); + + JetType substitute = substitutor.substitute(argument.getType(), Variance.INVARIANT); + assert substitute != null; + TypeProjection substitutedTypeProjection = new TypeProjection(argument.getProjectionKind(), substitute); + substitutor.addSubstitution(typeParameterDescriptor.getTypeConstructor(), substitutedTypeProjection); + } + for (JetType supertype : context.getConstructor().getSupertypes()) { + fillInDeepSubstitutor(supertype, substitutor); + } + } + @NotNull public static Map buildSubstitutionContext(@NotNull List parameters, @NotNull List contextArguments) { Map parameterValues = new HashMap(); + fillInSubstitutionContext(parameters, contextArguments, parameterValues); + return parameterValues; + } + + private static void fillInSubstitutionContext(List parameters, List contextArguments, Map parameterValues) { for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) { TypeParameterDescriptor parameter = parameters.get(i); TypeProjection value = contextArguments.get(i); parameterValues.put(parameter.getTypeConstructor(), value); } - return parameterValues; } @NotNull diff --git a/idea/testData/checker/regression/Jet72.jet b/idea/testData/checker/regression/Jet72.jet new file mode 100644 index 00000000000..0051d05d8d5 --- /dev/null +++ b/idea/testData/checker/regression/Jet72.jet @@ -0,0 +1,17 @@ +// JET-72 Type inference doesn't work when iterating over ArrayList + +import java.util.ArrayList + +class Item(val room: Object) { + val name : String +} + +val items: ArrayList = new ArrayList + +fun test(room : Object) { + for(val item: Item in items) { + if (item.room === room) { + System.out?.println("You see " + item.name) + } + } +}