Refactor DelegationResolver

Fix an issue in lazy resolve : filter out methods from delegated traits superclass
This commit is contained in:
Pavel V. Talanov
2013-09-17 13:24:06 +04:00
parent c3a5a9b7b5
commit 6fb7a79a1f
8 changed files with 192 additions and 62 deletions
@@ -32,67 +32,126 @@ import org.jetbrains.jet.lang.types.TypeUtils;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import static org.jetbrains.jet.lang.diagnostics.Errors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED; import static org.jetbrains.jet.lang.diagnostics.Errors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED;
import static org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.*;
public class DelegationResolver { public class DelegationResolver {
private DelegationResolver() {} private DelegationResolver() {
}
public static void addDelegatedMembers(@NotNull BindingTrace trace, @NotNull JetClassOrObject jetClass, @NotNull MutableClassDescriptor classDescriptor) { @NotNull
public static Collection<CallableMemberDescriptor> generateDelegatedMembers(
@NotNull BindingTrace trace,
@NotNull JetClassOrObject jetClass,
@NotNull MutableClassDescriptor classDescriptor
) {
Collection<CallableMemberDescriptor> delegatedMembers = new HashSet<CallableMemberDescriptor>();
for (JetDelegationSpecifier delegationSpecifier : jetClass.getDelegationSpecifiers()) { for (JetDelegationSpecifier delegationSpecifier : jetClass.getDelegationSpecifiers()) {
if (delegationSpecifier instanceof JetDelegatorByExpressionSpecifier) { if (!(delegationSpecifier instanceof JetDelegatorByExpressionSpecifier)) {
JetDelegatorByExpressionSpecifier specifier = (JetDelegatorByExpressionSpecifier) delegationSpecifier; continue;
JetType type = trace.get(BindingContext.TYPE, specifier.getTypeReference()); }
if (type != null) { JetDelegatorByExpressionSpecifier specifier = (JetDelegatorByExpressionSpecifier) delegationSpecifier;
final Collection<CallableMemberDescriptor> membersToSkip = getMembersFromClassSupertype(type); JetType type = trace.get(BindingContext.TYPE, specifier.getTypeReference());
Collection<CallableMemberDescriptor> descriptorsToDelegate = Collections2.filter(extractCallableMembers(type), if (type == null) {
new Predicate<CallableMemberDescriptor>() { continue;
@Override }
public boolean apply(@Nullable CallableMemberDescriptor descriptor) { Collection<CallableMemberDescriptor> candidates =
for (CallableMemberDescriptor memberToSkip : membersToSkip) { generateDelegationCandidates(classDescriptor, type, extractCallableMembers(type));
if (haveSameSignatures(memberToSkip, descriptor)) { for (CallableMemberDescriptor candidate : candidates) {
return false; if (existingMemberOverridesDelegatedMember(classDescriptor, candidate)) {
} continue;
}
return true;
}
});
Collection<CallableMemberDescriptor> generatedDescriptors = generateDelegatedMembers(classDescriptor, descriptorsToDelegate);
outer:
for (CallableMemberDescriptor descriptor : generatedDescriptors) {
for (CallableMemberDescriptor existingDescriptor : classDescriptor.getAllCallableMembers()) {
if (OverridingUtil.isOverridableBy(existingDescriptor, descriptor).getResult() == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE) {
if (existingDescriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION) {
//trying to delegate to many traits with the same methods
trace.report(MANY_IMPL_MEMBER_NOT_IMPLEMENTED.on(jetClass.getNameIdentifier(), jetClass, existingDescriptor));
}
continue outer;
}
}
if (descriptor instanceof PropertyDescriptor) {
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
classDescriptor.getBuilder().addPropertyDescriptor(propertyDescriptor);
}
else if (descriptor instanceof SimpleFunctionDescriptor) {
SimpleFunctionDescriptor functionDescriptor = (SimpleFunctionDescriptor) descriptor;
classDescriptor.getBuilder().addFunctionDescriptor(functionDescriptor);
}
}
} }
//only leave the first delegated member
if (checkClashWithOtherDelegatedMember(trace, jetClass, delegatedMembers, candidate)) {
continue;
}
delegatedMembers.add(candidate);
} }
} }
return delegatedMembers;
} }
private static boolean haveSameSignatures(CallableDescriptor memberOne, CallableDescriptor memberTwo) { private static boolean checkClashWithOtherDelegatedMember(
return OverridingUtil.isOverridableBy(memberOne, memberTwo).getResult() == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE; @NotNull BindingTrace trace,
@NotNull JetClassOrObject jetClass,
@NotNull Collection<CallableMemberDescriptor> delegatedMembers,
@NotNull CallableMemberDescriptor 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));
return true;
}
}
return false;
} }
private static Collection<CallableMemberDescriptor> getMembersFromClassSupertype(JetType type) { private static boolean existingMemberOverridesDelegatedMember(
MutableClassDescriptor classDescriptor,
CallableMemberDescriptor candidate
) {
for (CallableMemberDescriptor existingDescriptor : classDescriptor.getAllCallableMembers()) {
if (haveSameSignatures(existingDescriptor, candidate)) {
return true;
}
}
return false;
}
@NotNull
public static <T extends CallableMemberDescriptor> Collection<T> generateDelegationCandidates(
@NotNull ClassDescriptor classDescriptor,
@NotNull JetType delegatedTraitType,
@NotNull Collection<T> membersFromTrait
) {
Collection<T> descriptorsToDelegate = filterMembersFromSuperClassOfDelegatedTrait(delegatedTraitType, membersFromTrait);
Collection<T> result = Lists.newArrayList();
for (T memberDescriptor : descriptorsToDelegate) {
if (memberDescriptor.getModality().isOverridable()) {
Modality modality = DescriptorUtils.convertModality(memberDescriptor.getModality(), true);
@SuppressWarnings("unchecked")
T copy = (T) memberDescriptor.copy(classDescriptor, modality, memberDescriptor.getVisibility(),
CallableMemberDescriptor.Kind.DELEGATION, false);
result.add(copy);
}
}
return result;
}
@NotNull
private static <T extends CallableMemberDescriptor> Collection<T> filterMembersFromSuperClassOfDelegatedTrait(
@NotNull JetType delegatedTraitType,
@NotNull Collection<T> membersFromTrait
) {
final Collection<CallableMemberDescriptor> membersToSkip = getMembersFromClassSupertypeOfTrait(delegatedTraitType);
return Collections2.filter(
membersFromTrait,
new Predicate<CallableMemberDescriptor>() {
@Override
public boolean apply(@Nullable CallableMemberDescriptor descriptor) {
for (CallableMemberDescriptor memberToSkip : membersToSkip) {
if (haveSameSignatures(memberToSkip, descriptor)) {
return false;
}
}
return true;
}
});
}
private static boolean haveSameSignatures(@NotNull CallableDescriptor memberOne, @NotNull CallableDescriptor memberTwo) {
return OverridingUtil.isOverridableBy(memberOne, memberTwo).getResult() == OVERRIDABLE;
}
@NotNull
private static Collection<CallableMemberDescriptor> getMembersFromClassSupertypeOfTrait(@NotNull JetType delegateTraitType) {
JetType classSupertype = null; JetType classSupertype = null;
for (JetType supertype : TypeUtils.getAllSupertypes(type)) { for (JetType supertype : TypeUtils.getAllSupertypes(delegateTraitType)) {
if (isNotTrait(supertype.getConstructor().getDeclarationDescriptor())) { if (isNotTrait(supertype.getConstructor().getDeclarationDescriptor())) {
classSupertype = supertype; classSupertype = supertype;
break; break;
@@ -116,17 +175,20 @@ public class DelegationResolver {
return false; return false;
} }
public static <T extends CallableMemberDescriptor> Collection<T> generateDelegatedMembers(DeclarationDescriptor newOwner, Collection<T> delegatedDescriptors) { public static void generateDelegatesInAClass(
Collection<CallableMemberDescriptor> result = Lists.newArrayList(); @NotNull MutableClassDescriptor classDescriptor,
for (CallableMemberDescriptor memberDescriptor : delegatedDescriptors) { @NotNull BindingTrace trace,
if (memberDescriptor.getModality().isOverridable()) { @NotNull JetClassOrObject jetClassOrObject
Modality modality = DescriptorUtils.convertModality(memberDescriptor.getModality(), true); ) {
CallableMemberDescriptor copy = for (CallableMemberDescriptor descriptor : generateDelegatedMembers(trace, jetClassOrObject, classDescriptor)) {
memberDescriptor.copy(newOwner, modality, memberDescriptor.getVisibility(), CallableMemberDescriptor.Kind.DELEGATION, false); if (descriptor instanceof PropertyDescriptor) {
result.add(copy); PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
classDescriptor.getBuilder().addPropertyDescriptor(propertyDescriptor);
}
else if (descriptor instanceof SimpleFunctionDescriptor) {
SimpleFunctionDescriptor functionDescriptor = (SimpleFunctionDescriptor) descriptor;
classDescriptor.getBuilder().addFunctionDescriptor(functionDescriptor);
} }
} }
//noinspection unchecked
return (Collection) result;
} }
} }
@@ -114,9 +114,9 @@ public class OverrideResolver {
} }
} }
PsiElement declaration = BindingContextUtils.classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor); JetClassOrObject classOrObject = (JetClassOrObject) BindingContextUtils
DelegationResolver.addDelegatedMembers(trace, (JetClassOrObject) declaration, classDescriptor); .classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
DelegationResolver.generateDelegatesInAClass(classDescriptor, trace, classOrObject);
generateOverridesInAClass(classDescriptor); generateOverridesInAClass(classDescriptor);
} }
@@ -42,6 +42,8 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import static org.jetbrains.jet.lang.resolve.DelegationResolver.generateDelegationCandidates;
public class LazyClassMemberScope extends AbstractLazyMemberScope<LazyClassDescriptor, ClassMemberDeclarationProvider> { public class LazyClassMemberScope extends AbstractLazyMemberScope<LazyClassDescriptor, ClassMemberDeclarationProvider> {
@NotNull @NotNull
@@ -268,9 +270,7 @@ public class LazyClassMemberScope extends AbstractLazyMemberScope<LazyClassDescr
typeReference, typeReference,
resolveSession.getTrace(), resolveSession.getTrace(),
false); false);
Collection<T> descriptors = result.addAll(generateDelegationCandidates(thisDescriptor, supertype, extractor.extract(supertype, name)));
DelegationResolver.generateDelegatedMembers(thisDescriptor, extractor.extract(supertype, name));
result.addAll(descriptors);
} }
} }
} }
@@ -0,0 +1,23 @@
package test
abstract class A {
abstract fun foo()
}
trait X : A {
fun bar() {
}
}
open class B() : A() {
override fun foo() {
}
}
class C() : A(), X {
override fun foo() {
}
}
class D(val c: C) : B(), X by c {
}
@@ -0,0 +1,30 @@
package test
internal abstract class A {
/*primary*/ public constructor A()
internal abstract fun foo(): jet.Unit
}
internal open class B : test.A {
/*primary*/ public constructor B()
internal open override /*1*/ fun foo(): jet.Unit
}
internal final class C : test.A, test.X {
/*primary*/ public constructor C()
internal open override /*1*/ /*fake_override*/ fun bar(): jet.Unit
internal open override /*2*/ fun foo(): jet.Unit
}
internal final class D : test.B, test.X {
/*primary*/ public constructor D(/*0*/ c: test.C)
internal final val c: test.C
internal final fun <get-c>(): test.C
internal open override /*1*/ /*delegation*/ fun bar(): jet.Unit
internal open override /*2*/ /*fake_override*/ fun foo(): jet.Unit
}
internal trait X : test.A {
internal open fun bar(): jet.Unit
internal abstract override /*1*/ /*fake_override*/ fun foo(): jet.Unit
}
@@ -426,6 +426,11 @@ public class DescriptorSerializationTestGenerated extends AbstractDescriptorSeri
doTest("compiler/testData/loadKotlin/fun/InheritValsDifferentTypes.kt"); doTest("compiler/testData/loadKotlin/fun/InheritValsDifferentTypes.kt");
} }
@TestMetadata("NoDelegationForFunctionInheritedFromTraitSuperClass.kt")
public void testNoDelegationForFunctionInheritedFromTraitSuperClass() throws Exception {
doTest("compiler/testData/loadKotlin/fun/NoDelegationForFunctionInheritedFromTraitSuperClass.kt");
}
@TestMetadata("NoSamAdapter.kt") @TestMetadata("NoSamAdapter.kt")
public void testNoSamAdapter() throws Exception { public void testNoSamAdapter() throws Exception {
doTest("compiler/testData/loadKotlin/fun/NoSamAdapter.kt"); doTest("compiler/testData/loadKotlin/fun/NoSamAdapter.kt");
@@ -645,6 +645,11 @@ public class LoadCompiledKotlinTestGenerated extends AbstractLoadCompiledKotlinT
doTestWithAccessors("compiler/testData/loadKotlin/fun/InheritValsDifferentTypes.kt"); doTestWithAccessors("compiler/testData/loadKotlin/fun/InheritValsDifferentTypes.kt");
} }
@TestMetadata("NoDelegationForFunctionInheritedFromTraitSuperClass.kt")
public void testNoDelegationForFunctionInheritedFromTraitSuperClass() throws Exception {
doTestWithAccessors("compiler/testData/loadKotlin/fun/NoDelegationForFunctionInheritedFromTraitSuperClass.kt");
}
@TestMetadata("NoSamAdapter.kt") @TestMetadata("NoSamAdapter.kt")
public void testNoSamAdapter() throws Exception { public void testNoSamAdapter() throws Exception {
doTestWithAccessors("compiler/testData/loadKotlin/fun/NoSamAdapter.kt"); doTestWithAccessors("compiler/testData/loadKotlin/fun/NoSamAdapter.kt");
@@ -647,6 +647,11 @@ public class LazyResolveNamespaceComparingTestGenerated extends AbstractLazyReso
doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/fun/InheritValsDifferentTypes.kt"); doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/fun/InheritValsDifferentTypes.kt");
} }
@TestMetadata("NoDelegationForFunctionInheritedFromTraitSuperClass.kt")
public void testNoDelegationForFunctionInheritedFromTraitSuperClass() throws Exception {
doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/fun/NoDelegationForFunctionInheritedFromTraitSuperClass.kt");
}
@TestMetadata("NoSamAdapter.kt") @TestMetadata("NoSamAdapter.kt")
public void testNoSamAdapter() throws Exception { public void testNoSamAdapter() throws Exception {
doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/fun/NoSamAdapter.kt"); doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/fun/NoSamAdapter.kt");