K2: Fix ISE from inference on delegate vars with implicit types
See the test data. ISE happened because at some point after incomplete `getValue` resolution of `a` property, we updated in the `transformAccessors` the property type to the `Variable(Y)` type and then used it as a 3rd argument for `setValue` call which is incorrect because the variable belongs to a different constraint system (from `getValue`). Mostly, the fix is just a repeating K1 behavior, namely postponing `setValue` resolution until delegate inference is completed. ^KT-59066 Fixed
This commit is contained in:
committed by
Space Team
parent
b8c4a4c80a
commit
41933facbb
+12
@@ -8974,12 +8974,24 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/missedSetter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noInferenceFromGetValueThroughSetValue.kt")
|
||||
public void testNoInferenceFromGetValueThroughSetValue() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noInferenceFromGetValueThroughSetValue.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noInferenceFromWrappedDelegate.kt")
|
||||
public void testNoInferenceFromWrappedDelegate() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noInferenceFromWrappedDelegate.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noPreliminarySetterInferenceForImplicitlyTypedVar.kt")
|
||||
public void testNoPreliminarySetterInferenceForImplicitlyTypedVar() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noPreliminarySetterInferenceForImplicitlyTypedVar.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonDefaultAccessors.kt")
|
||||
public void testNonDefaultAccessors() throws Exception {
|
||||
|
||||
+12
@@ -8974,12 +8974,24 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/missedSetter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noInferenceFromGetValueThroughSetValue.kt")
|
||||
public void testNoInferenceFromGetValueThroughSetValue() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noInferenceFromGetValueThroughSetValue.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noInferenceFromWrappedDelegate.kt")
|
||||
public void testNoInferenceFromWrappedDelegate() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noInferenceFromWrappedDelegate.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noPreliminarySetterInferenceForImplicitlyTypedVar.kt")
|
||||
public void testNoPreliminarySetterInferenceForImplicitlyTypedVar() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noPreliminarySetterInferenceForImplicitlyTypedVar.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonDefaultAccessors.kt")
|
||||
public void testNonDefaultAccessors() throws Exception {
|
||||
|
||||
+12
@@ -8974,12 +8974,24 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/missedSetter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noInferenceFromGetValueThroughSetValue.kt")
|
||||
public void testNoInferenceFromGetValueThroughSetValue() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noInferenceFromGetValueThroughSetValue.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noInferenceFromWrappedDelegate.kt")
|
||||
public void testNoInferenceFromWrappedDelegate() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noInferenceFromWrappedDelegate.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noPreliminarySetterInferenceForImplicitlyTypedVar.kt")
|
||||
public void testNoPreliminarySetterInferenceForImplicitlyTypedVar() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noPreliminarySetterInferenceForImplicitlyTypedVar.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonDefaultAccessors.kt")
|
||||
public void testNonDefaultAccessors() throws Exception {
|
||||
|
||||
+12
@@ -8980,12 +8980,24 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/missedSetter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noInferenceFromGetValueThroughSetValue.kt")
|
||||
public void testNoInferenceFromGetValueThroughSetValue() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noInferenceFromGetValueThroughSetValue.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noInferenceFromWrappedDelegate.kt")
|
||||
public void testNoInferenceFromWrappedDelegate() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noInferenceFromWrappedDelegate.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noPreliminarySetterInferenceForImplicitlyTypedVar.kt")
|
||||
public void testNoPreliminarySetterInferenceForImplicitlyTypedVar() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noPreliminarySetterInferenceForImplicitlyTypedVar.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonDefaultAccessors.kt")
|
||||
public void testNonDefaultAccessors() throws Exception {
|
||||
|
||||
+40
-7
@@ -181,7 +181,9 @@ open class FirDeclarationsResolveTransformer(
|
||||
val propertyTypeIsKnown = propertyTypeRefAfterResolve is FirResolvedTypeRef
|
||||
val mayResolveGetter = mayResolveSetter || !propertyTypeIsKnown
|
||||
if (mayResolveGetter) {
|
||||
property.transformAccessors(mayResolveSetter)
|
||||
property.transformAccessors(
|
||||
if (mayResolveSetter) SetterResolutionMode.FULLY_RESOLVE else SetterResolutionMode.ONLY_IMPLICIT_PARAMETER_TYPE
|
||||
)
|
||||
property.replaceBodyResolveState(
|
||||
if (mayResolveSetter) FirPropertyBodyResolveState.EVERYTHING_RESOLVED
|
||||
else FirPropertyBodyResolveState.INITIALIZER_AND_GETTER_RESOLVED
|
||||
@@ -277,6 +279,8 @@ open class FirDeclarationsResolveTransformer(
|
||||
}
|
||||
|
||||
private fun transformPropertyAccessorsWithDelegate(property: FirProperty, delegate: FirExpression) {
|
||||
val isImplicitTypedProperty = property.returnTypeRef is FirImplicitTypeRef
|
||||
|
||||
context.forPropertyDelegateAccessors(property, resolutionContext, callCompleter) {
|
||||
dataFlowAnalyzer.enterDelegateExpression()
|
||||
// Resolve delegate expression, after that, delegate will contain either expr.provideDelegate or expr
|
||||
@@ -288,7 +292,12 @@ open class FirDeclarationsResolveTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
property.transformAccessors()
|
||||
// We don't use inference from setValue calls (i.e. don't resolve setters until the delegate inference is completed),
|
||||
// when property doesn't have explicit type.
|
||||
// It's necessary because we need to supply the property type as the 3rd argument for `setValue` and there might be uninferred
|
||||
// variables from `getValue`.
|
||||
// The same logic was used at K1 (see org.jetbrains.kotlin.resolve.DelegatedPropertyResolver.inferDelegateTypeFromGetSetValueMethods)
|
||||
property.transformAccessors(if (isImplicitTypedProperty) SetterResolutionMode.SKIP else SetterResolutionMode.FULLY_RESOLVE)
|
||||
val completedCalls = completeCandidates()
|
||||
|
||||
val finalSubstitutor = createFinalSubstitutor()
|
||||
@@ -310,10 +319,14 @@ open class FirDeclarationsResolveTransformer(
|
||||
completedCalls.forEach {
|
||||
it.transformSingle(callCompletionResultsWriter, null)
|
||||
}
|
||||
|
||||
dataFlowAnalyzer.exitDelegateExpression(delegate)
|
||||
property
|
||||
}
|
||||
|
||||
// `isImplicitTypedProperty` means we haven't run setter resolution yet (see its second usage)
|
||||
if (isImplicitTypedProperty) {
|
||||
property.resolveSetter(property.returnTypeRef, mayResolveSetterBody = true)
|
||||
}
|
||||
|
||||
dataFlowAnalyzer.exitDelegateExpression(delegate)
|
||||
}
|
||||
|
||||
override fun transformPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: ResolutionMode): FirStatement {
|
||||
@@ -420,7 +433,17 @@ open class FirDeclarationsResolveTransformer(
|
||||
return variable
|
||||
}
|
||||
|
||||
private fun FirProperty.transformAccessors(mayResolveSetter: Boolean = true) {
|
||||
// TODO: This enum might actually be a boolean (resolve setter/don't resolve setter)
|
||||
// But for some reason, in IDE there's a need to resolve setter's parameter types on the implicit-resolution stage
|
||||
// See ad183434137939a0c9eeea2f7df9ef522672a18e commit.
|
||||
// But for delegate inference case, we don't need both body of the setter and its parameter resolved (SKIP mode)
|
||||
private enum class SetterResolutionMode {
|
||||
FULLY_RESOLVE, ONLY_IMPLICIT_PARAMETER_TYPE, SKIP
|
||||
}
|
||||
|
||||
private fun FirProperty.transformAccessors(
|
||||
setterResolutionMode: SetterResolutionMode = SetterResolutionMode.FULLY_RESOLVE
|
||||
) {
|
||||
var enhancedTypeRef = returnTypeRef
|
||||
if (bodyResolveState < FirPropertyBodyResolveState.INITIALIZER_AND_GETTER_RESOLVED) {
|
||||
getter?.let {
|
||||
@@ -433,10 +456,20 @@ open class FirDeclarationsResolveTransformer(
|
||||
// We need update type of getter for case when its type was approximated
|
||||
getter?.transformTypeWithPropertyType(enhancedTypeRef, forceUpdateForNonImplicitTypes = true)
|
||||
}
|
||||
|
||||
if (setterResolutionMode != SetterResolutionMode.SKIP) {
|
||||
resolveSetter(enhancedTypeRef, mayResolveSetterBody = setterResolutionMode == SetterResolutionMode.FULLY_RESOLVE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirProperty.resolveSetter(
|
||||
enhancedTypeRef: FirTypeRef,
|
||||
mayResolveSetterBody: Boolean,
|
||||
) {
|
||||
setter?.let {
|
||||
it.transformTypeWithPropertyType(enhancedTypeRef)
|
||||
|
||||
if (mayResolveSetter) {
|
||||
if (mayResolveSetterBody) {
|
||||
transformAccessor(it, enhancedTypeRef, this)
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
// Definitions
|
||||
class M<E>
|
||||
|
||||
operator fun <X> M<out X>.getValue(thisRef: Any?, property: KProperty<*>): String = "value"
|
||||
operator fun <Z> M<in Z>.setValue(thisRef: Any?, property: KProperty<*>, value: Z) {}
|
||||
|
||||
fun <U> m(): M<U> = M()
|
||||
|
||||
// We don't allow to infer type of a delegate expression through a setValue, where the argument (value) is constrained by the return type of a getValue
|
||||
var a by <!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>m<!>()
|
||||
|
||||
// We infer type of delegate expression through a setValue from the explicit property type
|
||||
var b: String by m()
|
||||
|
||||
fun takeString(v: String) {}
|
||||
|
||||
fun main() {
|
||||
takeString(a)
|
||||
a = "a"
|
||||
takeString(b)
|
||||
b = "b"
|
||||
}
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
// Definitions
|
||||
class M<E>
|
||||
|
||||
operator fun <X> M<out X>.getValue(thisRef: Any?, property: KProperty<*>): String = "value"
|
||||
operator fun <Z> M<in Z>.setValue(thisRef: Any?, property: KProperty<*>, value: Z) {}
|
||||
|
||||
fun <U> m(): M<U> = M()
|
||||
|
||||
// We don't allow to infer type of a delegate expression through a setValue, where the argument (value) is constrained by the return type of a getValue
|
||||
var a by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE!><!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>m<!>()<!>
|
||||
|
||||
// We infer type of delegate expression through a setValue from the explicit property type
|
||||
var b: String by m()
|
||||
|
||||
fun takeString(v: String) {}
|
||||
|
||||
fun main() {
|
||||
takeString(<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>a<!>)
|
||||
<!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>a<!> = "a"
|
||||
takeString(b)
|
||||
b = "b"
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// WITH_REFLECT
|
||||
// ISSUE: KT-59066
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
interface M< E>
|
||||
|
||||
public operator fun <X, Y : X> M<out X>.getValue(thisRef: Any?, property: KProperty<*>): Y =
|
||||
TODO()
|
||||
|
||||
public operator fun <Z> M<in Z>.setValue(thisRef: Any?, property: KProperty<*>, value: Z) {}
|
||||
|
||||
fun <U> m(u: U): M<U> = TODO()
|
||||
|
||||
var a by m("")
|
||||
|
||||
var b by mutableMapOf("" to 1)
|
||||
|
||||
fun main() {
|
||||
a.length
|
||||
a = "b"
|
||||
|
||||
b = 23
|
||||
b.and(4)
|
||||
}
|
||||
Generated
+12
@@ -8980,12 +8980,24 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/missedSetter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noInferenceFromGetValueThroughSetValue.kt")
|
||||
public void testNoInferenceFromGetValueThroughSetValue() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noInferenceFromGetValueThroughSetValue.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noInferenceFromWrappedDelegate.kt")
|
||||
public void testNoInferenceFromWrappedDelegate() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noInferenceFromWrappedDelegate.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noPreliminarySetterInferenceForImplicitlyTypedVar.kt")
|
||||
public void testNoPreliminarySetterInferenceForImplicitlyTypedVar() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/delegatedProperty/noPreliminarySetterInferenceForImplicitlyTypedVar.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonDefaultAccessors.kt")
|
||||
public void testNonDefaultAccessors() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user