diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DelegationResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DelegationResolver.java index c49c16a15dd..c3546641873 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DelegationResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DelegationResolver.java @@ -20,6 +20,7 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; +import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; @@ -27,15 +28,14 @@ import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor; import org.jetbrains.jet.lang.psi.JetClassOrObject; import org.jetbrains.jet.lang.psi.JetDelegationSpecifier; import org.jetbrains.jet.lang.psi.JetDelegatorByExpressionSpecifier; +import org.jetbrains.jet.lang.psi.JetTypeReference; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeUtils; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; +import java.util.*; import static org.jetbrains.jet.lang.diagnostics.Errors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED; -import static org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.*; +import static org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE; public class DelegationResolver { @@ -43,48 +43,75 @@ public class DelegationResolver { } @NotNull - public static Collection generateDelegatedMembers( + public static Collection generateDelegatedMembers( + @NotNull JetClassOrObject classOrObject, + @NotNull ClassDescriptor classDescriptor, + @NotNull Collection existingMembers, @NotNull BindingTrace trace, - @NotNull JetClassOrObject jetClass, - @NotNull MutableClassDescriptor classDescriptor + @NotNull Callback callback ) { - Collection delegatedMembers = new HashSet(); - for (JetDelegationSpecifier delegationSpecifier : jetClass.getDelegationSpecifiers()) { + Collection delegatedMembers = new HashSet(); + for (JetDelegationSpecifier delegationSpecifier : classOrObject.getDelegationSpecifiers()) { if (!(delegationSpecifier instanceof JetDelegatorByExpressionSpecifier)) { continue; } JetDelegatorByExpressionSpecifier specifier = (JetDelegatorByExpressionSpecifier) delegationSpecifier; - JetType type = trace.get(BindingContext.TYPE, specifier.getTypeReference()); - if (type == null) { + JetTypeReference typeReference = specifier.getTypeReference(); + if (typeReference == null) { continue; } - Collection candidates = - generateDelegationCandidates(classDescriptor, type, extractCallableMembers(type)); - for (CallableMemberDescriptor candidate : candidates) { - if (existingMemberOverridesDelegatedMember(classDescriptor, candidate)) { - continue; - } - //only leave the first delegated member - if (checkClashWithOtherDelegatedMember(trace, jetClass, delegatedMembers, candidate)) { - continue; - } - - delegatedMembers.add(candidate); + JetType delegatedTraitType = callback.getTypeByTypeReference(typeReference); + if (delegatedTraitType == null) { + continue; } + Collection delegatesForTrait = generateDelegatesForOneTrait(classOrObject, classDescriptor, existingMembers, + trace, callback, delegatedMembers, delegatedTraitType); + delegatedMembers.addAll(delegatesForTrait); } return delegatedMembers; } - private static boolean checkClashWithOtherDelegatedMember( + @NotNull + private static Collection generateDelegatesForOneTrait( + @NotNull JetClassOrObject classOrObject, + @NotNull ClassDescriptor classDescriptor, + @NotNull Collection existingMembers, @NotNull BindingTrace trace, - @NotNull JetClassOrObject jetClass, - @NotNull Collection delegatedMembers, - @NotNull CallableMemberDescriptor candidate + @NotNull Callback callback, + @NotNull Collection existingDelegates, + @NotNull JetType delegatedTraitType + ) { + Collection result = new HashSet(); + Collection candidates = generateDelegationCandidates(classDescriptor, delegatedTraitType, callback); + for (T candidate : candidates) { + if (existingMemberOverridesDelegatedMember(candidate, existingMembers)) { + continue; + } + //only leave the first delegated member + if (checkClashWithOtherDelegatedMember(trace, classOrObject, existingDelegates, candidate)) { + continue; + } + + result.add(candidate); + } + return result; + } + + private static boolean checkClashWithOtherDelegatedMember( + @NotNull BindingTrace trace, + @Nullable JetClassOrObject jetClass, + @NotNull Collection delegatedMembers, + @NotNull T candidate ) { for (CallableMemberDescriptor alreadyDelegatedMember : delegatedMembers) { if (haveSameSignatures(alreadyDelegatedMember, candidate)) { //trying to delegate to many traits with the same methods - trace.report(MANY_IMPL_MEMBER_NOT_IMPLEMENTED.on(jetClass.getNameIdentifier(), jetClass, alreadyDelegatedMember)); + if (jetClass != null) { + PsiElement nameIdentifier = jetClass.getNameIdentifier(); + if (nameIdentifier != null) { + trace.report(MANY_IMPL_MEMBER_NOT_IMPLEMENTED.on(nameIdentifier, jetClass, alreadyDelegatedMember)); + } + } return true; } } @@ -92,10 +119,10 @@ public class DelegationResolver { } private static boolean existingMemberOverridesDelegatedMember( - MutableClassDescriptor classDescriptor, - CallableMemberDescriptor candidate + @NotNull CallableMemberDescriptor candidate, + @NotNull Collection existingMembers ) { - for (CallableMemberDescriptor existingDescriptor : classDescriptor.getAllCallableMembers()) { + for (CallableDescriptor existingDescriptor : existingMembers) { if (haveSameSignatures(existingDescriptor, candidate)) { return true; } @@ -104,12 +131,13 @@ public class DelegationResolver { } @NotNull - public static Collection generateDelegationCandidates( + private static Collection generateDelegationCandidates( @NotNull ClassDescriptor classDescriptor, @NotNull JetType delegatedTraitType, - @NotNull Collection membersFromTrait + @NotNull Callback callback ) { - Collection descriptorsToDelegate = filterMembersFromSuperClassOfDelegatedTrait(delegatedTraitType, membersFromTrait); + Collection membersFromTrait = callback.getMembersByType(delegatedTraitType); + Collection descriptorsToDelegate = filterMembersFromSuperClassOfDelegatedTrait(delegatedTraitType, membersFromTrait, callback); Collection result = Lists.newArrayList(); for (T memberDescriptor : descriptorsToDelegate) { if (memberDescriptor.getModality().isOverridable()) { @@ -126,9 +154,10 @@ public class DelegationResolver { @NotNull private static Collection filterMembersFromSuperClassOfDelegatedTrait( @NotNull JetType delegatedTraitType, - @NotNull Collection membersFromTrait + @NotNull Collection membersFromTrait, + @NotNull Callback callback ) { - final Collection membersToSkip = getMembersFromClassSupertypeOfTrait(delegatedTraitType); + final Collection membersToSkip = getMembersFromClassSupertypeOfTrait(delegatedTraitType, callback); return Collections2.filter( membersFromTrait, new Predicate() { @@ -149,7 +178,10 @@ public class DelegationResolver { } @NotNull - private static Collection getMembersFromClassSupertypeOfTrait(@NotNull JetType delegateTraitType) { + private static Collection getMembersFromClassSupertypeOfTrait( + @NotNull JetType delegateTraitType, + @NotNull Callback callback + ) { JetType classSupertype = null; for (JetType supertype : TypeUtils.getAllSupertypes(delegateTraitType)) { if (isNotTrait(supertype.getConstructor().getDeclarationDescriptor())) { @@ -157,14 +189,7 @@ public class DelegationResolver { break; } } - - return classSupertype != null ? extractCallableMembers(classSupertype) : Collections.emptyList(); - } - - @SuppressWarnings("unchecked") - private static Collection extractCallableMembers(JetType type) { - return (Collection) Collections2.filter(type.getMemberScope().getAllDescriptors(), - Predicates.instanceOf(CallableMemberDescriptor.class)); + return classSupertype != null ? callback.getMembersByType(classSupertype) : Collections.emptyList(); } private static boolean isNotTrait(DeclarationDescriptor descriptor) { @@ -177,10 +202,28 @@ public class DelegationResolver { public static void generateDelegatesInAClass( @NotNull MutableClassDescriptor classDescriptor, - @NotNull BindingTrace trace, + @NotNull final BindingTrace trace, @NotNull JetClassOrObject jetClassOrObject ) { - for (CallableMemberDescriptor descriptor : generateDelegatedMembers(trace, jetClassOrObject, classDescriptor)) { + Callback eagerResolveCallback = new Callback() { + @Nullable + @Override + public JetType getTypeByTypeReference(@NotNull JetTypeReference reference) { + return trace.get(BindingContext.TYPE, reference); + } + + @NotNull + @Override + public Collection getMembersByType(@NotNull JetType type) { + //noinspection unchecked + return (Collection) Collections2.filter(type.getMemberScope().getAllDescriptors(), + Predicates.instanceOf(CallableMemberDescriptor.class)); + } + }; + Set existingMembers = classDescriptor.getAllCallableMembers(); + Collection delegatedMembers = + generateDelegatedMembers(jetClassOrObject, classDescriptor, existingMembers, trace, eagerResolveCallback); + for (CallableMemberDescriptor descriptor : delegatedMembers) { if (descriptor instanceof PropertyDescriptor) { PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor; classDescriptor.getBuilder().addPropertyDescriptor(propertyDescriptor); @@ -191,4 +234,12 @@ public class DelegationResolver { } } } -} + + public interface Callback { + @Nullable + JetType getTypeByTypeReference(@NotNull JetTypeReference reference); + + @NotNull + Collection getMembersByType(@NotNull JetType type); + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/descriptors/LazyClassMemberScope.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/descriptors/LazyClassMemberScope.java index 0e08d4d9af8..f91bc8ee060 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/descriptors/LazyClassMemberScope.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/descriptors/LazyClassMemberScope.java @@ -42,7 +42,9 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import static org.jetbrains.jet.lang.resolve.DelegationResolver.generateDelegationCandidates; +import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.DELEGATION; +import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.FAKE_OVERRIDE; +import static org.jetbrains.jet.lang.resolve.DelegationResolver.generateDelegatedMembers; public class LazyClassMemberScope extends AbstractLazyMemberScope { @@ -140,7 +142,7 @@ public class LazyClassMemberScope extends AbstractLazyMemberScope functions = super.getFunctions(name); for (FunctionDescriptor functionDescriptor : functions) { - if (functionDescriptor.getKind() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + if (functionDescriptor.getKind() != FAKE_OVERRIDE && functionDescriptor.getKind() != DELEGATION) { OverrideResolver.resolveUnknownVisibilityForMember(functionDescriptor, resolveSession.getTrace()); } } @@ -153,7 +155,7 @@ public class LazyClassMemberScope extends AbstractLazyMemberScope properties = super.getProperties(name); for (VariableDescriptor variableDescriptor : properties) { PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor; - if (propertyDescriptor.getKind() != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - OverrideResolver.resolveUnknownVisibilityForMember(propertyDescriptor, resolveSession.getTrace()); - } + if (propertyDescriptor.getKind() == FAKE_OVERRIDE || propertyDescriptor.getKind() == DELEGATION) continue; + OverrideResolver.resolveUnknownVisibilityForMember(propertyDescriptor, resolveSession.getTrace()); } return properties; } @@ -251,29 +252,40 @@ public class LazyClassMemberScope extends AbstractLazyMemberScope void generateDelegatingDescriptors( - @NotNull Name name, - @NotNull MemberExtractor extractor, - @NotNull Set result + @NotNull + private Collection generateDelegatingDescriptors( + @NotNull final Name name, + @NotNull final MemberExtractor extractor, + @NotNull Collection existingDescriptors ) { - for (JetDelegationSpecifier delegationSpecifier : declarationProvider.getOwnerInfo().getDelegationSpecifiers()) { - if (delegationSpecifier instanceof JetDelegatorByExpressionSpecifier) { - JetDelegatorByExpressionSpecifier specifier = (JetDelegatorByExpressionSpecifier) delegationSpecifier; - JetTypeReference typeReference = specifier.getTypeReference(); - if (typeReference != null) { - JetType supertype = resolveSession.getInjector().getTypeResolver().resolveType( - thisDescriptor.getScopeForClassHeaderResolution(), - typeReference, - resolveSession.getTrace(), - false); - result.addAll(generateDelegationCandidates(thisDescriptor, supertype, extractor.extract(supertype, name))); - } - } + //class objects do not have delegated members + if (thisDescriptor.getKind() == ClassKind.CLASS_OBJECT) { + return Collections.emptySet(); } + DelegationResolver.Callback lazyResolveCallback = new DelegationResolver.Callback() { + @Nullable + @Override + public JetType getTypeByTypeReference(@NotNull JetTypeReference reference) { + return resolveSession.getInjector().getTypeResolver().resolveType( + thisDescriptor.getScopeForClassHeaderResolution(), + reference, + resolveSession.getTrace(), + false); + } + + @NotNull + @Override + public Collection getMembersByType(@NotNull JetType type) { + return extractor.extract(type, name); + } + }; + JetClassOrObject classOrObject = declarationProvider.getOwnerInfo().getCorrespondingClassOrObject(); + assert classOrObject != null : "Should not be null for non class object class."; + return generateDelegatedMembers(classOrObject, thisDescriptor, existingDescriptors, resolveSession.getTrace(), lazyResolveCallback); } @Override diff --git a/compiler/testData/loadKotlin/fun/DeclaredMemberOverridesDelegated.kt b/compiler/testData/loadKotlin/fun/DeclaredMemberOverridesDelegated.kt new file mode 100644 index 00000000000..e1905be2a94 --- /dev/null +++ b/compiler/testData/loadKotlin/fun/DeclaredMemberOverridesDelegated.kt @@ -0,0 +1,13 @@ +package test + +trait X { + fun foo() +} + +trait Y : X { +} + +class B(val a: X) : X by a, Y { + override fun foo() { + } +} diff --git a/compiler/testData/loadKotlin/fun/DeclaredMemberOverridesDelegated.txt b/compiler/testData/loadKotlin/fun/DeclaredMemberOverridesDelegated.txt new file mode 100644 index 00000000000..be27869d466 --- /dev/null +++ b/compiler/testData/loadKotlin/fun/DeclaredMemberOverridesDelegated.txt @@ -0,0 +1,16 @@ +package test + +internal final class B : test.X, test.Y { + /*primary*/ public constructor B(/*0*/ a: test.X) + internal final val a: test.X + internal final fun (): test.X + internal open override /*2*/ fun foo(): jet.Unit +} + +internal trait X { + internal abstract fun foo(): jet.Unit +} + +internal trait Y : test.X { + internal abstract override /*1*/ /*fake_override*/ fun foo(): jet.Unit +} diff --git a/compiler/tests/org/jetbrains/jet/descriptors/serialization/DescriptorSerializationTestGenerated.java b/compiler/tests/org/jetbrains/jet/descriptors/serialization/DescriptorSerializationTestGenerated.java index 3f9bd383b33..df7a8b150ff 100644 --- a/compiler/tests/org/jetbrains/jet/descriptors/serialization/DescriptorSerializationTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/descriptors/serialization/DescriptorSerializationTestGenerated.java @@ -411,6 +411,11 @@ public class DescriptorSerializationTestGenerated extends AbstractDescriptorSeri JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/fun"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("DeclaredMemberOverridesDelegated.kt") + public void testDeclaredMemberOverridesDelegated() throws Exception { + doTest("compiler/testData/loadKotlin/fun/DeclaredMemberOverridesDelegated.kt"); + } + @TestMetadata("InheritMethodsDifferentReturnTypesAndVisibilities.kt") public void testInheritMethodsDifferentReturnTypesAndVisibilities() throws Exception { doTest("compiler/testData/loadKotlin/fun/InheritMethodsDifferentReturnTypesAndVisibilities.kt"); diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadCompiledKotlinTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadCompiledKotlinTestGenerated.java index 444cc2b342b..ac3d02faf36 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadCompiledKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadCompiledKotlinTestGenerated.java @@ -630,6 +630,11 @@ public class LoadCompiledKotlinTestGenerated extends AbstractLoadCompiledKotlinT JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/fun"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("DeclaredMemberOverridesDelegated.kt") + public void testDeclaredMemberOverridesDelegated() throws Exception { + doTestWithAccessors("compiler/testData/loadKotlin/fun/DeclaredMemberOverridesDelegated.kt"); + } + @TestMetadata("InheritMethodsDifferentReturnTypesAndVisibilities.kt") public void testInheritMethodsDifferentReturnTypesAndVisibilities() throws Exception { doTestWithAccessors("compiler/testData/loadKotlin/fun/InheritMethodsDifferentReturnTypesAndVisibilities.kt"); diff --git a/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/LazyResolveNamespaceComparingTestGenerated.java b/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/LazyResolveNamespaceComparingTestGenerated.java index a7b325dc2a6..83acbb40c6b 100644 --- a/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/LazyResolveNamespaceComparingTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/LazyResolveNamespaceComparingTestGenerated.java @@ -632,6 +632,11 @@ public class LazyResolveNamespaceComparingTestGenerated extends AbstractLazyReso JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadKotlin/fun"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("DeclaredMemberOverridesDelegated.kt") + public void testDeclaredMemberOverridesDelegated() throws Exception { + doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/fun/DeclaredMemberOverridesDelegated.kt"); + } + @TestMetadata("InheritMethodsDifferentReturnTypesAndVisibilities.kt") public void testInheritMethodsDifferentReturnTypesAndVisibilities() throws Exception { doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/fun/InheritMethodsDifferentReturnTypesAndVisibilities.kt");