diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/state/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/jet/codegen/state/JetTypeMapper.java index 43a0403d584..456fdb051ab 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/state/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/state/JetTypeMapper.java @@ -606,13 +606,38 @@ public class JetTypeMapper extends BindingTraceAware { } sw.writeReturnType(); - mapReturnType(f, sw); + if (forceBoxedReturnType(f)) { + // TYPE_PARAMETER is a hack to automatically box the return type + //noinspection ConstantConditions + mapType(f.getReturnType(), sw, JetTypeMapperMode.TYPE_PARAMETER); + } + else { + mapReturnType(f, sw); + } sw.writeReturnTypeEnd(); } return sw.makeJvmMethodSignature(mapFunctionName(f)); } + /** + * @return true iff a given function descriptor should be compiled to a method with boxed return type regardless of whether return type + * of that descriptor is nullable or not. This happens when a function returning a value of a primitive type overrides another function + * with a non-primitive return type. In that case the generated method's return type should be boxed: otherwise it's not possible to use + * this class from Java since javac issues errors when loading the class (incompatible return types) + */ + private static boolean forceBoxedReturnType(@NotNull FunctionDescriptor descriptor) { + //noinspection ConstantConditions + if (!KotlinBuiltIns.getInstance().isPrimitiveType(descriptor.getReturnType())) return false; + + for (FunctionDescriptor overridden : descriptor.getOverriddenDescriptors()) { + //noinspection ConstantConditions + if (!KotlinBuiltIns.getInstance().isPrimitiveType(overridden.getOriginal().getReturnType())) return true; + } + + return false; + } + private static void writeVoidReturn(@NotNull BothSignatureWriter sw) { sw.writeReturnType(); sw.writeAsmType(Type.VOID_TYPE); diff --git a/compiler/testData/codegen/bytecodeText/noWrapperForMethodReturningPrimitive.kt b/compiler/testData/codegen/bytecodeText/noWrapperForMethodReturningPrimitive.kt new file mode 100644 index 00000000000..b808c069ad3 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/noWrapperForMethodReturningPrimitive.kt @@ -0,0 +1,10 @@ +class A { + fun foo(): Int = 42 +} + +fun bar() = A().foo() + +// Test that for a simple final method in a final class we don't generate a bridge returning wrapper type +// (we only generate the method returning a primitive int) + +// 0 foo\(\)Ljava/lang/Integer; diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ByteOverridesObject.java b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ByteOverridesObject.java new file mode 100644 index 00000000000..98a91cf5dc3 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ByteOverridesObject.java @@ -0,0 +1,18 @@ +class ExtendsB extends B { + void test() { + byte x = foo(); + Byte y = foo(); + Object z = foo(); + } +} + +class ExtendsC extends C { + void test() { + byte x = foo(); + Byte y = foo(); + Object z = foo(); + } + + @Override + public Byte foo() { return 42; } +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ByteOverridesObject.kt b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ByteOverridesObject.kt new file mode 100644 index 00000000000..56ed4bb0c24 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ByteOverridesObject.kt @@ -0,0 +1,9 @@ +trait A { + fun foo(): T +} + +open class B : A { + override fun foo(): Byte = 42 +} + +abstract class C : A diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallFinalNotInSubclass.java b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallFinalNotInSubclass.java new file mode 100644 index 00000000000..c564b63d36a --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallFinalNotInSubclass.java @@ -0,0 +1,13 @@ +class Test { + void test() { + A a = new B(); + int ax = a.foo(); + Integer ay = a.foo(); + Object az = a.foo(); + + B b = new B(); + int bx = b.foo(); + Integer by = b.foo(); + Object bz = b.foo(); + } +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallFinalNotInSubclass.kt b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallFinalNotInSubclass.kt new file mode 100644 index 00000000000..31d95a680a0 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallFinalNotInSubclass.kt @@ -0,0 +1,7 @@ +trait A { + fun foo(): T +} + +class B : A { + override final fun foo(): Int = 42 +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallNotInSubclass.java b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallNotInSubclass.java new file mode 100644 index 00000000000..c564b63d36a --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallNotInSubclass.java @@ -0,0 +1,13 @@ +class Test { + void test() { + A a = new B(); + int ax = a.foo(); + Integer ay = a.foo(); + Object az = a.foo(); + + B b = new B(); + int bx = b.foo(); + Integer by = b.foo(); + Object bz = b.foo(); + } +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallNotInSubclass.kt b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallNotInSubclass.kt new file mode 100644 index 00000000000..a85d58f3b96 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallNotInSubclass.kt @@ -0,0 +1,7 @@ +trait A { + fun foo(): T +} + +class B : A { + override fun foo(): Int = 42 +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CovariantReturnTypeOverride.java b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CovariantReturnTypeOverride.java new file mode 100644 index 00000000000..066c182e09d --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CovariantReturnTypeOverride.java @@ -0,0 +1,7 @@ +class Test extends B { + void test() { + int x = foo(); + Integer y = foo(); + Object z = foo(); + } +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CovariantReturnTypeOverride.kt b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CovariantReturnTypeOverride.kt new file mode 100644 index 00000000000..3933364861d --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CovariantReturnTypeOverride.kt @@ -0,0 +1,7 @@ +trait A { + fun foo(): Any +} + +open class B : A { + override fun foo(): Int = 42 +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/FinalOverride.java b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/FinalOverride.java new file mode 100644 index 00000000000..961ffef09ac --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/FinalOverride.java @@ -0,0 +1,13 @@ +class Test extends B { + void test() { + A a = new B(); + int ax = a.foo(); + Integer ay = a.foo(); + Object az = a.foo(); + + B b = new B(); + int bx = b.foo(); + Integer by = b.foo(); + Object bz = b.foo(); + } +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/FinalOverride.kt b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/FinalOverride.kt new file mode 100644 index 00000000000..cf2315839a3 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/FinalOverride.kt @@ -0,0 +1,7 @@ +trait A { + fun foo(): T +} + +open class B : A { + override final fun foo(): Int = 42 +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesComparable.java b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesComparable.java new file mode 100644 index 00000000000..3bcf0df7f73 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesComparable.java @@ -0,0 +1,18 @@ +class ExtendsB extends B { + void test() { + int x = foo(); + Integer y = foo(); + Object z = foo(); + } +} + +class ExtendsC extends C { + void test() { + int x = foo(); + Integer y = foo(); + Object z = foo(); + } + + @Override + public Integer foo() { return 42; } +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesComparable.kt b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesComparable.kt new file mode 100644 index 00000000000..db99282bf6b --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesComparable.kt @@ -0,0 +1,9 @@ +trait A> { + fun foo(): T +} + +open class B : A { + override fun foo(): Int = 42 +} + +abstract class C : A diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesNumber.java b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesNumber.java new file mode 100644 index 00000000000..3bcf0df7f73 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesNumber.java @@ -0,0 +1,18 @@ +class ExtendsB extends B { + void test() { + int x = foo(); + Integer y = foo(); + Object z = foo(); + } +} + +class ExtendsC extends C { + void test() { + int x = foo(); + Integer y = foo(); + Object z = foo(); + } + + @Override + public Integer foo() { return 42; } +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesNumber.kt b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesNumber.kt new file mode 100644 index 00000000000..2f3b1bfaf05 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesNumber.kt @@ -0,0 +1,9 @@ +trait A { + fun foo(): T +} + +open class B : A { + override fun foo(): Int = 42 +} + +abstract class C : A diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesObject.java b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesObject.java new file mode 100644 index 00000000000..3bcf0df7f73 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesObject.java @@ -0,0 +1,18 @@ +class ExtendsB extends B { + void test() { + int x = foo(); + Integer y = foo(); + Object z = foo(); + } +} + +class ExtendsC extends C { + void test() { + int x = foo(); + Integer y = foo(); + Object z = foo(); + } + + @Override + public Integer foo() { return 42; } +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesObject.kt b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesObject.kt new file mode 100644 index 00000000000..3a82da93e1d --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesObject.kt @@ -0,0 +1,9 @@ +trait A { + fun foo(): T +} + +open class B : A { + override fun foo(): Int = 42 +} + +abstract class C : A diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ManyClassesHierarchy.java b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ManyClassesHierarchy.java new file mode 100644 index 00000000000..f24e11fef2e --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ManyClassesHierarchy.java @@ -0,0 +1,7 @@ +class ExtendsD extends D { + void test() { + int x = foo(); + Integer y = foo(); + Object z = foo(); + } +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ManyClassesHierarchy.kt b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ManyClassesHierarchy.kt new file mode 100644 index 00000000000..a0bf49b5345 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ManyClassesHierarchy.kt @@ -0,0 +1,11 @@ +trait A { + fun foo(): T +} + +trait B : A + +abstract class C : B + +open class D : C() { + override fun foo(): Int = 42 +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/NullableIntOverridesObject.java b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/NullableIntOverridesObject.java new file mode 100644 index 00000000000..3bcf0df7f73 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/NullableIntOverridesObject.java @@ -0,0 +1,18 @@ +class ExtendsB extends B { + void test() { + int x = foo(); + Integer y = foo(); + Object z = foo(); + } +} + +class ExtendsC extends C { + void test() { + int x = foo(); + Integer y = foo(); + Object z = foo(); + } + + @Override + public Integer foo() { return 42; } +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/NullableIntOverridesObject.kt b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/NullableIntOverridesObject.kt new file mode 100644 index 00000000000..c1d8b4b5ba8 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/NullableIntOverridesObject.kt @@ -0,0 +1,9 @@ +trait A { + fun foo(): T +} + +open class B : A { + override fun foo(): Int? = 42 +} + +abstract class C : A diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/OverrideInJava.java b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/OverrideInJava.java new file mode 100644 index 00000000000..2a33e86196a --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/OverrideInJava.java @@ -0,0 +1,12 @@ +class ExtendsB extends B { + @Override + public Integer foo() { + return 239; + } + + void test() { + int x = foo(); + Integer y = foo(); + Object z = foo(); + } +} diff --git a/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/OverrideInJava.kt b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/OverrideInJava.kt new file mode 100644 index 00000000000..4bc87f2e664 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/OverrideInJava.kt @@ -0,0 +1,7 @@ +trait A { + fun foo(): T +} + +abstract class B : A { + override abstract fun foo(): Int +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/BytecodeTextTestGenerated.java index 7a2c5d92d4c..dd95fcdc21a 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/BytecodeTextTestGenerated.java @@ -92,6 +92,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest("compiler/testData/codegen/bytecodeText/noVolatileAnnotation.kt"); } + @TestMetadata("noWrapperForMethodReturningPrimitive.kt") + public void testNoWrapperForMethodReturningPrimitive() throws Exception { + doTest("compiler/testData/codegen/bytecodeText/noWrapperForMethodReturningPrimitive.kt"); + } + @TestMetadata("privateDefaultArgs.kt") public void testPrivateDefaultArgs() throws Exception { doTest("compiler/testData/codegen/bytecodeText/privateDefaultArgs.kt"); diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java index 2f1757ef9a0..6f7d0e7bafb 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java @@ -91,6 +91,7 @@ public class CompileJavaAgainstKotlinTestGenerated extends AbstractCompileJavaAg } @TestMetadata("compiler/testData/compileJavaAgainstKotlin/method") + @InnerTestClasses({Method.PrimitiveOverride.class}) public static class Method extends AbstractCompileJavaAgainstKotlinTest { public void testAllFilesPresentInMethod() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/compileJavaAgainstKotlin/method"), Pattern.compile("^(.+)\\.kt$"), true); @@ -186,6 +187,75 @@ public class CompileJavaAgainstKotlinTestGenerated extends AbstractCompileJavaAg doTest("compiler/testData/compileJavaAgainstKotlin/method/Void.kt"); } + @TestMetadata("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride") + public static class PrimitiveOverride extends AbstractCompileJavaAgainstKotlinTest { + public void testAllFilesPresentInPrimitiveOverride() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("ByteOverridesObject.kt") + public void testByteOverridesObject() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ByteOverridesObject.kt"); + } + + @TestMetadata("CallFinalNotInSubclass.kt") + public void testCallFinalNotInSubclass() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallFinalNotInSubclass.kt"); + } + + @TestMetadata("CallNotInSubclass.kt") + public void testCallNotInSubclass() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CallNotInSubclass.kt"); + } + + @TestMetadata("CovariantReturnTypeOverride.kt") + public void testCovariantReturnTypeOverride() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/CovariantReturnTypeOverride.kt"); + } + + @TestMetadata("FinalOverride.kt") + public void testFinalOverride() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/FinalOverride.kt"); + } + + @TestMetadata("IntOverridesComparable.kt") + public void testIntOverridesComparable() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesComparable.kt"); + } + + @TestMetadata("IntOverridesNumber.kt") + public void testIntOverridesNumber() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesNumber.kt"); + } + + @TestMetadata("IntOverridesObject.kt") + public void testIntOverridesObject() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/IntOverridesObject.kt"); + } + + @TestMetadata("ManyClassesHierarchy.kt") + public void testManyClassesHierarchy() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/ManyClassesHierarchy.kt"); + } + + @TestMetadata("NullableIntOverridesObject.kt") + public void testNullableIntOverridesObject() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/NullableIntOverridesObject.kt"); + } + + @TestMetadata("OverrideInJava.kt") + public void testOverrideInJava() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/primitiveOverride/OverrideInJava.kt"); + } + + } + + public static Test innerSuite() { + TestSuite suite = new TestSuite("Method"); + suite.addTestSuite(Method.class); + suite.addTestSuite(PrimitiveOverride.class); + return suite; + } } @TestMetadata("compiler/testData/compileJavaAgainstKotlin/property") @@ -238,7 +308,7 @@ public class CompileJavaAgainstKotlinTestGenerated extends AbstractCompileJavaAg TestSuite suite = new TestSuite("CompileJavaAgainstKotlinTestGenerated"); suite.addTestSuite(CompileJavaAgainstKotlinTestGenerated.class); suite.addTestSuite(Class.class); - suite.addTestSuite(Method.class); + suite.addTest(Method.innerSuite()); suite.addTestSuite(Property.class); suite.addTestSuite(StaticFields.class); return suite;