From 45298e0bad22ef26ab15b9f499a38b30c74d0809 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 1 Feb 2016 19:06:59 +0300 Subject: [PATCH] Delegated properties now cannot be used before initialization #KT-10869 Fixed --- .../cfg/ControlFlowInformationProvider.java | 15 ++++++--- .../kotlin/cfg/ControlFlowProcessor.kt | 2 ++ .../properties/DelegatedProperty.instructions | 12 ++++--- .../properties/DelegatedProperty.values | 5 +-- .../unreachableDelegation.instructions | 14 ++++---- .../properties/unreachableDelegation.values | 5 +-- .../delegatedPropertyEarlyAccess.kt | 32 +++++++++++++++++++ .../delegatedPropertyEarlyAccess.txt | 23 +++++++++++++ .../tests/delegatedProperty/recursiveType.kt | 2 +- .../checkers/DiagnosticsTestGenerated.java | 6 ++++ 10 files changed, 95 insertions(+), 21 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedPropertyEarlyAccess.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedPropertyEarlyAccess.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java index fe6d59eec93..16321aee816 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java @@ -360,6 +360,14 @@ public class ControlFlowInformationProvider { } } + private boolean isDefinitelyInitialized(@NotNull PropertyDescriptor propertyDescriptor) { + if (propertyDescriptor.isLateInit()) return true; + if (trace.get(BACKING_FIELD_REQUIRED, propertyDescriptor) == Boolean.TRUE) return false; + PsiElement property = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor); + if (property instanceof KtProperty && ((KtProperty) property).hasDelegate()) return false; + return true; + } + private void checkIsInitialized( @NotNull VariableInitContext ctxt, @NotNull KtElement element, @@ -370,11 +378,8 @@ public class ControlFlowInformationProvider { boolean isDefinitelyInitialized = ctxt.exitInitState.definitelyInitialized(); VariableDescriptor variableDescriptor = ctxt.variableDescriptor; - if (variableDescriptor instanceof PropertyDescriptor) { - PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor; - if (propertyDescriptor.isLateInit() || !trace.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) { - isDefinitelyInitialized = true; - } + if (!isDefinitelyInitialized && variableDescriptor instanceof PropertyDescriptor) { + isDefinitelyInitialized = isDefinitelyInitialized((PropertyDescriptor) variableDescriptor); } if (!isDefinitelyInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) { if (!(variableDescriptor instanceof PropertyDescriptor)) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt index 674715e4eb7..4cd259ba033 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -1001,6 +1001,8 @@ class ControlFlowProcessor(private val trace: BindingTrace) { } val delegate = property.delegateExpression if (delegate != null) { + // We do not want to have getDeferredValue(delegate) here, because delegate value will be read anyway later + visitAssignment(property, getDeferredValue(null), property) generateInstructions(delegate) if (builder.getBoundValue(delegate) != null) { createSyntheticValue(property, MagicKind.VALUE_CONSUMER, delegate) diff --git a/compiler/testData/cfg/declarations/properties/DelegatedProperty.instructions b/compiler/testData/cfg/declarations/properties/DelegatedProperty.instructions index e2748bdd5c4..7d613ff69aa 100644 --- a/compiler/testData/cfg/declarations/properties/DelegatedProperty.instructions +++ b/compiler/testData/cfg/declarations/properties/DelegatedProperty.instructions @@ -54,12 +54,14 @@ val b by a L0: 1 v(val b by a) - r(a) -> - magic[VALUE_CONSUMER](val b by a|) -> + magic[UNRECOGNIZED_WRITE_RHS](val b by a) -> + w(b|) + r(a) -> + magic[VALUE_CONSUMER](val b by a|) -> L1: - NEXT:[] + NEXT:[] error: - PREV:[] + PREV:[] sink: - PREV:[, ] + PREV:[, ] ===================== diff --git a/compiler/testData/cfg/declarations/properties/DelegatedProperty.values b/compiler/testData/cfg/declarations/properties/DelegatedProperty.values index 52a4952622b..5fbd6cb405f 100644 --- a/compiler/testData/cfg/declarations/properties/DelegatedProperty.values +++ b/compiler/testData/cfg/declarations/properties/DelegatedProperty.values @@ -19,6 +19,7 @@ Delegate() : Delegate NEW: call(Delegate(), ) -> == b == val b by a --------------------- - : * NEW: magic[VALUE_CONSUMER](val b by a|) -> -a : Delegate NEW: r(a) -> + : Int NEW: magic[UNRECOGNIZED_WRITE_RHS](val b by a) -> + : * NEW: magic[VALUE_CONSUMER](val b by a|) -> +a : Delegate NEW: r(a) -> ===================== diff --git a/compiler/testData/cfg/declarations/properties/unreachableDelegation.instructions b/compiler/testData/cfg/declarations/properties/unreachableDelegation.instructions index 3072861821f..4917a2c8549 100644 --- a/compiler/testData/cfg/declarations/properties/unreachableDelegation.instructions +++ b/compiler/testData/cfg/declarations/properties/unreachableDelegation.instructions @@ -4,14 +4,16 @@ val foo: Int by throw NullPointerException() L0: 1 v(val foo: Int by throw NullPointerException()) + magic[UNRECOGNIZED_WRITE_RHS](val foo: Int by throw NullPointerException()) -> + w(foo|) mark(throw NullPointerException()) mark(NullPointerException()) - call(NullPointerException(), ) -> - throw (throw NullPointerException()|) NEXT:[] + call(NullPointerException(), ) -> + throw (throw NullPointerException()|) NEXT:[] L1: - NEXT:[] PREV:[] + NEXT:[] PREV:[] error: - PREV:[throw (throw NullPointerException()|)] + PREV:[throw (throw NullPointerException()|)] sink: - PREV:[, ] -===================== \ No newline at end of file + PREV:[, ] +===================== diff --git a/compiler/testData/cfg/declarations/properties/unreachableDelegation.values b/compiler/testData/cfg/declarations/properties/unreachableDelegation.values index 95e81c70145..008c765c89d 100644 --- a/compiler/testData/cfg/declarations/properties/unreachableDelegation.values +++ b/compiler/testData/cfg/declarations/properties/unreachableDelegation.values @@ -1,5 +1,6 @@ == foo == val foo: Int by throw NullPointerException() --------------------- -NullPointerException() : {<: Throwable} NEW: call(NullPointerException(), ) -> -===================== \ No newline at end of file + : Int NEW: magic[UNRECOGNIZED_WRITE_RHS](val foo: Int by throw NullPointerException()) -> +NullPointerException() : {<: Throwable} NEW: call(NullPointerException(), ) -> +===================== diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedPropertyEarlyAccess.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedPropertyEarlyAccess.kt new file mode 100644 index 00000000000..23f9b0a3e01 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedPropertyEarlyAccess.kt @@ -0,0 +1,32 @@ +// See also KT-10869: Accessing lazy properties from init causes IllegalArgumentException + +import kotlin.reflect.KProperty + +class CustomDelegate { + operator fun getValue(thisRef: Any?, prop: KProperty<*>): String = prop.name +} + +class Kaboom() { + // Here and below we should have errors for simple AND delegated + init { + delegated.hashCode() + simple.hashCode() + withGetter.hashCode() + } + + val other = delegated + + val another = simple + + val something = withGetter + + val delegated: String by CustomDelegate() + + val simple = "xyz" + + val withGetter: String + get() = "abc" + + // No error should be here + val after = delegated +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedPropertyEarlyAccess.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedPropertyEarlyAccess.txt new file mode 100644 index 00000000000..706644bf663 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedPropertyEarlyAccess.txt @@ -0,0 +1,23 @@ +package + +public final class CustomDelegate { + public constructor CustomDelegate() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ prop: kotlin.reflect.KProperty<*>): kotlin.String + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Kaboom { + public constructor Kaboom() + public final val after: kotlin.String + public final val another: kotlin.String = "xyz" + public final val delegated: kotlin.String + public final val other: kotlin.String + public final val simple: kotlin.String = "xyz" + public final val something: kotlin.String + public final val withGetter: 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/delegatedProperty/recursiveType.kt b/compiler/testData/diagnostics/tests/delegatedProperty/recursiveType.kt index 211693d2bf9..12b5c99b298 100644 --- a/compiler/testData/diagnostics/tests/delegatedProperty/recursiveType.kt +++ b/compiler/testData/diagnostics/tests/delegatedProperty/recursiveType.kt @@ -6,7 +6,7 @@ val a by a val b by Delegate(b) -val c by d +val c by d val d by c class Delegate(i: Int) { diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 79c07c9fcee..2f641595976 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -2853,6 +2853,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("delegatedPropertyEarlyAccess.kt") + public void testDelegatedPropertyEarlyAccess() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/delegatedPropertyEarlyAccess.kt"); + doTest(fileName); + } + @TestMetadata("elvisNotProcessed.kt") public void testElvisNotProcessed() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/elvisNotProcessed.kt");