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:
Denis.Zharkov
2023-06-09 11:36:47 +02:00
committed by Space Team
parent b8c4a4c80a
commit 41933facbb
9 changed files with 175 additions and 7 deletions
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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)
}
}
@@ -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"
}
@@ -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"
}
@@ -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)
}
@@ -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 {