diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java index ad89c1d21cc..d445ef887bc 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java @@ -422,14 +422,6 @@ public class PropertyCodegen { } modifiers |= getVisibilityForBackingField(propertyDescriptor, isDelegate); - // If val is initialized in EXACTLY_ONCE closure, other class from the same package initializes it - // so, its visibility should be package private and not final - if (!propertyDescriptor.isVar() && - bindingContext.get(BindingContext.FIELD_CAPTURED_IN_EXACLY_ONCE_CLOSURE, propertyDescriptor) != null - ) { - modifiers &= ~(ACC_PRIVATE | ACC_FINAL); - } - if (AsmUtil.isPropertyWithBackingFieldCopyInOuterClass(propertyDescriptor)) { ImplementationBodyCodegen parentBodyCodegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen(); parentBodyCodegen.addCompanionObjectPropertyToCopy(propertyDescriptor, defaultValue); diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index c75a6f83a5a..8fc288a5f66 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -3816,6 +3816,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldAsClassDelegate.kt"); } + @TestMetadata("fieldInitialization.kt") + public void testFieldInitialization() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.kt"); + } + @TestMetadata("infiniteLoops.kt") public void testInfiniteLoops() throws Exception { runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.kt"); diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 019dc526324..f2d9a8ef7ce 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -5097,11 +5097,6 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/contracts/exception.kt"); } - @TestMetadata("field.kt") - public void testField() throws Exception { - runTest("compiler/testData/codegen/box/contracts/field.kt"); - } - @TestMetadata("forLoop.kt") public void testForLoop() throws Exception { runTest("compiler/testData/codegen/box/contracts/forLoop.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index 7f4905ac2ac..a1a012372e9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -179,7 +179,6 @@ public interface BindingContext { WritableSlice USED_AS_RESULT_OF_LAMBDA = Slices.createSimpleSetSlice(); WritableSlice CAPTURED_IN_CLOSURE = new BasicWritableSlice<>(DO_NOTHING); - WritableSlice FIELD_CAPTURED_IN_EXACLY_ONCE_CLOSURE = new BasicWritableSlice<>(DO_NOTHING); WritableSlice PRELIMINARY_VISITOR = new BasicWritableSlice<>(DO_NOTHING); WritableSlice, Boolean> DEFERRED_TYPE = Slices.createCollectiveSetSlice(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CapturingInClosureChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CapturingInClosureChecker.kt index c9346c4a55c..07076e88012 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CapturingInClosureChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/CapturingInClosureChecker.kt @@ -13,10 +13,10 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.kotlin.diagnostics.Errors.CAPTURED_VAL_INITIALIZATION import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext.CAPTURED_IN_CLOSURE -import org.jetbrains.kotlin.resolve.BindingContext.FIELD_CAPTURED_IN_EXACLY_ONCE_CLOSURE import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -35,6 +35,7 @@ class CapturingInClosureChecker : CallChecker { val variableDescriptor = variableResolvedCall.resultingDescriptor as? VariableDescriptor if (variableDescriptor != null) { checkCapturingInClosure(variableDescriptor, context.trace, context.scope) + checkFieldInExactlyOnceLambdaInitialization(variableDescriptor, context.trace, context.scope.ownerDescriptor, reportOn) } } @@ -47,12 +48,23 @@ class CapturingInClosureChecker : CallChecker { return } } - // Check whether a field is captured in EXACTLY_ONCE contract - if (variable !is PropertyDescriptor || scopeContainer !is AnonymousFunctionDescriptor) return + } + + private fun checkFieldInExactlyOnceLambdaInitialization( + variable: VariableDescriptor, + trace: BindingTrace, + scopeContainer: DeclarationDescriptor, + reportOn: PsiElement + ) { + if (variable !is PropertyDescriptor || scopeContainer !is AnonymousFunctionDescriptor || variable.isVar) return val scopeDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(scopeContainer) as? KtFunction ?: return if (scopeContainer.containingDeclaration !is ConstructorDescriptor) return - if (isExactlyOnceContract(trace.bindingContext, scopeDeclaration)) { - trace.record(FIELD_CAPTURED_IN_EXACLY_ONCE_CLOSURE, variable) + if (!isExactlyOnceContract(trace.bindingContext, scopeDeclaration)) return + if (trace.bindingContext[CAPTURED_IN_CLOSURE, variable] == CaptureKind.NOT_INLINE) return + val (callee, param) = getCalleeDescriptorAndParameter(trace.bindingContext, scopeDeclaration) ?: return + if (callee !is FunctionDescriptor) return + if (!callee.isInline || (param.isCrossinline || !InlineUtil.isInlineParameter(param))) { + trace.report(CAPTURED_VAL_INITIALIZATION.on(reportOn as KtExpression, variable)) } } diff --git a/compiler/testData/codegen/box/contracts/field.kt b/compiler/testData/codegen/box/contracts/field.kt deleted file mode 100644 index 23f17b21876..00000000000 --- a/compiler/testData/codegen/box/contracts/field.kt +++ /dev/null @@ -1,26 +0,0 @@ -// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts -// IGNORE_BACKEND: NATIVE -// WITH_RUNTIME -// IGNORE_LIGHT_ANALYSIS - -import kotlin.contracts.* - -fun runOnce(action: () -> Unit) { - contract { - callsInPlace(action, InvocationKind.EXACTLY_ONCE) - } - action() -} - -class Foo { - val res: String - init { - runOnce { - res = "OK" - } - } -} - -fun box(): String { - return Foo().res -} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.fir.kt new file mode 100644 index 00000000000..7790af334c6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.fir.kt @@ -0,0 +1,60 @@ +// !LANGUAGE: +AllowContractsForCustomFunctions +UseCallsInPlaceEffect +// !USE_EXPERIMENTAL: kotlin.internal.ContractsDsl + +import kotlin.contracts.* + +@kotlin.contracts.ExperimentalContracts +inline fun inlineMe(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() +} + +@kotlin.contracts.ExperimentalContracts +inline fun crossinlineMe(crossinline block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() +} + +@Suppress("NOTHING_TO_INLINE") +@kotlin.contracts.ExperimentalContracts +inline fun noinlineMe(noinline block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() +} + +@kotlin.contracts.ExperimentalContracts +fun notinline(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() +} + +@kotlin.contracts.ExperimentalContracts +class Test { + val a: String + val b: String + val c: String + val d: String + + init { + inlineMe { + a = "allowed" + } + crossinlineMe { + b = "not allowed" + } + noinlineMe { + c = "not allowed" + } + notinline { + d = "not allowed" + } + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.kt new file mode 100644 index 00000000000..f6218f49d64 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.kt @@ -0,0 +1,60 @@ +// !LANGUAGE: +AllowContractsForCustomFunctions +UseCallsInPlaceEffect +// !USE_EXPERIMENTAL: kotlin.internal.ContractsDsl + +import kotlin.contracts.* + +@kotlin.contracts.ExperimentalContracts +inline fun inlineMe(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() +} + +@kotlin.contracts.ExperimentalContracts +inline fun crossinlineMe(crossinline block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() +} + +@Suppress("NOTHING_TO_INLINE") +@kotlin.contracts.ExperimentalContracts +inline fun noinlineMe(noinline block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() +} + +@kotlin.contracts.ExperimentalContracts +fun notinline(block: () -> Unit) { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + block() +} + +@kotlin.contracts.ExperimentalContracts +class Test { + val a: String + val b: String + val c: String + val d: String + + init { + inlineMe { + a = "allowed" + } + crossinlineMe { + b = "not allowed" + } + noinlineMe { + c = "not allowed" + } + notinline { + d = "not allowed" + } + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.txt new file mode 100644 index 00000000000..7f702e58029 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.txt @@ -0,0 +1,24 @@ +package + +@kotlin.contracts.ExperimentalContracts public inline fun crossinlineMe(/*0*/ crossinline block: () -> kotlin.Unit): kotlin.Unit + CallsInPlace(block, EXACTLY_ONCE) + +@kotlin.contracts.ExperimentalContracts public inline fun inlineMe(/*0*/ block: () -> kotlin.Unit): kotlin.Unit + CallsInPlace(block, EXACTLY_ONCE) + +@kotlin.Suppress(names = {"NOTHING_TO_INLINE"}) @kotlin.contracts.ExperimentalContracts public inline fun noinlineMe(/*0*/ noinline block: () -> kotlin.Unit): kotlin.Unit + CallsInPlace(block, EXACTLY_ONCE) + +@kotlin.contracts.ExperimentalContracts public fun notinline(/*0*/ block: () -> kotlin.Unit): kotlin.Unit + CallsInPlace(block, EXACTLY_ONCE) + +@kotlin.contracts.ExperimentalContracts public final class Test { + public constructor Test() + public final val a: kotlin.String + public final val b: kotlin.String + public final val c: kotlin.String + public final val d: 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/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteInitialization.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteInitialization.kt index d63d0910727..74cf4cbc28d 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteInitialization.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteInitialization.kt @@ -86,7 +86,7 @@ class DefiniteInitializationInInitSection { val y: Int init { - myRun { x = 42 } + myRun { x = 42 } unknownRun { y = 239 } } } \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index ce03af3b83a..4f85187e8c5 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3823,6 +3823,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldAsClassDelegate.kt"); } + @TestMetadata("fieldInitialization.kt") + public void testFieldInitialization() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.kt"); + } + @TestMetadata("infiniteLoops.kt") public void testInfiniteLoops() throws Exception { runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 6520f9b6703..95c1abbc104 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -3818,6 +3818,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldAsClassDelegate.kt"); } + @TestMetadata("fieldInitialization.kt") + public void testFieldInitialization() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/fieldInitialization.kt"); + } + @TestMetadata("infiniteLoops.kt") public void testInfiniteLoops() throws Exception { runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 002854c588c..2affa12a9e2 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -5127,11 +5127,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/contracts/exception.kt"); } - @TestMetadata("field.kt") - public void testField() throws Exception { - runTest("compiler/testData/codegen/box/contracts/field.kt"); - } - @TestMetadata("forLoop.kt") public void testForLoop() throws Exception { runTest("compiler/testData/codegen/box/contracts/forLoop.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index eec8e6adc73..97ebdaca23d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -5127,11 +5127,6 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/contracts/exception.kt"); } - @TestMetadata("field.kt") - public void testField() throws Exception { - runTest("compiler/testData/codegen/box/contracts/field.kt"); - } - @TestMetadata("forLoop.kt") public void testForLoop() throws Exception { runTest("compiler/testData/codegen/box/contracts/forLoop.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 68071003cc2..d1124ef1b20 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -5097,11 +5097,6 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/contracts/exception.kt"); } - @TestMetadata("field.kt") - public void testField() throws Exception { - runTest("compiler/testData/codegen/box/contracts/field.kt"); - } - @TestMetadata("forLoop.kt") public void testForLoop() throws Exception { runTest("compiler/testData/codegen/box/contracts/forLoop.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java index 05123a80e64..add5901978c 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java @@ -4127,11 +4127,6 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes runTest("compiler/testData/codegen/box/contracts/exception.kt"); } - @TestMetadata("field.kt") - public void testField() throws Exception { - runTest("compiler/testData/codegen/box/contracts/field.kt"); - } - @TestMetadata("forLoop.kt") public void testForLoop() throws Exception { runTest("compiler/testData/codegen/box/contracts/forLoop.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index f01d537d549..64923641de8 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -4137,11 +4137,6 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/contracts/exception.kt"); } - @TestMetadata("field.kt") - public void testField() throws Exception { - runTest("compiler/testData/codegen/box/contracts/field.kt"); - } - @TestMetadata("forLoop.kt") public void testForLoop() throws Exception { runTest("compiler/testData/codegen/box/contracts/forLoop.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 4462357ab25..bab7fab1f34 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -4137,11 +4137,6 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/contracts/exception.kt"); } - @TestMetadata("field.kt") - public void testField() throws Exception { - runTest("compiler/testData/codegen/box/contracts/field.kt"); - } - @TestMetadata("forLoop.kt") public void testForLoop() throws Exception { runTest("compiler/testData/codegen/box/contracts/forLoop.kt");