diff --git a/compiler/backend-common/backend-common.iml b/compiler/backend-common/backend-common.iml index 4184658ad62..d0975d60831 100644 --- a/compiler/backend-common/backend-common.iml +++ b/compiler/backend-common/backend-common.iml @@ -7,8 +7,8 @@ - - + + diff --git a/compiler/backend-common/src/bridges/bridges.kt b/compiler/backend-common/src/bridges/bridges.kt new file mode 100644 index 00000000000..7ebc16845de --- /dev/null +++ b/compiler/backend-common/src/bridges/bridges.kt @@ -0,0 +1,117 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.codegen.bridges + +import org.jetbrains.jet.utils.DFS +import java.util.HashSet + +public trait FunctionHandle { + val isDeclaration: Boolean + val isAbstract: Boolean + + fun getOverridden(): Iterable +} + +public data class Bridge( + public val from: Signature, + public val to: Signature +) { + override fun toString() = "$from -> $to" +} + + +public fun generateBridges( + function: Function, + signature: (Function) -> Signature +): Set> { + // If it's an abstract function, no bridges are needed: when an implementation will appear in some concrete subclass, all necessary + // bridges will be generated there + if (function.isAbstract) return setOf() + + val fake = !function.isDeclaration + + // If it's a concrete fake override and all of its super-functions are concrete, then every possible bridge is already generated + // into some of the super-classes and will be inherited in this class + if (fake && function.getOverridden().none { it.isAbstract }) return setOf() + + val implementation = findConcreteSuperDeclaration(function) + + val bridgesToGenerate = findAllReachableDeclarations(function).mapTo(HashSet(), signature) + + if (fake) { + // If it's a concrete fake override, some of the bridges may be inherited from the super-classes. Specifically, bridges for all + // declarations that are reachable from all concrete immediate super-functions of the given function. Note that all such bridges are + // guaranteed to delegate to the same implementation as bridges for the given function, that's why it's safe to inherit them + [suppress("UNCHECKED_CAST")] + for (overridden in function.getOverridden() as Iterable) { + if (!overridden.isAbstract) { + bridgesToGenerate.removeAll(findAllReachableDeclarations(overridden).map(signature)) + } + } + } + + val method = signature(implementation) + bridgesToGenerate.remove(method) + return bridgesToGenerate.map { Bridge(it, method) }.toSet() +} + +private fun findAllReachableDeclarations(function: Function): MutableSet { + val collector = object : DFS.NodeHandlerWithListResult() { + override fun afterChildren(current: Function?) { + if (current != null && current.isDeclaration) { + result.add(current) + } + } + } + [suppress("UNCHECKED_CAST")] + DFS.dfs(listOf(function), { it!!.getOverridden() as Iterable }, collector) + return HashSet(collector.result()) +} + +/** + * Given a concrete function, finds an implementation (a concrete declaration) of this function in the supertypes. + * The implementation is guaranteed to exist because if it wouldn't, the given function would've been abstract + */ +private fun findConcreteSuperDeclaration(function: Function): Function { + require(!function.isAbstract, "Only concrete functions have implementations: $function") + + if (function.isDeclaration) return function + + // To find an implementation of a concrete fake override, we should find among all its super-functions such function that: + // 1) it's a concrete declaration + // 2) it's not reachable from any other declaration reachable from the given function + // The compiler guarantees that there will be exactly one such function. + // The following algorithm is used: first, we find all declarations reachable from the given function. Then for each such declaration + // we remove from that result all declarations reachable from it. The result is now guaranteed to have exactly one concrete declaration + // (and possibly some abstract declarations, which don't matter) + + val result = findAllReachableDeclarations(function) + val toRemove = HashSet() + for (declaration in result) { + val reachable = findAllReachableDeclarations(declaration) + reachable.remove(declaration) + toRemove.addAll(reachable) + } + result.removeAll(toRemove) + + val concreteRelevantDeclarations = result.filter { !it.isAbstract } + if (concreteRelevantDeclarations.size != 1) { + error("Concrete fake override $function should have exactly one concrete super-declaration: $concreteRelevantDeclarations") + } + + return concreteRelevantDeclarations[0] +} diff --git a/compiler/backend-common/src/bridges/impl.kt b/compiler/backend-common/src/bridges/impl.kt new file mode 100644 index 00000000000..d3ee805f462 --- /dev/null +++ b/compiler/backend-common/src/bridges/impl.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.codegen.bridges + +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor +import org.jetbrains.jet.lang.descriptors.Modality + +public fun generateBridgesForFunctionDescriptor( + descriptor: FunctionDescriptor, + signature: (FunctionDescriptor) -> Signature +): Set> { + return generateBridges(DescriptorBasedFunctionHandle(descriptor), { signature(it.descriptor) }) +} + +private data class DescriptorBasedFunctionHandle(val descriptor: FunctionDescriptor) : FunctionHandle { + override val isDeclaration: Boolean = descriptor.getKind().isReal() + override val isAbstract: Boolean = descriptor.getModality() == Modality.ABSTRACT + + override fun getOverridden(): Iterable { + return descriptor.getOverriddenDescriptors().map { DescriptorBasedFunctionHandle(it.getOriginal()) } + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java index 9fa2ef06d9a..648b52e0aaf 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ClassBodyCodegen.java @@ -18,8 +18,6 @@ package org.jetbrains.jet.codegen; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.org.objectweb.asm.MethodVisitor; -import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.jet.codegen.context.ClassContext; import org.jetbrains.jet.codegen.state.GenerationState; import org.jetbrains.jet.lang.descriptors.*; @@ -29,13 +27,15 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.name.Name; +import org.jetbrains.org.objectweb.asm.MethodVisitor; +import org.jetbrains.org.objectweb.asm.Type; import java.util.Collections; import java.util.List; +import static org.jetbrains.jet.codegen.binding.CodegenBinding.enumEntryNeedSubclass; import static org.jetbrains.org.objectweb.asm.Opcodes.ACC_STATIC; import static org.jetbrains.org.objectweb.asm.Opcodes.RETURN; -import static org.jetbrains.jet.codegen.binding.CodegenBinding.enumEntryNeedSubclass; public abstract class ClassBodyCodegen extends MemberCodegen { protected final JetClassOrObject myClass; @@ -104,7 +104,7 @@ public abstract class ClassBodyCodegen extends MemberCodegen { if (memberDescriptor instanceof FunctionDescriptor) { FunctionDescriptor member = (FunctionDescriptor) memberDescriptor; if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - functionCodegen.generateBridgesForFakeOverride(member); + functionCodegen.generateBridges(member); } } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java index 68b13498a04..e0f1637f0a6 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -23,10 +23,11 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import kotlin.Function1; -import kotlin.KotlinPackage; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.codegen.binding.CodegenBinding; +import org.jetbrains.jet.codegen.bridges.Bridge; +import org.jetbrains.jet.codegen.bridges.BridgesPackage; import org.jetbrains.jet.codegen.context.CodegenContext; import org.jetbrains.jet.codegen.context.MethodContext; import org.jetbrains.jet.codegen.context.PackageFacadeContext; @@ -45,7 +46,6 @@ import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.resolve.constants.JavaClassValue; import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.name.FqName; -import org.jetbrains.jet.utils.DFS; import org.jetbrains.org.objectweb.asm.AnnotationVisitor; import org.jetbrains.org.objectweb.asm.Label; import org.jetbrains.org.objectweb.asm.MethodVisitor; @@ -56,7 +56,10 @@ import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.*; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; import static org.jetbrains.jet.codegen.AsmUtil.*; import static org.jetbrains.jet.codegen.JvmSerializationBindings.*; @@ -417,108 +420,31 @@ public class FunctionCodegen extends ParentCodegenAwareImpl { return bytecode; } - private void generateBridges(@NotNull FunctionDescriptor descriptor) { + public void generateBridges(@NotNull FunctionDescriptor descriptor) { if (descriptor instanceof ConstructorDescriptor) return; if (owner.getContextKind() == OwnerKind.TRAIT_IMPL) return; if (isTrait(descriptor.getContainingDeclaration())) return; - Set bridgesToGenerate = findAllReachableDeclarations(descriptor); - - Method method = typeMapper.mapSignature(descriptor).getAsmMethod(); - bridgesToGenerate.remove(method); + Set> bridgesToGenerate = BridgesPackage.generateBridgesForFunctionDescriptor( + descriptor, + new Function1() { + @Override + public Method invoke(FunctionDescriptor descriptor) { + return typeMapper.mapSignature(descriptor).getAsmMethod(); + } + } + ); if (!bridgesToGenerate.isEmpty()) { - PsiElement origin = callableDescriptorToDeclaration(bindingContext, descriptor); - for (Method bridge : bridgesToGenerate) { - generateBridge(origin, bridge, method); + PsiElement origin = descriptor.getKind().isReal() + ? callableDescriptorToDeclaration(bindingContext, descriptor) + : null; + for (Bridge bridge : bridgesToGenerate) { + generateBridge(origin, bridge.getFrom(), bridge.getTo()); } } } - public void generateBridgesForFakeOverride(@NotNull FunctionDescriptor descriptor) { - if (owner.getContextKind() == OwnerKind.TRAIT_IMPL) return; - - // If it's an abstract fake override, no bridges are needed: when an implementation will appear in some subclass, all necessary - // bridges will be generated there - if (descriptor.getModality() == Modality.ABSTRACT) return; - - // If it's a concrete fake override and all of its super-functions are non-abstract, then every possible bridge is already generated - // into some of the super-classes and will be inherited in this class - if (!hasAbstractSuperFunction(descriptor)) return; - - FunctionDescriptor implementation = findNonAbstractDeclaration(descriptor); - - Set bridgesToGenerate = findAllReachableDeclarations(descriptor); - // TODO: remove also all declarations reachable from all reachable non-abstract fake overrides in classes - bridgesToGenerate.removeAll(findAllReachableDeclarations(implementation)); - - Method method = typeMapper.mapSignature(implementation).getAsmMethod(); - for (Method bridge : bridgesToGenerate) { - generateBridge(null, bridge, method); - } - } - - private static boolean hasAbstractSuperFunction(@NotNull FunctionDescriptor descriptor) { - for (FunctionDescriptor overridden : descriptor.getOverriddenDescriptors()) { - if (overridden.getOriginal().getModality() == Modality.ABSTRACT) { - return true; - } - } - return false; - } - - @NotNull - private Set findAllReachableDeclarations(@NotNull FunctionDescriptor descriptor) { - DFS.Neighbors neighbors = new DFS.Neighbors() { - @NotNull - @Override - public Iterable getNeighbors(FunctionDescriptor current) { - return KotlinPackage.map(current.getOverriddenDescriptors(), new Function1() { - @Override - public FunctionDescriptor invoke(FunctionDescriptor descriptor) { - return descriptor.getOriginal(); - } - }); - } - }; - DFS.NodeHandlerWithListResult collector = - new DFS.NodeHandlerWithListResult() { - @Override - public void afterChildren(FunctionDescriptor current) { - if (current.getKind().isReal()) { - result.add(typeMapper.mapSignature(current).getAsmMethod()); - } - } - }; - DFS.dfs(Collections.singleton(descriptor), neighbors, collector); - return new HashSet(collector.result()); - } - - /** - * Given a non-abstract function of any kind, finds an implementation (a non-abstract non-fake-override) of this function - * in the supertypes. The implementation is guaranteed to exist because if it wouldn't, the given function would've been abstract. - */ - @NotNull - private static FunctionDescriptor findNonAbstractDeclaration(@NotNull FunctionDescriptor descriptor) { - if (descriptor.getModality() == Modality.ABSTRACT) { - throw new IllegalArgumentException("Only non-abstract functions have implementations: " + descriptor); - } - - if (descriptor.getKind().isReal()) { - return descriptor; - } - - for (FunctionDescriptor overriddenSubstituted : descriptor.getOverriddenDescriptors()) { - FunctionDescriptor overridden = overriddenSubstituted.getOriginal(); - if (overridden.getModality() != Modality.ABSTRACT) { - return findNonAbstractDeclaration(overridden); - } - } - - throw new IllegalStateException("No non-abstract declaration found for non-abstract function: " + - descriptor + "\nOverridden: " + descriptor.getOverriddenDescriptors()); - } - @NotNull private static String[] getThrownExceptions(@NotNull FunctionDescriptor function, @NotNull final JetTypeMapper mapper) { AnnotationDescriptor annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.throws")); @@ -767,23 +693,6 @@ public class FunctionCodegen extends ParentCodegenAwareImpl { return true; } - private static boolean differentMethods(Method method, Method overridden) { - if (!method.getReturnType().equals(overridden.getReturnType())) { - return true; - } - Type[] methodArgumentTypes = method.getArgumentTypes(); - Type[] overriddenArgumentTypes = overridden.getArgumentTypes(); - if (methodArgumentTypes.length != overriddenArgumentTypes.length) { - return true; - } - for (int i = 0; i != methodArgumentTypes.length; ++i) { - if (!methodArgumentTypes[i].equals(overriddenArgumentTypes[i])) { - return true; - } - } - return false; - } - private void generateBridge(@Nullable PsiElement origin, @NotNull Method bridge, @NotNull Method delegateTo) { int flags = ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC; // TODO. diff --git a/compiler/testData/codegen/box/bridges/complexMultiInheritance.kt b/compiler/testData/codegen/box/bridges/complexMultiInheritance.kt new file mode 100644 index 00000000000..cce9635c317 --- /dev/null +++ b/compiler/testData/codegen/box/bridges/complexMultiInheritance.kt @@ -0,0 +1,25 @@ +open class A { + open fun foo(): Any = "A" +} + +trait B : A + +open class C : A() { + override fun foo(): Int = 222 +} + +trait D { + fun foo(): Number +} + +class E : B, C(), D + +fun box(): String { + val e = E() + if (e.foo() != 222) return "Fail 1" + if ((e : D).foo() != 222) return "Fail 2" + if ((e : C).foo() != 222) return "Fail 3" + if ((e : B).foo() != 222) return "Fail 4" + if ((e : A).foo() != 222) return "Fail 5" + return "OK" +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/BridgeTest.kt b/compiler/tests/org/jetbrains/jet/codegen/BridgeTest.kt new file mode 100644 index 00000000000..d980273efa9 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/codegen/BridgeTest.kt @@ -0,0 +1,563 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.codegen + +import junit.framework.TestCase +import org.jetbrains.jet.codegen.bridges.* +import kotlin.test.assertEquals +import java.util.HashSet +import org.jetbrains.jet.utils.DFS + +class BridgeTest : TestCase() { + private class Fun(val text: String) : FunctionHandle { + override val isDeclaration: Boolean get() = text[1] == 'D' + override val isAbstract: Boolean get() = text[0] == '-' + + val signature: Char get() = text[2] + + val overriddenFunctions: MutableList = arrayListOf() + override fun getOverridden() = overriddenFunctions + + override fun toString() = text + } + + private class Meth(val function: Fun) { + override fun equals(other: Any?) = other is Meth && other.function.signature == function.signature + override fun hashCode() = function.signature.hashCode() + override fun toString() = function.toString() + } + + private fun v(text: String): Fun { + assert(text.length == 3, "Function vertex representation should consist of 3 characters: $text") + assert(text[0] in setOf('-', '+'), "First character should be '-' for abstract functions or '+' for concrete ones: $text") + assert(text[1] in setOf('D', 'F'), "Second character should be 'D' for declarations or 'F' for fake overrides: $text") + assert(text[2].isDigit(), + "Third character should be a number that represents a signature (same numbers mean the same method signatures)") + return Fun(text) + } + + private fun bridge(from: Fun, to: Fun): Bridge = Bridge(Meth(from), Meth(to)) + + /** + * Constructs a graph out of the given pairs of vertices. First vertex should be a function in the derived class, + * second -- the corresponding overridden function in a superclass. + * + * Checks that the graph satisfies the following conditions: + * 1. Each fake override should have a super-declaration + * 2. Each concrete fake override should have exactly one concrete super-declaration. More accurately, for each concrete + * fake override F there is a concrete declaration D in supertypes such that every other concrete super-declaration of F + * is either reachable from D or is reachable from any abstract super-declaration of F (or both). This condition is effectively + * equivalent to the compiler guarantee that each class inherits not more than one implementation of each function. + * + * NOTE: abstract fake overrides CAN have concrete super-declarations! (traits with required classes) + * + * NOTE 2: the graph validation procedure probably doesn't cover all the possible cases compared to the analogous code in the compiler. + * There may be bugs here and they should be fixed accordingly. + */ + private fun graph(vararg edges: Pair) { + for ((from, to) in edges) { + from.overriddenFunctions.add(to) + } + + fun findAllReachableDeclarations(from: Fun): MutableSet { + val handler = object : DFS.NodeHandlerWithListResult() { + override fun afterChildren(current: Fun?) { + if (current!!.isDeclaration) { + result.add(current) + } + } + } + DFS.dfs(listOf(from), { it!!.getOverridden() }, handler) + val result = HashSet(handler.result()) + result.remove(from) + return result + } + + val vertices = edges.flatMapTo(HashSet()) { pair -> listOf(pair.first, pair.second) } + + for (vertex in vertices) { + val directConcreteSuperFunctions = vertex.overriddenFunctions.filter { !it.isAbstract } + assert(directConcreteSuperFunctions.size <= 1, + "Incorrect test data: function $vertex has more than one direct concrete super-function: ${vertex.overriddenFunctions}\n" + + "This is not allowed because only classes can contain implementations (concrete functions), and having more than one " + + "concrete super-function means having more than one superclass, which is prohibited in Kotlin") + + if (vertex.isDeclaration) continue + + val superDeclarations = findAllReachableDeclarations(vertex) + assert(!superDeclarations.isEmpty(), "Incorrect test data: fake override vertex $vertex has no super-declarations") + + // Remove all declarations inherited by other declarations + val toRemove = HashSet() + for (superDeclaration in superDeclarations) { + toRemove.addAll(findAllReachableDeclarations(superDeclaration)) + } + superDeclarations.removeAll(toRemove) + val concreteDeclarations = superDeclarations.filter { !it.isAbstract } + + if (!vertex.isAbstract) { + assert(!concreteDeclarations.isEmpty(), + "Incorrect test data: concrete fake override vertex $vertex has no concrete super-declarations") + assert(concreteDeclarations.size == 1, + "Incorrect test data: concrete fake override vertex $vertex has more than one concrete super-declaration: " + + "$concreteDeclarations") + } + } + } + + private fun doTest(function: Fun, expectedBridges: Set>) { + val actualBridges = generateBridges(function, ::Meth) + assert(actualBridges.firstOrNull { it.from == it.to } == null, + "A bridge invoking itself was generated, which makes no sense, since it will result in StackOverflowError once called" + + ": $actualBridges") + assertEquals(expectedBridges, actualBridges, "Expected and actual bridge sets differ for function $function") + } + + // ------------------------------------------------------------------------------------------------------------------ + + // Simple tests with no bridges + + fun testOneVertexAbstract() { + val a = v("-D1") + graph() + doTest(a, setOf()) + } + + fun testOneVertexConcrete() { + val a = v("+D1") + graph() + doTest(a, setOf()) + } + + fun testSimpleFakeOverrideSameSignature() { + val a = v("+D1") + val b = v("+F1") + graph(b to a) + doTest(a, setOf()) + doTest(b, setOf()) + } + + fun testSimpleFakeOverrideDifferentSignature() { + val a = v("+D1") + val b = v("+F2") + graph(b to a) + doTest(a, setOf()) + doTest(b, setOf()) + } + + fun testSimpleDeclarationSameSignature() { + val a = v("+D1") + val b = v("+D1") + graph(b to a) + doTest(a, setOf()) + doTest(b, setOf()) + } + + fun testSimpleAbstractDeclarationSameSignature() { + val a = v("-D1") + val b = v("-D1") + graph(b to a) + doTest(a, setOf()) + doTest(b, setOf()) + } + + fun testSimpleAbstractDeclarationOverridesConcreteSameSignature() { + val a = v("+D1") + val b = v("-D1") + graph(b to a) + doTest(a, setOf()) + doTest(b, setOf()) + } + + // Simple tests where declaration "a" is inherited by declaration "b" with a different signature. + // Note that we don't generate bridges near abstract declarations in contrast to javac + + fun testSimpleConcreteDeclarationDifferentSignature() { + val a = v("+D1") + val b = v("+D2") + graph(b to a) + doTest(b, setOf(bridge(a, b))) + } + + fun testSimpleAbstractDeclarationDifferentSignature() { + val a = v("-D1") + val b = v("-D2") + graph(b to a) + doTest(b, setOf()) + } + + fun testSimpleAbstractDeclarationOverridesConcreteDifferentSignature() { + val a = v("+D1") + val b = v("-D2") + graph(b to a) + doTest(b, setOf()) + } + + fun testSimpleConcreteDeclarationOverridesAbstractDifferentSignature() { + val a = v("-D1") + val b = v("+D2") + graph(b to a) + doTest(b, setOf(bridge(a, b))) + } + + // Simple tests where declaration overrides declaration through a fake override in the super class, with a different signature + + fun testSimpleConcreteDeclarationOverridesConcreteThroughFakeOverride() { + val a = v("+D1") + val b = v("+F2") + val c = v("+D3") + graph(b to a, c to b) + doTest(a, setOf()) + doTest(b, setOf()) + doTest(c, setOf(bridge(a, c))) + } + + fun testSimpleConcreteDeclarationOverridesAbstractThroughFakeOverride() { + val a = v("-D1") + val b = v("-F2") + val c = v("+D3") + graph(b to a, c to b) + doTest(a, setOf()) + doTest(b, setOf()) + doTest(c, setOf(bridge(a, c))) + } + + fun testSimpleAbstractDeclarationOverridesConcreteThroughFakeOverride() { + val a = v("+D1") + val b = v("+F2") + val c = v("-D3") + graph(b to a, c to b) + doTest(a, setOf()) + doTest(b, setOf()) + doTest(c, setOf()) + } + + fun testSimpleAbstractDeclarationOverridesAbstractThroughFakeOverride() { + val a = v("-D1") + val b = v("-F2") + val c = v("-D3") + graph(b to a, c to b) + doTest(a, setOf()) + doTest(b, setOf()) + doTest(c, setOf()) + } + + // Declaration "c" overrides two declarations "a" and "b" + + fun testAbstractDeclarationOverridesTwoAbstractDeclarations() { + val a = v("-D1") + val b = v("-D2") + val c = v("-D3") + graph(c to a, c to b) + doTest(c, setOf()) + } + + fun testAbstractDeclarationOverridesAbstractAndConcreteDeclarations() { + val a = v("-D1") + val b = v("+D2") + val c = v("-D3") + graph(c to a, c to b) + doTest(c, setOf()) + } + + fun testConcreteDeclarationOverridesTwoAbstractDeclarations() { + val a = v("-D1") + val b = v("-D2") + val c = v("+D3") + graph(c to a, c to b) + doTest(c, setOf(bridge(a, c), bridge(b, c))) + } + + fun testConcreteDeclarationOverridesAbstractAndConcreteDeclarations() { + val a = v("-D1") + val b = v("+D2") + val c = v("+D3") + graph(c to a, c to b) + doTest(c, setOf(bridge(a, c), bridge(b, c))) + } + + // Declaration "c" overrides declaration "a" and declaration "b", which in turn overrides "a". + // We still need to generate both bridges near "c" to avoid several consecutive bridges in a call stack + + fun testConcreteDeclarationOverridesTwoInheritingDeclarations() { + val a = v("-D1") + val b = v("+D2") + val c = v("+D3") + graph(c to a, c to b, b to a) + doTest(a, setOf()) + doTest(b, setOf(bridge(a, b))) + doTest(c, setOf(bridge(a, c), bridge(b, c))) + } + + fun testConcreteDeclarationOverridesAbstractDeclarationOverridingConcrete() { + val a = v("+D1") + val b = v("-D2") + val c = v("+D3") + graph(c to a, c to b, b to a) + doTest(a, setOf()) + doTest(b, setOf()) + doTest(c, setOf(bridge(a, c), bridge(b, c))) + } + + // Diamonds where the sink (vertex "d") is a declaration: bridges from all super-declarations to "d" should be present + + fun testDiamondAbstractDeclarations() { + val a = v("-D1") + val b = v("-D2") + val c = v("-D3") + val d = v("-D4") + graph(b to a, c to a, d to b, d to c) + doTest(a, setOf()) + doTest(b, setOf()) + doTest(c, setOf()) + doTest(d, setOf()) + } + + fun testDiamondMixedDeclarations() { + val a = v("-D1") + val b = v("+D2") + val c = v("-D3") + val d = v("+D4") + graph(b to a, c to a, d to b, d to c) + doTest(a, setOf()) + doTest(b, setOf(bridge(a, b))) + doTest(c, setOf()) + doTest(d, setOf(bridge(a, d), bridge(b, d), bridge(c, d))) + } + + fun testDiamondAbstractFakeOverridesInTheMiddle() { + val a = v("-D1") + val b = v("-F2") + val c = v("-F3") + val d = v("+D4") + graph(b to a, c to a, d to b, d to c) + doTest(a, setOf()) + doTest(b, setOf()) + doTest(c, setOf()) + doTest(d, setOf(bridge(a, d))) + } + + // Fake override "c" overrides declarations "a" and "b": a bridge is needed if signatures are different and there's an implementation + + fun testAbstractFakeOverride() { + val a = v("-D1") + val b = v("-D2") + val c = v("-F3") + graph(c to a, c to b) + doTest(c, setOf()) + } + + fun testFakeOverrideSameSuperDeclarations() { + val a = v("-D1") + val b = v("+D1") + val c = v("+F2") + graph(c to a, c to b) + doTest(c, setOf()) + } + + fun testFakeOverrideAbstractAndConcreteDeclarations() { + val a = v("-D1") + val b = v("+D2") + val c = v("+F3") + graph(c to a, c to b) + doTest(c, setOf(bridge(a, b))) + } + + fun testFakeOverrideInheritingDeclarations() { + val a = v("-D1") + val b = v("+D2") + val c = v("+F3") + graph(c to a, c to b, b to a) + doTest(a, setOf()) + doTest(b, setOf(bridge(a, b))) + // Here bridge a->b is not needed in c, it's already present in b + doTest(c, setOf()) + } + + // Diamonds where the sink (vertex "d") is a fake override + + fun testDiamondFakeOverrideAbstractFakeAndConcrete() { + val a = v("-D1") + val b = v("-F2") + val c = v("+D3") + val d = v("+F4") + graph(b to a, c to a, d to b, d to c) + doTest(c, setOf(bridge(a, c))) + doTest(d, setOf()) + } + + fun testDiamondFakeOverrideAbstractAndConcrete() { + val a = v("-D1") + val b = v("-D2") + val c = v("+D3") + val d = v("+F4") + graph(b to a, c to a, d to b, d to c) + doTest(b, setOf()) + doTest(c, setOf(bridge(a, c))) + doTest(d, setOf(bridge(b, c))) + } + + fun testDiamondFakeOverrideAbstractOverridesConcrete() { + val a = v("+D1") + val b = v("+D2") + val c = v("-D3") + val d = v("+F4") + graph(b to a, c to a, d to b, d to c) + doTest(b, setOf(bridge(a, b))) + doTest(c, setOf()) + doTest(d, setOf(bridge(c, b))) + } + + // More complex tests where a fake override inherits several declarations + + fun testFakeOverrideInheritingDeclarationsAndAbstract() { + val a = v("-D1") + val b = v("-D2") + val c = v("+D3") + val e = v("+F4") + graph(e to a, e to b, e to c, c to b) + doTest(a, setOf()) + doTest(b, setOf()) + doTest(c, setOf(bridge(b, c))) + doTest(e, setOf(bridge(a, c))) + } + + fun testFakeOverrideManyDeclarations() { + val a = v("-D1") + val b = v("+D2") + val c = v("-D3") + val d = v("-D4") + val e = v("+F5") + graph(e to a, e to b, e to c, e to d) + doTest(e, setOf(bridge(a, b), bridge(c, b), bridge(d, b))) + } + + fun testFakeOverrideTwoDeclarationsThroughFakeOverrides() { + val a = v("+D1") + val b = v("+F2") + val c = v("-D3") + val d = v("-F4") + val e = v("+F5") + graph(b to a, d to c, e to b, e to d) + doTest(e, setOf(bridge(c, a))) + } + + fun testFakeOverrideMisleadingImplementation() { + val a = v("+D1") + val b = v("-D2") + val c = v("-D3") + val d = v("+D4") + val e = v("+F5") + val f = v("+F6") + graph(c to a, e to d, f to b, f to c, f to e) + doTest(e, setOf()) + // Although "a" is a concrete declaration, it's overridden with abstract in "c" and all bridges should delegate to "d" instead + doTest(f, setOf(bridge(a, d), bridge(b, d), bridge(c, d))) + } + + // Fake override overrides another fake override (or declaration) with some bridges already present there + + fun testFakeOverrideInheritsBridgeFromFakeOverride() { + val a = v("-D1") + val b = v("+D2") + val c = v("+F3") + val d = v("+F4") + graph(c to a, c to b, d to c) + doTest(c, setOf(bridge(a, b))) + doTest(d, setOf()) + } + + fun testFakeOverrideInheritsBridgesAndAbstract() { + val a = v("-D1") + val b = v("+D2") + val c = v("+F3") + val d = v("-D4") + val e = v("+F5") + graph(c to a, c to b, e to c, e to d) + doTest(c, setOf(bridge(a, b))) + // It's important that "e" shouldn't have "a->b" bridge, because it's inherited from "c" + doTest(e, setOf(bridge(d, b))) + } + + fun testFakeOverrideInheritsBridgeFromDeclaration() { + val a = v("+D1") + val b = v("-D2") + val c = v("+F3") + val d = v("+D4") + val e = v("-D5") + val f = v("+F6") + graph(c to a, c to b, d to c, f to d, f to e) + doTest(c, setOf(bridge(b, a))) + doTest(d, setOf(bridge(a, d), bridge(b, d))) + doTest(f, setOf(bridge(e, d))) + } + + fun testFakeOverrideDiamondWithExtraAbstract() { + val a = v("+D1") + val b = v("-F2") + val c = v("+D3") + val d = v("-D4") + val e = v("+F5") + graph(b to a, c to a, e to b, e to c, e to d) + doTest(c, setOf(bridge(a, c))) + // It's important that implementation of "e" is "c", not "a", so a bridge "d->c" should exist + doTest(e, setOf(bridge(d, c))) + } + + fun testLongTreeOfFakeOverrideBridgeInheritance() { + val a = v("+D1") + val b = v("-D2") + val c = v("-D3") + val d = v("+F4") + val e = v("-D5") + val f = v("-D6") + val g = v("+F7") + val h = v("-D8") + val i = v("-D9") + val j = v("+F0") + graph(d to a, d to b, d to c, + g to d, g to e, g to f, + j to g, j to h, j to i) + doTest(d, setOf(bridge(b, a), bridge(c, a))) + doTest(g, setOf(bridge(e, a), bridge(f, a))) + doTest(j, setOf(bridge(h, a), bridge(i, a))) + } + + fun testFakeOverrideShouldNotInheritBridgeFromAbstractDeclaration() { + val a = v("-D1") + val b = v("+D2") + val c = v("+F3") + val d = v("-D4") + val e = v("+D5") + val f = v("+F6") + graph(c to a, c to b, d to c, f to d, f to e) + doTest(c, setOf(bridge(a, b))) + // Although "f" has a concrete fake override "c" in its hierarchy, we should NOT silently inherit a bridge from it, + // because it corresponds to another implementation ("b"). Instead a bunch of new bridges should be generated, delegating to "e" + doTest(f, setOf(bridge(a, e), bridge(b, e), bridge(d, e))) + } + + fun testFakeOverrideShouldNotInheritBridgeFromAbstractFakeOverride() { + val a = v("+D1") + val b = v("-D2") + val c = v("-F3") + val d = v("+F4") + graph(c to a, c to b, d to a, d to c) + doTest(c, setOf()) + doTest(d, setOf(bridge(b, a))) + } +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index a7643d4176c..cbba360a843 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -285,6 +285,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/box/bridges"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("complexMultiInheritance.kt") + public void testComplexMultiInheritance() throws Exception { + doTest("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt"); + } + @TestMetadata("delegation.kt") public void testDelegation() throws Exception { doTest("compiler/testData/codegen/box/bridges/delegation.kt"); diff --git a/core/util.runtime/src/org/jetbrains/jet/utils/DFS.java b/core/util.runtime/src/org/jetbrains/jet/utils/DFS.java index 99dfd5d71cb..0cef37a5c9f 100644 --- a/core/util.runtime/src/org/jetbrains/jet/utils/DFS.java +++ b/core/util.runtime/src/org/jetbrains/jet/utils/DFS.java @@ -16,6 +16,7 @@ package org.jetbrains.jet.utils; +import kotlin.jvm.KotlinSignature; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -87,6 +88,7 @@ public class DFS { } public interface Neighbors { + @KotlinSignature("fun getNeighbors(current: N?): Iterable") @NotNull Iterable getNeighbors(N current); } @@ -123,9 +125,11 @@ public class DFS { } public static abstract class NodeHandlerWithListResult extends AbstractNodeHandler> { + @NotNull protected final LinkedList result = new LinkedList(); @Override + @NotNull public List result() { return result; }