Skip to content

Commit eaa61d2

Browse files
committed
K1: Fix regression with callable references as last statements in lambda
^KT-55729 Fixed
1 parent 5ef397a commit eaa61d2

File tree

7 files changed

+151
-2
lines changed

7 files changed

+151
-2
lines changed

analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingServices.java

+59-2
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
import org.jetbrains.kotlin.resolve.calls.tower.KotlinResolutionCallbacksImpl;
2929
import org.jetbrains.kotlin.resolve.calls.tower.LambdaContextInfo;
3030
import org.jetbrains.kotlin.resolve.scopes.*;
31+
import org.jetbrains.kotlin.types.KotlinType;
3132
import org.jetbrains.kotlin.types.error.ErrorTypeKind;
3233
import org.jetbrains.kotlin.types.error.ErrorUtils;
33-
import org.jetbrains.kotlin.types.KotlinType;
3434
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
3535
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
3636

@@ -382,7 +382,51 @@ private KotlinTypeInfo getTypeOfLastExpressionInBlock(
382382
)
383383
);
384384

385-
if (context.expectedType == NO_EXPECTED_TYPE && statementExpression instanceof KtCallableReferenceExpression) {
385+
// This part is necessary for properly handling the case of calls like `foo { ::bar }`.
386+
// In NI, `::bar` shouldn't be eagerly resolved, but introduced as a postponed atom to the general inference system.
387+
// The correct definition for the case would be satisfaction of three conditions:
388+
// (1) lastStatement is KtCallableReferenceExpression
389+
// (2) expected type is not Unit
390+
// (3) lastStatement.parent.parent [statement -> block -> function literal] is a lambda that is handled by NI
391+
//
392+
// When the conditions are met, `createCallArgument` at `org.jetbrains.kotlin.resolve.calls.tower.KotlinResolutionCallbacksImpl.analyzeAndGetLambdaReturnArguments`
393+
// we just pick that last statement in the lambda.
394+
//
395+
// But due to a bug in initial implementation `createDontCareTypeInfoForNILambda` (third condition), it was checking that
396+
// "lastStatement has a NI lambda among one of the parents".
397+
//
398+
// That immediately leads to incorrectly treatment of cases like:
399+
// foo {
400+
// val x = if (b) {
401+
// ::baz <-- the problem is here
402+
// } else {
403+
// ....
404+
// }
405+
// println()
406+
// }
407+
//
408+
// The problem with the `::baz` is that it should not be processed as a postponed atom for the call, but it has been.
409+
// At the same time, it's being ignored by the NI (because it's not the last statement), thus left unresolved.
410+
// The bugs are described at KT-52270 and KT-55931.
411+
//
412+
// During attempt to fix KT-52270, instead of fixing the third condition (that is actually broken), the second one has been changed to
413+
// "expected type is NO_EXPECTED_TYPE".
414+
// That helped to the main use case from the issue, but still didn't help with KT-55931.
415+
//
416+
// But the actual problem is that brought another regression bug (KT-55729) because after that we started
417+
// resolving last-statement callable references with expected type eagerly as regular top-level references (without conversions).
418+
// So, conversions stopped working there since 1.8.0
419+
//
420+
// While it might be tempting to revert the incorrect fix for KT-52270, and fix the third condition, we can't do it easily
421+
// because it should be a language feature (since it allows new green code), that can't be released in 1.8.10.
422+
// So, we're just trying to _partially_ revert the change, by allowing skipping reference not only in case of NO_EXPECTED_TYPE
423+
// but also in case we have non-trivial expected type AND lastStatement.parent.parent is function literal.
424+
//
425+
// That would mean that everything will work just the same as in 1.8.0, but the single case of
426+
// `expectSomeSpecificFunctionTypeReturnedFromLambda { ::functionNeededAConversion }` (that was been working in 1.7.20).
427+
//
428+
// On the KT-55931, currently we don't have plans to fix it until K2 (where it already seems to be fixed).
429+
if (isCallableReferenceShouldBeAttemptedToBeProcessedByNI(statementExpression, context, isUnitExpectedType)) {
386430
KotlinTypeInfo typeInfo = createDontCareTypeInfoForNILambda(statementExpression, context);
387431
if (typeInfo != null) return typeInfo;
388432
}
@@ -430,6 +474,19 @@ private KotlinTypeInfo getTypeOfLastExpressionInBlock(
430474
return result;
431475
}
432476

477+
private static boolean isCallableReferenceShouldBeAttemptedToBeProcessedByNI(
478+
@NotNull KtExpression statementExpression,
479+
@NotNull ExpressionTypingContext context,
480+
boolean isUnitExpectedType
481+
) {
482+
if (!(statementExpression instanceof KtCallableReferenceExpression)) return false;
483+
if (context.expectedType == NO_EXPECTED_TYPE) return true;
484+
// This is condition we added after 1.8.0 to fix KT-55729 (see the comment above its single usage)
485+
if (!isUnitExpectedType && statementExpression.getParent().getParent() instanceof KtFunctionLiteral) return true;
486+
487+
return false;
488+
}
489+
433490
@Nullable
434491
private static KotlinTypeInfo createDontCareTypeInfoForNILambda(
435492
@NotNull KtExpression statementExpression,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SKIP_TXT
2+
// ISSUE: KT-55729
3+
4+
fun main(b: Boolean) {
5+
callWithLambda {
6+
// The only relevant case for KT-55729, Unit conversion should work, but doesn't in K1 1.8.0
7+
// For K2, it still doesn't work (see KT-55936)
8+
::<!UNRESOLVED_REFERENCE!>test1<!>
9+
}
10+
11+
callWithLambda {
12+
// Unit conversion should work (for K2 see KT-55936)
13+
<!ARGUMENT_TYPE_MISMATCH!>if (b) ::test1 else ::test2<!>
14+
}
15+
16+
callWithLambda {
17+
// That hasn't been working ever in K1 nor K2
18+
<!ARGUMENT_TYPE_MISMATCH!>if (b) {
19+
::test1
20+
} else {
21+
::test2
22+
}<!>
23+
}
24+
25+
callWithLambda {
26+
// That hasn't been working ever in K1 nor K2
27+
(::<!UNRESOLVED_REFERENCE!>test1<!>)
28+
}
29+
}
30+
31+
fun test1(): String = ""
32+
fun test2(): String = ""
33+
34+
fun callWithLambda(action: () -> () -> Unit) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SKIP_TXT
2+
// ISSUE: KT-55729
3+
4+
fun main(b: Boolean) {
5+
callWithLambda {
6+
// The only relevant case for KT-55729, Unit conversion should work, but doesn't in K1 1.8.0
7+
// For K2, it still doesn't work (see KT-55936)
8+
::test1
9+
}
10+
11+
callWithLambda {
12+
// Unit conversion should work (for K2 see KT-55936)
13+
if (b) ::test1 else ::test2
14+
}
15+
16+
callWithLambda {
17+
// That hasn't been working ever in K1 nor K2
18+
if (b) <!TYPE_MISMATCH!>{
19+
<!TYPE_MISMATCH!>::<!TYPE_MISMATCH!>test1<!><!>
20+
}<!> else <!TYPE_MISMATCH!>{
21+
<!TYPE_MISMATCH!>::<!TYPE_MISMATCH!>test2<!><!>
22+
}<!>
23+
}
24+
25+
callWithLambda {
26+
// That hasn't been working ever in K1 nor K2
27+
(<!TYPE_MISMATCH, TYPE_MISMATCH!>::<!TYPE_MISMATCH!>test1<!><!>)
28+
}
29+
}
30+
31+
fun test1(): String = ""
32+
fun test2(): String = ""
33+
34+
fun callWithLambda(action: () -> () -> Unit) {}

compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)