Generate bridges for trait implementations properly

The intent was to keep the bridge codegen model as simple as possible: we
should be able to figure out all necessary bridges only by a minimal interface
that FunctionHandle provides (isAbstract, isDeclaration, getOverridden)

Add different tests for bridges

 #KT-318 Obsolete
This commit is contained in:
Alexander Udalov
2014-04-07 22:29:00 +04:00
parent 4a12ea9bda
commit 79e7ee91e4
14 changed files with 289 additions and 79 deletions
+72 -7
View File
@@ -16,8 +16,12 @@
package org.jetbrains.jet.codegen.bridges
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.descriptors.Modality
import org.jetbrains.jet.lang.descriptors.*
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.lang.resolve.OverridingUtil
import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil
import org.jetbrains.jet.lang.types.TypeUtils
import java.util.LinkedHashSet
public fun <Signature> generateBridgesForFunctionDescriptor(
descriptor: FunctionDescriptor,
@@ -26,11 +30,72 @@ public fun <Signature> generateBridgesForFunctionDescriptor(
return generateBridges(DescriptorBasedFunctionHandle(descriptor), { signature(it.descriptor) })
}
/**
* An implementation of FunctionHandle based on descriptors.
*
* This implementation workarounds a minor inconvenience in descriptor hierarchy regarding traits with implementations.
* Consider the following hierarchy:
*
* trait A { fun foo() = 42 }
* class B : A
*
* In terms of descriptors, we'll have a declaration in trait A with modality=OPEN and a fake override in class B with modality=OPEN.
* For the purposes of bridge generation though, it's much easier to "move" all implementations out of traits into their child classes,
* i.e. treat the function in A as a declaration with modality=ABSTRACT and a function in B as a _declaration_ with modality=OPEN.
*
* This provides us with the nice invariant that all implementations (concrete declarations) are always in classes. This means we _always_
* can generate a bridge near an implementation (of course, in case it has a super-declaration with a different signature). Ultimately this
* eases the process of determining what bridges are already generated in our supertypes and need to be inherited, not regenerated.
*/
private data class DescriptorBasedFunctionHandle(val descriptor: FunctionDescriptor) : FunctionHandle {
override val isDeclaration: Boolean = descriptor.getKind().isReal()
override val isAbstract: Boolean = descriptor.getModality() == Modality.ABSTRACT
private val _overridden = descriptor.getOverriddenDescriptors().map { DescriptorBasedFunctionHandle(it.getOriginal()) }
override fun getOverridden(): Iterable<FunctionHandle> {
return descriptor.getOverriddenDescriptors().map { DescriptorBasedFunctionHandle(it.getOriginal()) }
}
override val isDeclaration: Boolean =
descriptor.getKind().isReal() ||
findTraitImplementation(descriptor) != null
override val isAbstract: Boolean =
descriptor.getModality() == Modality.ABSTRACT ||
DescriptorUtils.isTrait(descriptor.getContainingDeclaration())
override fun getOverridden() = _overridden
}
/**
* Given a fake override in a class, returns an overridden declaration with implementation in trait, such that a method delegating to that
* trait implementation should be generated into the class containing the fake override; or null if the given function is not a fake
* override of any trait implementation or such method was already generated into some superclass
*/
public fun findTraitImplementation(descriptor: CallableMemberDescriptor): CallableMemberDescriptor? {
if (descriptor.getKind().isReal()) return null
if (CallResolverUtil.isOrOverridesSynthesized(descriptor)) return null
// TODO: this logic is quite common for bridge generation, find a way to abstract it to a single place
// TODO: don't use filterOutOverridden() here, it's an internal front-end utility (see its implementation)
val overriddenDeclarations = OverridingUtil.getOverriddenDeclarations(descriptor)
val filteredOverriddenDeclarations = OverridingUtil.filterOutOverridden(LinkedHashSet(overriddenDeclarations))
var implementation: CallableMemberDescriptor? = null
for (overriddenDeclaration in filteredOverriddenDeclarations) {
if (DescriptorUtils.isTrait(overriddenDeclaration.getContainingDeclaration()) && overriddenDeclaration.getModality() != Modality.ABSTRACT) {
assert(implementation == null) { "Ambiguous overridden declaration: $descriptor" }
implementation = overriddenDeclaration
}
}
if (implementation == null) {
return null
}
// If this implementation is already generated into one of the superclasses, we need not generate it again, it'll be inherited
val containingClass = descriptor.getContainingDeclaration() as ClassDescriptor
val implClassType = implementation!!.getExpectedThisObject()!!.getType()
for (supertype in containingClass.getDefaultType().getConstructor().getSupertypes()) {
if (!DescriptorUtils.isTrait(supertype.getConstructor().getDeclarationDescriptor()!!) &&
TypeUtils.getAllSupertypes(supertype).contains(implClassType)) {
return null
}
}
return implementation
}
@@ -18,6 +18,7 @@ package org.jetbrains.jet.codegen;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.bridges.BridgesPackage;
import org.jetbrains.jet.codegen.context.ClassContext;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.descriptors.*;
@@ -103,7 +104,7 @@ public abstract class ClassBodyCodegen extends MemberCodegen {
for (DeclarationDescriptor memberDescriptor : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
if (memberDescriptor instanceof FunctionDescriptor) {
FunctionDescriptor member = (FunctionDescriptor) memberDescriptor;
if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
if (!member.getKind().isReal() && BridgesPackage.findTraitImplementation(member) == null) {
functionCodegen.generateBridges(member);
}
}
@@ -41,6 +41,7 @@ import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
import org.jetbrains.jet.lang.resolve.constants.ArrayValue;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.constants.JavaClassValue;
@@ -425,6 +426,9 @@ public class FunctionCodegen extends ParentCodegenAwareImpl {
if (owner.getContextKind() == OwnerKind.TRAIT_IMPL) return;
if (isTrait(descriptor.getContainingDeclaration())) return;
// If the function doesn't have a physical declaration among super-functions, it's a SAM adapter or alike and doesn't need bridges
if (CallResolverUtil.isOrOverridesSynthesized(descriptor)) return;
Set<Bridge<Method>> bridgesToGenerate = BridgesPackage.generateBridgesForFunctionDescriptor(
descriptor,
new Function1<FunctionDescriptor, Method>() {
@@ -17,15 +17,14 @@
package org.jetbrains.jet.codegen;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.Pair;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.binding.CodegenBinding;
import org.jetbrains.jet.codegen.binding.MutableClosure;
import org.jetbrains.jet.codegen.bridges.BridgesPackage;
import org.jetbrains.jet.codegen.context.ClassContext;
import org.jetbrains.jet.codegen.context.ConstructorContext;
import org.jetbrains.jet.codegen.context.MethodContext;
@@ -1464,11 +1463,14 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
private void generateTraitMethods() {
if (JetPsiUtil.isTrait(myClass)) return;
for (Pair<CallableMemberDescriptor, CallableMemberDescriptor> pair : getTraitImplementations(descriptor)) {
CallableMemberDescriptor inheritedMember = pair.first;
CallableMemberDescriptor traitMember = pair.second;
for (DeclarationDescriptor declaration : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
if (!(declaration instanceof CallableMemberDescriptor)) continue;
assert traitMember.getModality() != Modality.ABSTRACT : "Cannot delegate to abstract trait method: " + pair;
CallableMemberDescriptor inheritedMember = (CallableMemberDescriptor) declaration;
CallableMemberDescriptor traitMember = BridgesPackage.findTraitImplementation(inheritedMember);
if (traitMember == null) continue;
assert traitMember.getModality() != Modality.ABSTRACT : "Cannot delegate to abstract trait method: " + inheritedMember;
// inheritedMember can be abstract here. In order for FunctionCodegen to generate the method body, we're creating a copy here
// with traitMember's modality
@@ -1810,70 +1812,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
}
}
/**
* Return pairs of descriptors. First is member of this that should be implemented by delegating to trait,
* second is member of trait that contain implementation.
*/
@NotNull
private static List<Pair<CallableMemberDescriptor, CallableMemberDescriptor>> getTraitImplementations(@NotNull ClassDescriptor classDescriptor) {
List<Pair<CallableMemberDescriptor, CallableMemberDescriptor>> result = Lists.newArrayList();
for (DeclarationDescriptor declaration : classDescriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
if (!(declaration instanceof CallableMemberDescriptor)) continue;
CallableMemberDescriptor callableMemberDescriptor = (CallableMemberDescriptor) declaration;
CallableMemberDescriptor implementation = findTraitImplementation(callableMemberDescriptor);
if (implementation != null) {
result.add(Pair.create(callableMemberDescriptor, implementation));
}
}
return result;
}
/**
* Given a fake override descriptor, returns an overridden non-abstract descriptor whose container is a trait
*/
@Nullable
private static CallableMemberDescriptor findTraitImplementation(@NotNull CallableMemberDescriptor descriptor) {
if (descriptor.getKind().isReal()) return null;
if (CallResolverUtil.isOrOverridesSynthesized(descriptor)) return null;
Collection<CallableMemberDescriptor> overriddenDeclarations = OverridingUtil.getOverriddenDeclarations(descriptor);
Collection<CallableMemberDescriptor> filteredOverriddenDeclarations =
OverridingUtil.filterOutOverridden(Sets.newLinkedHashSet(overriddenDeclarations));
int count = 0;
CallableMemberDescriptor implementation = null;
for (CallableMemberDescriptor overriddenDeclaration : filteredOverriddenDeclarations) {
if (isTrait(overriddenDeclaration.getContainingDeclaration()) && overriddenDeclaration.getModality() != Modality.ABSTRACT) {
implementation = overriddenDeclaration;
count++;
}
}
if (implementation == null) {
return null;
}
assert count == 1 : "Ambiguous overridden declaration: " + descriptor;
ClassDescriptor containingClass = (ClassDescriptor) descriptor.getContainingDeclaration();
for (JetType supertype : containingClass.getDefaultType().getConstructor().getSupertypes()) {
//noinspection ConstantConditions
if (!isTrait(supertype.getConstructor().getDeclarationDescriptor()) &&
TypeUtils.getAllSupertypes(supertype).contains(implementation.getExpectedThisObject().getType())) {
return null;
}
}
return implementation;
}
public void addClassObjectPropertyToCopy(PropertyDescriptor descriptor, Object defaultValue) {
if (classObjectPropertiesToCopy == null) {
classObjectPropertiesToCopy = new ArrayList<PropertyAndDefaultValue>();
@@ -0,0 +1,28 @@
import java.util.ArrayList
import java.util.Arrays
abstract class A {
abstract fun foo(): List<String>
}
trait B {
fun foo(): ArrayList<String> = ArrayList(Arrays.asList("B"))
}
open class C : A(), B
trait D {
fun foo(): Collection<String>
}
class E : D, C()
fun box(): String {
val e = E()
var r = e.foo()[0]
r += (e : D).foo().iterator().next()
r += (e : C).foo()[0]
r += (e : B).foo()[0]
r += (e : A).foo()[0]
return if (r == "BBBBB") "OK" else "Fail: $r"
}
@@ -0,0 +1,17 @@
abstract class A {
abstract fun foo(): Any
}
trait B {
fun foo(): String = "B"
}
trait C : A, B
class D : A(), C
fun box(): String {
val d = D()
val r = d.foo() + (d : C).foo() + (d : B).foo() + (d : A).foo()
return if (r == "BBBB") "OK" else "Fail: $r"
}
@@ -0,0 +1,23 @@
var result = ""
trait A {
var foo: String
get() = result
set(value) {
result += value
}
}
abstract class B {
abstract var foo: Any
}
class C : A, B()
fun box(): String {
val c = C()
c.foo = "1"
(c : B).foo = "2"
(c : A).foo = "3"
return if (result == "123") "OK" else "Fail: $result"
}
@@ -0,0 +1,18 @@
trait A {
fun foo(): String = "A"
}
trait B {
fun foo(): Any
}
class C : A, B
fun box(): String {
val c = C()
var result = ""
result += c.foo()
result += (c : B).foo()
result += (c : A).foo()
return if (result == "AAA") "OK" else "Fail: $result"
}
@@ -0,0 +1,23 @@
var result = ""
trait Base
open class Child : Base
trait A<T : Base> {
fun <E : T> foo(a : E) {
result += "A"
}
}
class B : A<Child> {
override fun <E : Child> foo(a : E) {
result += "B"
}
}
fun box(): String {
val b = B()
b.foo(Child())
(b : A<Child>).foo(Child())
return if (result == "BB") "OK" else "Fail: $result"
}
@@ -0,0 +1,22 @@
import java.util.ArrayList
import java.util.Arrays
trait A {
fun foo(): Collection<String>
}
trait B : A {
override fun foo(): MutableCollection<String>
}
class C : B {
override fun foo(): MutableList<String> = ArrayList(Arrays.asList("C"))
}
fun box(): String {
val c = C()
var r = c.foo().iterator().next()
r += (c : B).foo().iterator().next()
r += (c : A).foo().iterator().next()
return if (r == "CCC") "OK" else "Fail: $r"
}
@@ -0,0 +1,15 @@
trait A {
fun foo(): Any = "A"
}
trait B : A {
override fun foo(): String = "B"
}
class C : B
fun box(): String {
val c = C()
var r = c.foo() + (c : B).foo() + (c : A).foo()
return if (r == "BBB") "OK" else "Fail: $r"
}
@@ -0,0 +1,15 @@
open class A {
open fun foo(): Any = "A"
}
trait B : A {
override fun foo(): String = "B"
}
class C : A(), B
fun box(): String {
val c = C()
val r = c.foo() + (c : B).foo() + (c : A).foo()
return if (r == "BBB") "OK" else "Fail: $r"
}
@@ -290,6 +290,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt");
}
@TestMetadata("complexTraitImpl.kt")
public void testComplexTraitImpl() throws Exception {
doTest("compiler/testData/codegen/box/bridges/complexTraitImpl.kt");
}
@TestMetadata("delegation.kt")
public void testDelegation() throws Exception {
doTest("compiler/testData/codegen/box/bridges/delegation.kt");
@@ -315,11 +320,26 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest("compiler/testData/codegen/box/bridges/fakeGenericCovariantOverride.kt");
}
@TestMetadata("fakeOverrideInTraitWithRequiredFromTraitImpl.kt")
public void testFakeOverrideInTraitWithRequiredFromTraitImpl() throws Exception {
doTest("compiler/testData/codegen/box/bridges/fakeOverrideInTraitWithRequiredFromTraitImpl.kt");
}
@TestMetadata("fakeOverrideOfPropertySetterInTraitImpl.kt")
public void testFakeOverrideOfPropertySetterInTraitImpl() throws Exception {
doTest("compiler/testData/codegen/box/bridges/fakeOverrideOfPropertySetterInTraitImpl.kt");
}
@TestMetadata("fakeOverrideOfTraitImpl.kt")
public void testFakeOverrideOfTraitImpl() throws Exception {
doTest("compiler/testData/codegen/box/bridges/fakeOverrideOfTraitImpl.kt");
}
@TestMetadata("fakeOverrideWithImplementationInTrait.kt")
public void testFakeOverrideWithImplementationInTrait() throws Exception {
doTest("compiler/testData/codegen/box/bridges/fakeOverrideWithImplementationInTrait.kt");
}
@TestMetadata("fakeOverrideWithSeveralSuperDeclarations.kt")
public void testFakeOverrideWithSeveralSuperDeclarations() throws Exception {
doTest("compiler/testData/codegen/box/bridges/fakeOverrideWithSeveralSuperDeclarations.kt");
@@ -360,6 +380,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest("compiler/testData/codegen/box/bridges/kt2920.kt");
}
@TestMetadata("kt318.kt")
public void testKt318() throws Exception {
doTest("compiler/testData/codegen/box/bridges/kt318.kt");
}
@TestMetadata("longChainOneBridge.kt")
public void testLongChainOneBridge() throws Exception {
doTest("compiler/testData/codegen/box/bridges/longChainOneBridge.kt");
@@ -375,6 +400,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest("compiler/testData/codegen/box/bridges/methodFromTrait.kt");
}
@TestMetadata("noBridgeOnMutableCollectionInheritance.kt")
public void testNoBridgeOnMutableCollectionInheritance() throws Exception {
doTest("compiler/testData/codegen/box/bridges/noBridgeOnMutableCollectionInheritance.kt");
}
@TestMetadata("overrideAbstractProperty.kt")
public void testOverrideAbstractProperty() throws Exception {
doTest("compiler/testData/codegen/box/bridges/overrideAbstractProperty.kt");
@@ -440,6 +470,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest("compiler/testData/codegen/box/bridges/simpleUpperBound.kt");
}
@TestMetadata("traitImplInheritsTraitImpl.kt")
public void testTraitImplInheritsTraitImpl() throws Exception {
doTest("compiler/testData/codegen/box/bridges/traitImplInheritsTraitImpl.kt");
}
@TestMetadata("traitWithRequiredCovariantOverride.kt")
public void testTraitWithRequiredCovariantOverride() throws Exception {
doTest("compiler/testData/codegen/box/bridges/traitWithRequiredCovariantOverride.kt");
}
@TestMetadata("twoParentsWithDifferentMethodsTwoBridges.kt")
public void testTwoParentsWithDifferentMethodsTwoBridges() throws Exception {
doTest("compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges.kt");
@@ -318,7 +318,8 @@ public class OverridingUtil {
*
* @see CallableMemberDescriptor.Kind#isReal()
*/
public static Collection<CallableMemberDescriptor> getOverriddenDeclarations(CallableMemberDescriptor descriptor) {
@NotNull
public static Collection<CallableMemberDescriptor> getOverriddenDeclarations(@NotNull CallableMemberDescriptor descriptor) {
Map<ClassDescriptor, CallableMemberDescriptor> result = Maps.newHashMap();
getOverriddenDeclarations(descriptor, result);
return result.values();