diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java index bf0acc7017a..e8146d6d5b8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.types.*; import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext; import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices; import org.jetbrains.kotlin.types.expressions.ValueParameterResolver; +import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage; import org.jetbrains.kotlin.util.Box; import org.jetbrains.kotlin.util.ReenteringLazyValueComputationException; import org.jetbrains.kotlin.util.slicedMap.WritableSlice; @@ -48,8 +49,7 @@ import java.util.*; import static org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER; import static org.jetbrains.kotlin.diagnostics.Errors.*; -import static org.jetbrains.kotlin.resolve.BindingContext.CONSTRUCTOR_RESOLVED_DELEGATION_CALL; -import static org.jetbrains.kotlin.resolve.BindingContext.DEFERRED_TYPE; +import static org.jetbrains.kotlin.resolve.BindingContext.*; import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE; public class BodyResolver { @@ -350,6 +350,10 @@ public class BodyResolver { primaryConstructorDelegationCall[0] = null; } } + // Recording type info for callee to use later in JetObjectLiteralExpression + trace.record(PROCESSED, call.getCalleeExpression(), true); + trace.record(EXPRESSION_TYPE_INFO, call.getCalleeExpression(), + TypeInfoFactoryPackage.noTypeInfo(results.getResultingCall().getDataFlowInfoForArguments().getResultInfo())); } else { recordSupertype(typeReference, trace.getBindingContext().get(BindingContext.TYPE, typeReference)); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 0a0b732381c..85887b6268d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -609,8 +609,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { @Override public JetTypeInfo visitObjectLiteralExpression(@NotNull final JetObjectLiteralExpression expression, final ExpressionTypingContext context) { final JetType[] result = new JetType[1]; - final TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace, - "trace to resolve object literal expression", expression); + TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace, + "trace to resolve object literal expression", expression); ObservableBindingTrace.RecordHandler handler = new ObservableBindingTrace.RecordHandler() { @Override @@ -625,11 +625,6 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } }); result[0] = defaultType; - // noinspection ConstantConditions - if (!context.trace.get(PROCESSED, expression)) { - temporaryTrace.recordType(expression, defaultType); - temporaryTrace.record(PROCESSED, expression); - } } } }; @@ -643,7 +638,25 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { components.additionalCheckerProvider, components.dynamicTypesSettings); temporaryTrace.commit(); - return TypeInfoFactoryPackage.createCheckedTypeInfo(result[0], context, expression); + DataFlowInfo resultFlowInfo = context.dataFlowInfo; + for (JetDelegationSpecifier specifier: expression.getObjectDeclaration().getDelegationSpecifiers()) { + if (specifier instanceof JetDelegatorToSuperCall) { + JetDelegatorToSuperCall delegator = (JetDelegatorToSuperCall)specifier; + JetTypeInfo delegatorTypeInfo = context.trace.get(EXPRESSION_TYPE_INFO, delegator.getCalleeExpression()); + if (delegatorTypeInfo != null) { + resultFlowInfo = resultFlowInfo.and(delegatorTypeInfo.getDataFlowInfo()); + } + } + } + // Breaks are not possible inside constructor arguments, so jumpPossible or jumpFlowInfo are not necessary here + JetTypeInfo resultTypeInfo = DataFlowUtils.checkType(TypeInfoFactoryPackage.createTypeInfo(result[0], resultFlowInfo), + expression, + context); + // We have to record it here, + // otherwise ExpressionTypingVisitorDispatcher records wrong information + context.trace.record(EXPRESSION_TYPE_INFO, expression, resultTypeInfo); + context.trace.record(PROCESSED, expression); + return resultTypeInfo; } @Nullable diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/assignment.kt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/assignment.kt new file mode 100644 index 00000000000..c28aed5c0b5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/assignment.kt @@ -0,0 +1,19 @@ +// See KT-6293: Smart cast doesn't work after object literal +abstract class Runnable { + abstract fun run() +} + +fun foo(): Int { + val c: Int? = null + if (c is Int) { + var k: Runnable + val d: Int = c + k = object: Runnable() { + override fun run() = Unit + } + // Unnecessary but not important smart cast + k.run() + return c + d + } + else return -1 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/assignment.txt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/assignment.txt new file mode 100644 index 00000000000..80ec48c3152 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/assignment.txt @@ -0,0 +1,11 @@ +package + +internal fun foo(): kotlin.Int + +internal abstract class Runnable { + public constructor Runnable() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal abstract fun run(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/base.kt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/base.kt new file mode 100644 index 00000000000..dcab62bb482 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/base.kt @@ -0,0 +1,19 @@ +// See KT-6293: Smart cast doesn't work after object literal +abstract class Runnable { + abstract fun run() +} + +fun foo(): Int { + val c: Int? = null + if (c is Int) { + val d: Int = c + // This object breaks data flow info propagation + val k = object: Runnable() { + override fun run() = Unit + } + k.run() + // Smart cast should work but error is reported + return c + d + } + else return -1 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/base.txt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/base.txt new file mode 100644 index 00000000000..80ec48c3152 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/base.txt @@ -0,0 +1,11 @@ +package + +internal fun foo(): kotlin.Int + +internal abstract class Runnable { + public constructor Runnable() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal abstract fun run(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/captured.kt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/captured.kt new file mode 100644 index 00000000000..d9756feaa31 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/captured.kt @@ -0,0 +1,22 @@ +abstract class Runnable { + abstract fun run() +} + +fun foo(): Int { + val c: Int? = null + var a: Int? + if (c is Int) { + a = 2 + val k = object: Runnable() { + init { + a = null + } + override fun run() = Unit + } + k.run() + val d: Int = c + // a is captured so smart cast is not possible + return d + a + } + else return -1 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/captured.txt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/captured.txt new file mode 100644 index 00000000000..80ec48c3152 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/captured.txt @@ -0,0 +1,11 @@ +package + +internal fun foo(): kotlin.Int + +internal abstract class Runnable { + public constructor Runnable() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal abstract fun run(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexcl.kt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexcl.kt new file mode 100644 index 00000000000..6f0635d16a3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexcl.kt @@ -0,0 +1,21 @@ +abstract class Runnable { + abstract fun run() +} + +fun foo(): Int { + val c: Int? = null + val a: Int? = 1 + if (c is Int) { + val k = object: Runnable() { + init { + a!!.toInt() + } + override fun run() = Unit + } + k.run() + val d: Int = c + // a is not null because of k constructor, but we do not know it + return a + d + } + else return -1 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexcl.txt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexcl.txt new file mode 100644 index 00000000000..80ec48c3152 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexcl.txt @@ -0,0 +1,11 @@ +package + +internal fun foo(): kotlin.Int + +internal abstract class Runnable { + public constructor Runnable() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal abstract fun run(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclArgument.kt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclArgument.kt new file mode 100644 index 00000000000..263175ea78b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclArgument.kt @@ -0,0 +1,17 @@ +abstract class Runnable(val arg: Int) { + abstract fun run(): Int +} + +fun foo(): Int { + val c: Int? = null + val a: Int? = 1 + if (c is Int) { + val k = object: Runnable(a!!) { + override fun run() = arg + } + k.run() + val d: Int = c + return a + d + } + else return -1 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclArgument.txt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclArgument.txt new file mode 100644 index 00000000000..64ecb94a8c2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclArgument.txt @@ -0,0 +1,12 @@ +package + +internal fun foo(): kotlin.Int + +internal abstract class Runnable { + public constructor Runnable(/*0*/ arg: kotlin.Int) + internal final val arg: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal abstract fun run(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclTwoArgument.kt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclTwoArgument.kt new file mode 100644 index 00000000000..5a997558888 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclTwoArgument.kt @@ -0,0 +1,21 @@ +abstract class Runnable(val arg: Int) { + abstract fun run(): Int +} + +interface Wrapper { + fun run(): Int +} + +fun foo(): Int { + val c: Int? = null + val a: Int? = 1 + if (c is Int) { + val k = object: Wrapper, Runnable(a!!) { + override fun run() = arg + } + k.run() + val d: Int = c + return a + d + } + else return -1 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclTwoArgument.txt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclTwoArgument.txt new file mode 100644 index 00000000000..d8f73ee0b41 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclTwoArgument.txt @@ -0,0 +1,19 @@ +package + +internal fun foo(): kotlin.Int + +internal abstract class Runnable { + public constructor Runnable(/*0*/ arg: kotlin.Int) + internal final val arg: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal abstract fun run(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +internal interface Wrapper { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal abstract fun run(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/kt7110.kt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/kt7110.kt new file mode 100644 index 00000000000..9e11061b29c --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/kt7110.kt @@ -0,0 +1,11 @@ +// Anonymous object's initialization does not affect smart casts + +abstract class A(val s: String) { + fun bar(): String = s +} + +fun foo(o: String?): Int { + val a = object : A(o!!){} + a.bar() + return o.length() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/kt7110.txt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/kt7110.txt new file mode 100644 index 00000000000..13c3d00b287 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/kt7110.txt @@ -0,0 +1,12 @@ +package + +internal fun foo(/*0*/ o: kotlin.String?): kotlin.Int + +internal abstract class A { + public constructor A(/*0*/ s: kotlin.String) + internal final val s: kotlin.String + internal final fun bar(): kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/receiver.kt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/receiver.kt new file mode 100644 index 00000000000..a6d833c5577 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/receiver.kt @@ -0,0 +1,16 @@ +// See KT-6293: Smart cast doesn't work after object literal +abstract class Runnable { + abstract fun run() +} + +fun foo(): Int { + val c: Int? = null + if (c is Int) { + val d: Int = c + object: Runnable() { + override fun run() = Unit + }.run() + return c + d + } + else return -1 +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/receiver.txt b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/receiver.txt new file mode 100644 index 00000000000..80ec48c3152 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/objectLiterals/receiver.txt @@ -0,0 +1,11 @@ +package + +internal fun foo(): kotlin.Int + +internal abstract class Runnable { + public constructor Runnable() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal abstract fun run(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index e25da311ef3..696624602b7 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -11655,6 +11655,63 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { } } + @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ObjectLiterals extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInObjectLiterals() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/smartCasts/objectLiterals"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("assignment.kt") + public void testAssignment() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/assignment.kt"); + doTest(fileName); + } + + @TestMetadata("base.kt") + public void testBase() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/base.kt"); + doTest(fileName); + } + + @TestMetadata("captured.kt") + public void testCaptured() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/captured.kt"); + doTest(fileName); + } + + @TestMetadata("exclexcl.kt") + public void testExclexcl() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexcl.kt"); + doTest(fileName); + } + + @TestMetadata("exclexclArgument.kt") + public void testExclexclArgument() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclArgument.kt"); + doTest(fileName); + } + + @TestMetadata("exclexclTwoArgument.kt") + public void testExclexclTwoArgument() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/exclexclTwoArgument.kt"); + doTest(fileName); + } + + @TestMetadata("kt7110.kt") + public void testKt7110() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/kt7110.kt"); + doTest(fileName); + } + + @TestMetadata("receiver.kt") + public void testReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals/receiver.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/publicVals") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)