diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index f7cbfc5acac..92c65fee23d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -359,7 +359,7 @@ public class ExpressionCodegen extends KtVisitor impleme @Override public StackValue visitSuperExpression(@NotNull KtSuperExpression expression, StackValue data) { - return StackValue.thisOrOuter(this, getSuperCallLabelTarget(context, expression), true, true); + return StackValue.thisOrOuter(this, getSuperCallLabelTarget(context, expression), true, false); } @NotNull @@ -2572,7 +2572,8 @@ public class ExpressionCodegen extends KtVisitor impleme } } else { - return StackValue.thisOrOuter(this, receiverDescriptor, isSuper, isEnumEntry(receiverDescriptor)); + return StackValue.thisOrOuter(this, receiverDescriptor, isSuper, + receiverValue instanceof CastClassReceiver || isEnumEntry(receiverDescriptor)); } } else if (receiverValue instanceof ScriptReceiver) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index c11ddcbb9b4..e004cdd58e8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -460,11 +460,11 @@ public abstract class StackValue { @NotNull ExpressionCodegen codegen, @NotNull ClassDescriptor descriptor, boolean isSuper, - boolean isExplicit + boolean castReceiver ) { - // Coerce explicit 'this' for the case when it is smart cast. + // Coerce 'this' for the case when it is smart cast. // Do not coerce for other cases due to the 'protected' access issues (JVMS 7, 4.9.2 Structural Constraints). - boolean coerceType = descriptor.getKind() == ClassKind.INTERFACE || (isExplicit && !isSuper); + boolean coerceType = descriptor.getKind() == ClassKind.INTERFACE || (castReceiver && !isSuper); return new ThisOuter(codegen, descriptor, isSuper, coerceType); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TaskPrioritizer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TaskPrioritizer.kt index b128040e1d8..334d58962d8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TaskPrioritizer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tasks/TaskPrioritizer.kt @@ -37,6 +37,8 @@ import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject import org.jetbrains.kotlin.resolve.descriptorUtil.hasLowPriorityInOverloadResolution import org.jetbrains.kotlin.resolve.scopes.ImportingScope import org.jetbrains.kotlin.resolve.scopes.LexicalScope +import org.jetbrains.kotlin.resolve.scopes.receivers.CastClassReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ClassReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.QualifierReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER @@ -234,9 +236,16 @@ public class TaskPrioritizer( } val filteredMembers = if (filter == null) membersForThisVariant else membersForThisVariant.filter(filter) + val dispatchReceiver = + if (explicitReceiver.value is ClassReceiver && type != explicitReceiver.value.type) { + CastClassReceiver(explicitReceiver.value.declarationDescriptor, type) + } + else { + explicitReceiver.value + } convertWithReceivers( filteredMembers, - explicitReceiver.value, + dispatchReceiver, NO_RECEIVER, members, createKind(DISPATCH_RECEIVER, isExplicit), diff --git a/compiler/testData/codegen/box/smartCasts/falseSmartCast.kt b/compiler/testData/codegen/box/smartCasts/falseSmartCast.kt new file mode 100644 index 00000000000..cfdcf7e1e7c --- /dev/null +++ b/compiler/testData/codegen/box/smartCasts/falseSmartCast.kt @@ -0,0 +1,17 @@ +open class SuperFoo { + public fun bar(): String { + if (this is Foo) { + superFoo() // Smart cast + return baz() // Cannot be cast + } + return baz() + } + + public fun baz() = "OK" +} + +class Foo : SuperFoo() { + public fun superFoo() {} +} + +fun box(): String = Foo().bar() \ No newline at end of file diff --git a/compiler/testData/codegen/box/smartCasts/implicitExtensionReceiver.kt b/compiler/testData/codegen/box/smartCasts/implicitExtensionReceiver.kt new file mode 100644 index 00000000000..f315a202bdb --- /dev/null +++ b/compiler/testData/codegen/box/smartCasts/implicitExtensionReceiver.kt @@ -0,0 +1,7 @@ +class A { + fun foo() = "OK" +} + +fun A?.bar() = if (this != null) foo() else "FAIL" + +fun box() = A().bar() diff --git a/compiler/testData/codegen/box/smartCasts/implicitMemberReceiver.kt b/compiler/testData/codegen/box/smartCasts/implicitMemberReceiver.kt new file mode 100644 index 00000000000..f38fa9fd9b5 --- /dev/null +++ b/compiler/testData/codegen/box/smartCasts/implicitMemberReceiver.kt @@ -0,0 +1,20 @@ +open class A { + open val a = "OK" +} + +class B : A() { + override val a = "FAIL" + fun foo() = "CRUSH" +} + +class C { + fun A?.complex(): String { + if (this is B) return foo() + else if (this != null) return a + else return "???" + } + + fun bar() = A().complex() +} + +fun box() = C().bar() diff --git a/compiler/testData/codegen/box/smartCasts/implicitReceiver.kt b/compiler/testData/codegen/box/smartCasts/implicitReceiver.kt new file mode 100644 index 00000000000..911f53248c5 --- /dev/null +++ b/compiler/testData/codegen/box/smartCasts/implicitReceiver.kt @@ -0,0 +1,13 @@ +open class A { + class B : A() { + val a = "FAIL" + } + + fun foo(): String { + if (this is B) return a + return "OK" + } +} + + +fun box(): String = A().foo() diff --git a/compiler/testData/codegen/box/smartCasts/implicitReceiverInWhen.kt b/compiler/testData/codegen/box/smartCasts/implicitReceiverInWhen.kt new file mode 100644 index 00000000000..ce31ad2d9f3 --- /dev/null +++ b/compiler/testData/codegen/box/smartCasts/implicitReceiverInWhen.kt @@ -0,0 +1,11 @@ +open class A { + fun f(): String = + when (this) { + is B -> x + else -> "FAIL" + } +} + +class B(val x: String) : A() + +fun box() = B("OK").f() diff --git a/compiler/testData/codegen/box/smartCasts/implicitToGrandSon.kt b/compiler/testData/codegen/box/smartCasts/implicitToGrandSon.kt new file mode 100644 index 00000000000..588cc9ff24d --- /dev/null +++ b/compiler/testData/codegen/box/smartCasts/implicitToGrandSon.kt @@ -0,0 +1,13 @@ +open class A { + open fun foo() = "FAIL" + + fun bar() = if (this is C) foo() else foo() +} + +open class B : A() + +open class C : B() { + override fun foo() = "OK" +} + +fun box() = C().bar() diff --git a/compiler/testData/codegen/bytecodeText/falseSmartCast.kt b/compiler/testData/codegen/bytecodeText/falseSmartCast.kt new file mode 100644 index 00000000000..dd6368542b7 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/falseSmartCast.kt @@ -0,0 +1,15 @@ +open class SuperFoo { + public fun bar() { + if (this is Foo) { + baz() + } + } + + public fun baz() {} +} + +class Foo : SuperFoo() + +// 0 INVOKEVIRTUAL SuperFoo.baz +// 1 CHECKCAST Foo +// 1 INVOKEVIRTUAL Foo.baz \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index df1a323bc25..c352c715ad7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -107,6 +107,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } + @TestMetadata("falseSmartCast.kt") + public void testFalseSmartCast() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/falseSmartCast.kt"); + doTest(fileName); + } + @TestMetadata("inlineFromOtherModule.kt") public void testInlineFromOtherModule() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineFromOtherModule.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index 982367afb32..85aee211f69 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -7274,6 +7274,51 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/smartCasts") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SmartCasts extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInSmartCasts() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/smartCasts"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("falseSmartCast.kt") + public void testFalseSmartCast() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/smartCasts/falseSmartCast.kt"); + doTest(fileName); + } + + @TestMetadata("implicitExtensionReceiver.kt") + public void testImplicitExtensionReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/smartCasts/implicitExtensionReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("implicitMemberReceiver.kt") + public void testImplicitMemberReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/smartCasts/implicitMemberReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("implicitReceiver.kt") + public void testImplicitReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/smartCasts/implicitReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("implicitReceiverInWhen.kt") + public void testImplicitReceiverInWhen() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/smartCasts/implicitReceiverInWhen.kt"); + doTest(fileName); + } + + @TestMetadata("implicitToGrandSon.kt") + public void testImplicitToGrandSon() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/smartCasts/implicitToGrandSon.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/staticFields") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/CastClassReceiver.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/CastClassReceiver.kt new file mode 100644 index 00000000000..6dc4b5a60d4 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/CastClassReceiver.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2015 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.kotlin.resolve.scopes.receivers + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.types.KotlinType + +class CastClassReceiver(originalDescriptor: ClassDescriptor, val targetType: KotlinType) : ClassReceiver(originalDescriptor) diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/ClassReceiver.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/ClassReceiver.java deleted file mode 100644 index c4506bb5a04..00000000000 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/ClassReceiver.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2010-2015 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.kotlin.resolve.scopes.receivers; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.descriptors.ClassDescriptor; -import org.jetbrains.kotlin.types.KotlinType; - -public class ClassReceiver implements ThisReceiver { - - private final ClassDescriptor classDescriptor; - - public ClassReceiver(@NotNull ClassDescriptor classDescriptor) { - this.classDescriptor = classDescriptor; - } - - @Override - public boolean exists() { - return true; - } - - @NotNull - @Override - public KotlinType getType() { - return classDescriptor.getDefaultType(); - } - - @NotNull - @Override - public ClassDescriptor getDeclarationDescriptor() { - return classDescriptor; - } - - @Override - public String toString() { - return "Class{" + getType() + "}"; - } -} diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/ClassReceiver.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/ClassReceiver.kt new file mode 100644 index 00000000000..add5b985940 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/ClassReceiver.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2015 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.kotlin.resolve.scopes.receivers + +import org.jetbrains.kotlin.descriptors.ClassDescriptor + +open class ClassReceiver(private val classDescriptor: ClassDescriptor) : ThisReceiver { + + override fun exists() = true + + override fun getType() = classDescriptor.defaultType + + override fun getDeclarationDescriptor() = classDescriptor + + override fun equals(other: Any?) = classDescriptor == (other as? ClassReceiver)?.classDescriptor + + override fun hashCode() = classDescriptor.hashCode() + + override fun toString() = "Class{$type}" +}