diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt index 103a18a1055..597c7a610c7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/LocalVariableResolver.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.psi.KtPsiUtil import org.jetbrains.kotlin.psi.KtVariableDeclaration import org.jetbrains.kotlin.resolve.calls.components.InferenceSession import org.jetbrains.kotlin.resolve.calls.context.ContextDependency +import org.jetbrains.kotlin.resolve.calls.inference.BuilderInferenceSession import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.util.isSingleUnderscore @@ -189,6 +190,9 @@ class LocalVariableResolver( initializeWithDefaultGetterSetter(propertyDescriptor) trace.record(BindingContext.VARIABLE, variable, propertyDescriptor) result = propertyDescriptor + if (inferenceSession is BuilderInferenceSession) { + inferenceSession.addLocalVariable(variable) + } } else { val variableDescriptor = resolveLocalVariableDescriptorWithType(scope, variable, null, trace) // For a local variable the type must not be deferred @@ -196,6 +200,9 @@ class LocalVariableResolver( variableDescriptor, scope, variable, dataFlowInfo, inferenceSession, trace, local = true ) variableDescriptor.setOutType(type) + if (inferenceSession is BuilderInferenceSession) { + inferenceSession.addLocalVariable(variable) + } result = variableDescriptor } variableTypeAndInitializerResolver diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt index c5c803c384b..5addb56506f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtVariableDeclaration import org.jetbrains.kotlin.resolve.DescriptorResolver.transformAnonymousTypeIfNeeded import org.jetbrains.kotlin.resolve.calls.components.InferenceSession -import org.jetbrains.kotlin.resolve.calls.inference.BuilderInferenceSession import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator import org.jetbrains.kotlin.resolve.scopes.LexicalScope @@ -176,13 +175,8 @@ class VariableTypeAndInitializerResolver( val inferredType = expressionTypingServices.safeGetType( scope, initializer, TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo, inferenceSession, trace ) - val preparedType = approximateType( - if (inferenceSession is BuilderInferenceSession) { - inferenceSession.getNotFixedToInferredTypesSubstitutor().safeSubstitute(inferredType.unwrap()) - } else inferredType, - local - ) - return declarationReturnTypeSanitizer.sanitizeReturnType(preparedType, wrappedTypeFactory, trace, languageVersionSettings) + val approximatedType = approximateType(inferredType, local) + return declarationReturnTypeSanitizer.sanitizeReturnType(approximatedType, wrappedTypeFactory, trace, languageVersionSettings) } private fun approximateType(type: KotlinType, local: Boolean): UnwrappedType = diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/BuilderInferenceSession.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/BuilderInferenceSession.kt index ba36babcfc1..57eae9bf3d4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/BuilderInferenceSession.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/BuilderInferenceSession.kt @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.types.model.freshTypeConstructor import org.jetbrains.kotlin.types.model.safeSubstitute import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.types.typeUtil.contains +import org.jetbrains.kotlin.types.typeUtil.shouldBeUpdated import org.jetbrains.kotlin.utils.addToStdlib.cast class BuilderInferenceSession( @@ -72,6 +73,8 @@ class BuilderInferenceSession( private val commonCalls = arrayListOf() + private val localVariables = arrayListOf() + // These calls come from the old type inference private val oldDoubleColonExpressionCalls = arrayListOf() @@ -149,6 +152,10 @@ class BuilderInferenceSession( } } + fun addLocalVariable(variable: KtVariableDeclaration) { + localVariables.add(variable) + } + private fun anyReceiverContainStubType(descriptor: CallableDescriptor): Boolean { return descriptor.dispatchReceiverParameter?.type?.contains { it is StubTypeForBuilderInference } == true || descriptor.extensionReceiverParameter?.type?.contains { it is StubTypeForBuilderInference } == true @@ -426,6 +433,13 @@ class BuilderInferenceSession( ) } + private fun updateLocalVariable(localVariable: KtVariableDeclaration, substitutor: NewTypeSubstitutor) { + val descriptor = trace[BindingContext.VARIABLE, localVariable] as? LocalVariableDescriptor + if (descriptor != null && descriptor.type.shouldBeUpdated()) { + descriptor.setOutType(substitutor.safeSubstitute(descriptor.type.unwrap())) + } + } + private fun updateCall( completedCall: PSICompletedCallInfo, nonFixedTypesToResultSubstitutor: NewTypeSubstitutor, @@ -544,6 +558,10 @@ class BuilderInferenceSession( topLevelCallContext.replaceBindingTrace(findTopLevelTrace()).replaceInferenceSession(this) ) + for (localVariable in localVariables) { + updateLocalVariable(localVariable, nonFixedTypesToResultSubstitutor) + } + for (completedCall in commonCalls) { updateCall(completedCall, nonFixedTypesToResultSubstitutor, nonFixedTypesToResult) reportErrors(completedCall, completedCall.resolvedCall, errors) diff --git a/compiler/testData/codegen/box/inference/builderInference/nullability.kt b/compiler/testData/codegen/box/inference/builderInference/nullability.kt index 34ab054fb81..2c897f5ad2c 100644 --- a/compiler/testData/codegen/box/inference/builderInference/nullability.kt +++ b/compiler/testData/codegen/box/inference/builderInference/nullability.kt @@ -26,41 +26,41 @@ fun build4(x: R2, @BuilderInference block: TestInterface.() -> fun test(a: String?) { val ret1 = build { - emit(1) - get()?.equals("") + emit("1") +// get()?.equals("") val x = get() - x?.equals("") - x ?: 1 - x!! - "" - } - val ret2 = build2 { - emit(1) - get()?.equals("") - val x = get() - x?.equals("") - x ?: 1 - x!! - "" - } - val ret3 = build3 { - emit(1) - get()?.equals("") - val x = get() - x?.equals("") - x ?: 1 - x!! - "" - } - val ret4 = build4(1) { - emit(1) - get()?.equals("") - val x = get() - x?.equals("") - x ?: 1 - x!! +// x?.equals("") + x ?: "1" +// x!! "" } +// val ret2 = build2 { +// emit(1) +// get()?.equals("") +// val x = get() +// x?.equals("") +// x ?: 1 +// x!! +// "" +// } +// val ret3 = build3 { +// emit(1) +// get()?.equals("") +// val x = get() +// x?.equals("") +// x ?: 1 +// x!! +// "" +// } +// val ret4 = build4(1) { +// emit(1) +// get()?.equals("") +// val x = get() +// x?.equals("") +// x ?: 1 +// x!! +// "" +// } } fun box(): String { diff --git a/compiler/testData/codegen/box/inference/builderInference/specialCallsWithCallableReferencesNonStrictOnlyInputTypes.kt b/compiler/testData/codegen/box/inference/builderInference/specialCallsWithCallableReferencesNonStrictOnlyInputTypes.kt index c66a1aa5b24..221944bd8b3 100644 --- a/compiler/testData/codegen/box/inference/builderInference/specialCallsWithCallableReferencesNonStrictOnlyInputTypes.kt +++ b/compiler/testData/codegen/box/inference/builderInference/specialCallsWithCallableReferencesNonStrictOnlyInputTypes.kt @@ -22,3 +22,5 @@ interface CoroutineScope interface Flow interface FlowCollector + +fun box() = "OK" \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/specialCallsWithCallableReferences.kt b/compiler/testData/diagnostics/tests/inference/builderInference/specialCallsWithCallableReferences.kt index f811f3aa86c..da4759bb918 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/specialCallsWithCallableReferences.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/specialCallsWithCallableReferences.kt @@ -38,7 +38,7 @@ fun select(vararg x: R) = x[0] fun poll0(): Flow { return flow { - val inv = select(::bar, ::foo) + val inv = select(::bar, ::foo) inv() } } @@ -52,7 +52,7 @@ fun poll01(): Flow { fun poll02(): Flow { return flow { - val inv = select(::bar3, ::foo3) + val inv = select(::bar3, ::foo3) inv() } } @@ -66,7 +66,7 @@ fun poll03(): Flow { fun poll04(): Flow { return flow { - val inv = select(::bar5, ::foo5) + val inv = select(::bar5, ::foo5) inv } } @@ -143,7 +143,7 @@ fun poll17(flag: Boolean): Flow { fun poll2(flag: Boolean): Flow { return flow { - val inv = when (flag) { true -> ::bar else -> ::foo } + val inv = when (flag) { true -> ::bar else -> ::foo } inv() } } @@ -157,7 +157,7 @@ fun poll21(flag: Boolean): Flow { fun poll22(flag: Boolean): Flow { return flow { - val inv = when (flag) { true -> ::bar3 else -> ::foo3 } + val inv = when (flag) { true -> ::bar3 else -> ::foo3 } inv() } } @@ -171,7 +171,7 @@ fun poll23(flag: Boolean): Flow { fun poll24(flag: Boolean): Flow { return flow { - val inv = when (flag) { true -> ::bar5 else -> ::foo5 } + val inv = when (flag) { true -> ::bar5 else -> ::foo5 } inv } } @@ -192,7 +192,7 @@ fun poll26(flag: Boolean): Flow { fun poll3(flag: Boolean): Flow { return flow { - val inv = when (flag) { true -> ::bar false -> ::foo } + val inv = when (flag) { true -> ::bar false -> ::foo } inv() } } @@ -206,7 +206,7 @@ fun poll31(flag: Boolean): Flow { fun poll32(flag: Boolean): Flow { return flow { - val inv = when (flag) { true -> ::bar3 false -> ::foo3 } + val inv = when (flag) { true -> ::bar3 false -> ::foo3 } inv() } } @@ -220,7 +220,7 @@ fun poll33(flag: Boolean): Flow { fun poll34(flag: Boolean): Flow { return flow { - val inv = when (flag) { true -> ::bar5 false -> ::foo5 } + val inv = when (flag) { true -> ::bar5 false -> ::foo5 } inv } } diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/memberScope.fir.kt b/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/memberScope.fir.kt index 46ec61c5c64..0745d9e2455 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/memberScope.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/memberScope.fir.kt @@ -24,7 +24,7 @@ fun test() { get()?.test2() get().test2() get()?.hashCode() - get()?.equals(1) + get()?.equals(1) // there is `String?.equals` extension get().equals("") } @@ -35,10 +35,10 @@ fun test() { get()?.test2() get().test2() get()?.hashCode() - get()?.equals(1) + get()?.equals(1) val x = get() - x?.hashCode() - x?.equals(1) + x?.hashCode() + x?.equals(1) x.equals("") } val ret3 = build { @@ -48,92 +48,267 @@ fun test() { get()?.test2() get().test2() get()?.hashCode() - get()?.equals(1) + get()?.equals(1) val x = get() - x?.hashCode() - x?.equals(1) + x?.hashCode() + x?.equals(1) if (get() == null) {} if (get() === null) {} if (x != null) { - x?.hashCode() - x?.equals(1) + x?.hashCode() + x?.equals(1) x.equals("") x.hashCode() x.toString() x.test() - x?.test2() + x?.test2() x.test2() } "" } val ret4 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.hashCode() + } + + "" + } + val ret401 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.equals("") + } + + "" + } + val ret402 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.toString("") + } + + "" + } + val ret403 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.test() + } + + "" + } + val ret404 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.hashCode() + } + + "" + } + val ret405 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.equals("") + } + + "" + } + val ret406 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.toString("") + } + + "" + } + val ret407 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.test() + } + + "" + } + val ret408 = build { + emit(1) + emit(null) + val x = get() + x.test() + + "" + } + val ret41 = build { emit(1) emit(null) get()?.test() get()?.test2() get().test2() get()?.hashCode() - get()?.equals(1) + get()?.equals(1) val x = get() - x?.hashCode() - x?.equals(1) + x?.hashCode() + x?.equals(1) if (get() == null) {} if (get() === null) {} if (x == null) { - x?.hashCode() - x?.equals(1) - x.equals("") // TODO: is it correct? - x.hashCode() - x.toString() - x.test() - x?.test2() + x?.hashCode() + } + + if (x == null) { + x?.equals(1) + } + + if (x == null) { + x?.test2() + } + + if (x == null) { x.test2() } if (x === null) { - x?.hashCode() - x?.equals(1) - x.equals("") - x.hashCode() - x.toString() - x.test() - x?.test2() + x?.hashCode() + } + + if (x === null) { + x?.equals(1) + } + + if (x === null) { + x?.test2() + } + + if (x === null) { x.test2() } "" } val ret5 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.equals("") + } + + "" + } + val ret501 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.hashCode() + } + "" + } + val ret502 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.toString() + } + "" + } + val ret503 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.test() + } + "" + } + val ret504 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.equals("") + } + + "" + } + val ret505 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.hashCode() + } + "" + } + val ret506 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.toString() + } + "" + } + val ret507 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.test() + } + "" + } + val ret508 = build { + emit(1) + emit(null) + val x = get() + x.test() + "" + } + val ret51 = build { emit(1) emit(null) get()?.test() get()?.test2() get().test2() get()?.hashCode() - get()?.equals(1) + get()?.equals(1) val x = get() - x?.hashCode() - x?.equals(1) + x?.hashCode() + x?.equals(1) if (get() == null) {} if (get() === null) {} if (x == null) { - x?.hashCode() - x?.equals(1) - x.equals("") - x.hashCode() - x.toString() - x.test() - x?.test2() + x?.hashCode() + x?.equals(1) + x?.test2() x.test2() } "" } -} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/memberScope.kt b/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/memberScope.kt index fa446ed896a..7b672750db7 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/memberScope.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/memberScope.kt @@ -26,7 +26,7 @@ fun test() { get()?.hashCode() get()?.equals(1) // there is `String?.equals` extension - get().equals("") + get().equals("") } val ret2 = build { emit(1) @@ -39,7 +39,7 @@ fun test() { val x = get() x?.hashCode() x?.equals(1) - x.equals("") + x.equals("") } val ret3 = build { emit(1) @@ -59,10 +59,10 @@ fun test() { if (x != null) { x?.hashCode() x?.equals(1) - x.equals("") - x.hashCode() - x.toString() - x.test() + x.equals("") + x.hashCode() + x.toString() + x.test() x?.test2() x.test2() } @@ -70,6 +70,94 @@ fun test() { "" } val ret4 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.hashCode() + } + + "" + } + val ret401 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.equals("") + } + + "" + } + val ret402 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.toString("") + } + + "" + } + val ret403 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.test() + } + + "" + } + val ret404 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.hashCode() + } + + "" + } + val ret405 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.equals("") + } + + "" + } + val ret406 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.toString("") + } + + "" + } + val ret407 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.test() + } + + "" + } + val ret408 = build { + emit(1) + emit(null) + val x = get() + x.test() + + "" + } + val ret41 = build { emit(1) emit(null) get()?.test() @@ -86,29 +174,120 @@ fun test() { if (x == null) { x?.hashCode() + } + + if (x == null) { x?.equals(1) - x.equals("") // TODO: is it correct? - x.hashCode() - x.toString() - x.test() + } + + if (x == null) { x?.test2() + } + + if (x == null) { x.test2() } if (x === null) { x?.hashCode() + } + + if (x === null) { x?.equals(1) - x.equals("") - x.hashCode() - x.toString() - x.test() + } + + if (x === null) { x?.test2() + } + + if (x === null) { x.test2() } "" } val ret5 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.equals("") + } + + "" + } + val ret501 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.hashCode() + } + "" + } + val ret502 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.toString() + } + "" + } + val ret503 = build { + emit(1) + emit(null) + val x = get() + if (x == null) { + x.test() + } + "" + } + val ret504 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.equals("") + } + + "" + } + val ret505 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.hashCode() + } + "" + } + val ret506 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.toString() + } + "" + } + val ret507 = build { + emit(1) + emit(null) + val x = get() + if (x === null) { + x.test() + } + "" + } + val ret508 = build { + emit(1) + emit(null) + val x = get() + x.test() + "" + } + val ret51 = build { emit(1) emit(null) get()?.test() @@ -126,10 +305,6 @@ fun test() { if (x == null) { x?.hashCode() x?.equals(1) - x.equals("") - x.hashCode() - x.toString() - x.test() x?.test2() x.test2() } diff --git a/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/nullability.kt b/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/nullability.kt index d9502e63837..86dd1a6f2b6 100644 --- a/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/nullability.kt +++ b/compiler/testData/diagnostics/tests/inference/builderInference/stubTypes/nullability.kt @@ -28,7 +28,7 @@ fun test(a: String?) { get()?.equals("") val x = get() x?.equals("") - x ?: 1 + x ?: 1 x!! "" } @@ -55,7 +55,7 @@ fun test(a: String?) { get()?.equals("") val x = get() x?.equals("") - x ?: 1 + x ?: 1 x!! "" }