提交 8cc5f2ab 编写于 作者: I Ilmir Usmanov

Forbid val field initialization inside EXACLTY_ONCE lambda

unless the lambda is inline. This way, final field will remain final.
上级 6d6a2280
......@@ -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);
......
......@@ -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");
......@@ -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");
......@@ -179,7 +179,6 @@ public interface BindingContext {
WritableSlice<KtElement, Boolean> USED_AS_RESULT_OF_LAMBDA = Slices.createSimpleSetSlice();
WritableSlice<VariableDescriptor, CaptureKind> CAPTURED_IN_CLOSURE = new BasicWritableSlice<>(DO_NOTHING);
WritableSlice<PropertyDescriptor, Boolean> FIELD_CAPTURED_IN_EXACLY_ONCE_CLOSURE = new BasicWritableSlice<>(DO_NOTHING);
WritableSlice<KtDeclaration, PreliminaryDeclarationVisitor> PRELIMINARY_VISITOR = new BasicWritableSlice<>(DO_NOTHING);
WritableSlice<Box<DeferredType>, Boolean> DEFERRED_TYPE = Slices.createCollectiveSetSlice();
......
......@@ -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))
}
}
......
// !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
}
// !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"
}
}
}
// !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 {
<!CAPTURED_VAL_INITIALIZATION!>b<!> = "not allowed"
}
noinlineMe {
<!CAPTURED_VAL_INITIALIZATION!>c<!> = "not allowed"
}
notinline {
<!CAPTURED_VAL_INITIALIZATION!>d<!> = "not allowed"
}
}
}
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
}
......@@ -86,7 +86,7 @@ class DefiniteInitializationInInitSection {
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val y: Int<!>
init {
myRun { x = 42 }
myRun { <!CAPTURED_VAL_INITIALIZATION!>x<!> = 42 }
unknownRun { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>y<!> = 239 }
}
}
\ No newline at end of file
......@@ -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");
......@@ -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");
......@@ -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");
......@@ -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");
......@@ -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");
......@@ -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");
......@@ -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");
......@@ -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");
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册