Delegated properties now cannot be used before initialization #KT-10869 Fixed
This commit is contained in:
@@ -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)) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
+7
-5
@@ -54,12 +54,14 @@ val b by a
|
||||
L0:
|
||||
1 <START>
|
||||
v(val b by a)
|
||||
r(a) -> <v0>
|
||||
magic[VALUE_CONSUMER](val b by a|<v0>) -> <v1>
|
||||
magic[UNRECOGNIZED_WRITE_RHS](val b by a) -> <v0>
|
||||
w(b|<v0>)
|
||||
r(a) -> <v1>
|
||||
magic[VALUE_CONSUMER](val b by a|<v1>) -> <v2>
|
||||
L1:
|
||||
<END> NEXT:[<SINK>]
|
||||
<END> NEXT:[<SINK>]
|
||||
error:
|
||||
<ERROR> PREV:[]
|
||||
<ERROR> PREV:[]
|
||||
sink:
|
||||
<SINK> PREV:[<ERROR>, <END>]
|
||||
<SINK> PREV:[<ERROR>, <END>]
|
||||
=====================
|
||||
|
||||
@@ -19,6 +19,7 @@ Delegate() <v0>: Delegate NEW: call(Delegate(), <init>) -> <v0>
|
||||
== b ==
|
||||
val b by a
|
||||
---------------------
|
||||
<v1>: * NEW: magic[VALUE_CONSUMER](val b by a|<v0>) -> <v1>
|
||||
a <v0>: Delegate NEW: r(a) -> <v0>
|
||||
<v0>: Int NEW: magic[UNRECOGNIZED_WRITE_RHS](val b by a) -> <v0>
|
||||
<v2>: * NEW: magic[VALUE_CONSUMER](val b by a|<v1>) -> <v2>
|
||||
a <v1>: Delegate NEW: r(a) -> <v1>
|
||||
=====================
|
||||
|
||||
+8
-6
@@ -4,14 +4,16 @@ val foo: Int by throw NullPointerException()
|
||||
L0:
|
||||
1 <START>
|
||||
v(val foo: Int by throw NullPointerException())
|
||||
magic[UNRECOGNIZED_WRITE_RHS](val foo: Int by throw NullPointerException()) -> <v0>
|
||||
w(foo|<v0>)
|
||||
mark(throw NullPointerException())
|
||||
mark(NullPointerException())
|
||||
call(NullPointerException(), <init>) -> <v0>
|
||||
throw (throw NullPointerException()|<v0>) NEXT:[<ERROR>]
|
||||
call(NullPointerException(), <init>) -> <v1>
|
||||
throw (throw NullPointerException()|<v1>) NEXT:[<ERROR>]
|
||||
L1:
|
||||
<END> NEXT:[<SINK>] PREV:[]
|
||||
<END> NEXT:[<SINK>] PREV:[]
|
||||
error:
|
||||
<ERROR> PREV:[throw (throw NullPointerException()|<v0>)]
|
||||
<ERROR> PREV:[throw (throw NullPointerException()|<v1>)]
|
||||
sink:
|
||||
<SINK> PREV:[<ERROR>, <END>]
|
||||
=====================
|
||||
<SINK> PREV:[<ERROR>, <END>]
|
||||
=====================
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
== foo ==
|
||||
val foo: Int by throw NullPointerException()
|
||||
---------------------
|
||||
NullPointerException() <v0>: {<: Throwable} NEW: call(NullPointerException(), <init>) -> <v0>
|
||||
=====================
|
||||
<v0>: Int NEW: magic[UNRECOGNIZED_WRITE_RHS](val foo: Int by throw NullPointerException()) -> <v0>
|
||||
NullPointerException() <v1>: {<: Throwable} NEW: call(NullPointerException(), <init>) -> <v1>
|
||||
=====================
|
||||
|
||||
+32
@@ -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 {
|
||||
<!UNINITIALIZED_VARIABLE!>delegated<!>.hashCode()
|
||||
<!UNINITIALIZED_VARIABLE!>simple<!>.hashCode()
|
||||
withGetter.hashCode()
|
||||
}
|
||||
|
||||
val other = <!UNINITIALIZED_VARIABLE!>delegated<!>
|
||||
|
||||
val another = <!UNINITIALIZED_VARIABLE!>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
|
||||
}
|
||||
+23
@@ -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
|
||||
}
|
||||
@@ -6,7 +6,7 @@ val a by <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>a<!>
|
||||
|
||||
val b by Delegate(<!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>b<!>)
|
||||
|
||||
val c by <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>d<!>
|
||||
val c by <!UNINITIALIZED_VARIABLE, DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>d<!>
|
||||
val d by <!TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM!>c<!>
|
||||
|
||||
class Delegate(i: Int) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user