From 4d5a4ccd6b9c89b81a7747ea57b4eb555e26041b Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 12 May 2022 14:03:58 +0300 Subject: [PATCH] [FE 1.0] Don't fail on calls of functions with contracts from object ^KT-51704 Fixed --- ...CompilerTestFE10TestdataTestGenerated.java | 6 +++ ...irOldFrontendDiagnosticsTestGenerated.java | 6 +++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 +++ .../model/functors/SubstitutingFunctor.kt | 43 ++++++++++++++++--- .../smartcasts/functionInCompanion.kt | 38 ++++++++++++++++ .../smartcasts/functionInCompanion.txt | 31 +++++++++++++ .../test/runners/DiagnosticTestGenerated.java | 6 +++ 7 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.txt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 43dc17b02f5..12db2a6840d 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -35747,6 +35747,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.kt"); } + @Test + @TestMetadata("functionInCompanion.kt") + public void testFunctionInCompanion() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.kt"); + } + @Test @TestMetadata("intersectingInfo.kt") public void testIntersectingInfo() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 2eabb05d90c..f0282405cb7 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -35747,6 +35747,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.kt"); } + @Test + @TestMetadata("functionInCompanion.kt") + public void testFunctionInCompanion() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.kt"); + } + @Test @TestMetadata("intersectingInfo.kt") public void testIntersectingInfo() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 4eaa187b6c0..67f4bfa4671 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -35747,6 +35747,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.kt"); } + @Test + @TestMetadata("functionInCompanion.kt") + public void testFunctionInCompanion() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.kt"); + } + @Test @TestMetadata("intersectingInfo.kt") public void testIntersectingInfo() throws Exception { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/SubstitutingFunctor.kt b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/SubstitutingFunctor.kt index 5303efa94d2..efe847827da 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/SubstitutingFunctor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/contracts/model/functors/SubstitutingFunctor.kt @@ -20,8 +20,11 @@ import org.jetbrains.kotlin.contracts.model.* import org.jetbrains.kotlin.contracts.model.structure.* import org.jetbrains.kotlin.contracts.model.visitors.Reducer import org.jetbrains.kotlin.contracts.model.visitors.Substitutor +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueDescriptor +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver import org.jetbrains.kotlin.utils.addIfNotNull class SubstitutingFunctor( @@ -31,13 +34,7 @@ class SubstitutingFunctor( override fun doInvocation(arguments: List, typeSubstitution: ESTypeSubstitution, reducer: Reducer): List { if (basicEffects.isEmpty()) return emptyList() - val receiver = - listOfNotNull(ownerFunction.dispatchReceiverParameter?.toESVariable(), ownerFunction.extensionReceiverParameter?.toESVariable()) - val parameters = receiver + ownerFunction.valueParameters.map { it.toESVariable() } - - assert(parameters.size == arguments.size) { - "Arguments and parameters size mismatch: arguments.size = ${arguments.size}, parameters.size = ${parameters.size}" - } + val parameters = computeParameters(arguments) val substitutions = parameters.zip(arguments).toMap() val substitutor = Substitutor(substitutions, typeSubstitution, reducer) @@ -61,6 +58,38 @@ class SubstitutingFunctor( return substitutedClauses } + private fun computeParameters(arguments: List): List { + val dispatchReceiver = ownerFunction.dispatchReceiverParameter?.toESVariable() + val extensionReceiver = ownerFunction.extensionReceiverParameter?.toESVariable() + val receivers = listOfNotNull(dispatchReceiver, extensionReceiver) + val valueParameters = ownerFunction.valueParameters.map { it.toESVariable() } + val parameters = receivers + valueParameters + + if (parameters.size == arguments.size) { + return parameters + } + + fun fail(): Nothing { + error("Arguments and parameters size mismatch: arguments.size = ${arguments.size}, parameters.size = ${parameters.size}") + } + + val dispatchParameterDescriptor = dispatchReceiver?.descriptor ?: fail() + if (dispatchParameterDescriptor is ReceiverParameterDescriptor) { + val classDescriptor = (dispatchParameterDescriptor.value as? ImplicitClassReceiver)?.classDescriptor ?: fail() + if (classDescriptor.kind == ClassKind.OBJECT) { + val parametersWithoutDispatch = buildList { + extensionReceiver?.let { add(it) } + addAll(valueParameters) + } + if (parametersWithoutDispatch.size != arguments.size) { + fail() + } + return parametersWithoutDispatch + } + } + fail() + } + private fun combine(effect: SimpleEffect, substitutedCondition: ESEffect): ESEffect? { if (substitutedCondition !is ConditionalEffect) return null diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.kt new file mode 100644 index 00000000000..feffac47e2d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.kt @@ -0,0 +1,38 @@ +// FIR_IDENTICAL +// ISSUE: KT-51704 + +import Foo.Companion.checkSomethingCompanion +import Bar.checkSomethingObject +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +class Foo { + companion object { + @OptIn(ExperimentalContracts::class) + fun checkSomethingCompanion(condition: Boolean, message: String) { + contract { + returns() implies (condition) + } + if (!condition) + throw Exception(message) + } + } +} + +object Bar { + @OptIn(ExperimentalContracts::class) + fun checkSomethingObject(condition: Boolean, message: String) { + contract { + returns() implies (condition) + } + if (!condition) + throw Exception(message) + } +} + +fun usage() { + Foo.checkSomethingCompanion(1 == 2, "wat") // ok + checkSomethingCompanion(1 == 2, "wat") // compiler crash + Bar.checkSomethingObject(1 == 2, "wat") // ok + checkSomethingObject(1 == 2, "wat") // compiler crash +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.txt b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.txt new file mode 100644 index 00000000000..94604243b06 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.txt @@ -0,0 +1,31 @@ +package + +public fun usage(): kotlin.Unit + +public object Bar { + private constructor Bar() + @kotlin.OptIn(markerClass = {kotlin.contracts.ExperimentalContracts::class}) public final fun checkSomethingObject(/*0*/ condition: kotlin.Boolean, /*1*/ message: kotlin.String): kotlin.Unit + Returns(WILDCARD) -> condition + + 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 +} + +public final class Foo { + public constructor Foo() + 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 + + public companion object Companion { + private constructor Companion() + @kotlin.OptIn(markerClass = {kotlin.contracts.ExperimentalContracts::class}) public final fun checkSomethingCompanion(/*0*/ condition: kotlin.Boolean, /*1*/ message: kotlin.String): kotlin.Unit + Returns(WILDCARD) -> condition + + 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/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index f4776eafbc5..8a3b50763fb 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -35837,6 +35837,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/externalArguments.kt"); } + @Test + @TestMetadata("functionInCompanion.kt") + public void testFunctionInCompanion() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/functionInCompanion.kt"); + } + @Test @TestMetadata("intersectingInfo.kt") public void testIntersectingInfo() throws Exception {