Skip to content

Commit cd1ae7f

Browse files
committed
Add resolution status to report about unsuccessful smartcast
#KT-10248 Fixed #KT-11119 Fixed
1 parent 0f44972 commit cd1ae7f

File tree

12 files changed

+178
-10
lines changed

12 files changed

+178
-10
lines changed

compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ class CandidateResolver(
559559
}
560560
if (!smartCastResult.isCorrect) {
561561
// Error about unstable smart cast reported within checkAndRecordPossibleCast
562-
return OTHER_ERROR
562+
return UNSTABLE_SMARTCAST_ERROR
563563
}
564564
}
565565
}

compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/results/ResolutionStatus.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public enum ResolutionStatus {
2424
UNKNOWN_STATUS,
2525
UNSAFE_CALL_ERROR,
2626
WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR,
27+
UNSTABLE_SMARTCAST_ERROR,
2728
OTHER_ERROR,
2829
ARGUMENTS_MAPPING_ERROR,
2930
// '1.foo()' shouldn't be resolved to 'fun String.foo()'
@@ -40,6 +41,7 @@ public enum ResolutionStatus {
4041
public static final EnumSet<ResolutionStatus>[] SEVERITY_LEVELS = new EnumSet[] {
4142
EnumSet.of(UNSAFE_CALL_ERROR), // weakest
4243
EnumSet.of(WRONG_NUMBER_OF_TYPE_ARGUMENTS_ERROR),
44+
EnumSet.of(UNSTABLE_SMARTCAST_ERROR),
4345
EnumSet.of(OTHER_ERROR),
4446
EnumSet.of(ARGUMENTS_MAPPING_ERROR),
4547
EnumSet.of(RECEIVER_TYPE_ERROR),

compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2010-2016 JetBrains s.r.o.
2+
* Copyright 2010-2017 JetBrains s.r.o.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,8 +20,10 @@ import gnu.trove.THashSet
2020
import gnu.trove.TObjectHashingStrategy
2121
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
2222
import org.jetbrains.kotlin.descriptors.CallableDescriptor
23+
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
2324
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
2425
import org.jetbrains.kotlin.descriptors.Visibilities
26+
import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor
2527
import org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides
2628
import org.jetbrains.kotlin.resolve.OverridingUtil
2729
import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
@@ -70,7 +72,22 @@ class OverloadingConflictResolver<C : Any>(
7072
}
7173

7274
val noEquivalentCalls = filterOutEquivalentCalls(fixedCandidates)
73-
val noOverrides = OverridingUtil.filterOverrides(noEquivalentCalls) { it.resultingDescriptor }
75+
val noOverrides = OverridingUtil.filterOverrides(noEquivalentCalls) { a, b ->
76+
val aDescriptor = a.resultingDescriptor
77+
val bDescriptor = b.resultingDescriptor
78+
// Here we'd like to handle situation when we have two synthetic descriptors as in syntheticSAMExtensions.kt
79+
80+
// Without this, we'll pick all synthetic descriptors as they don't have overridden descriptors and
81+
// then report ambiguity, which isn't very convenient
82+
if (aDescriptor is SyntheticMemberDescriptor<*> && bDescriptor is SyntheticMemberDescriptor<*>) {
83+
val aBaseDescriptor = aDescriptor.baseDescriptorForSynthetic
84+
val bBaseDescriptor = bDescriptor.baseDescriptorForSynthetic
85+
if (aBaseDescriptor is CallableMemberDescriptor && bBaseDescriptor is CallableMemberDescriptor) {
86+
return@filterOverrides Pair(aBaseDescriptor, bBaseDescriptor)
87+
}
88+
}
89+
Pair(aDescriptor, bDescriptor)
90+
}
7491
if (noOverrides.size == 1) {
7592
return noOverrides
7693
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// !DIAGNOSTICS: -UNUSED_PARAMETER
2+
3+
class A {
4+
fun f(x: Boolean): Int = 0
5+
6+
fun f(y: String): Int = 0
7+
}
8+
9+
class B {
10+
private var a: A? = null
11+
12+
fun takeInt(i: Int) {}
13+
14+
fun f() {
15+
a = A()
16+
<!SMARTCAST_IMPOSSIBLE!>a<!>.f(true)
17+
takeInt(<!SMARTCAST_IMPOSSIBLE!>a<!>.f(""))
18+
a.<!NONE_APPLICABLE!>f<!>()
19+
}
20+
21+
fun g() {
22+
takeInt(if (3 > 2) {
23+
a = A()
24+
<!SMARTCAST_IMPOSSIBLE!>a<!>.f(true)
25+
} else {
26+
6
27+
})
28+
}
29+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package
2+
3+
public final class A {
4+
public constructor A()
5+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
6+
public final fun f(/*0*/ x: kotlin.Boolean): kotlin.Int
7+
public final fun f(/*0*/ y: kotlin.String): kotlin.Int
8+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
9+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
10+
}
11+
12+
public final class B {
13+
public constructor B()
14+
private final var a: A?
15+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
16+
public final fun f(): kotlin.Unit
17+
public final fun g(): kotlin.Unit
18+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
19+
public final fun takeInt(/*0*/ i: kotlin.Int): kotlin.Unit
20+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// !DIAGNOSTICS: -UNUSED_PARAMETER
2+
3+
interface Ctx
4+
class CtxImpl : Ctx {
5+
fun doJob(a: Int) {}
6+
fun doJob(s: String) {}
7+
}
8+
9+
open class Test(open val ctx: Ctx) {
10+
fun test() {
11+
when (ctx) {
12+
is CtxImpl -> <!SMARTCAST_IMPOSSIBLE!>ctx<!>.doJob(2)
13+
}
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package
2+
3+
public interface Ctx {
4+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
5+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
6+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
7+
}
8+
9+
public final class CtxImpl : Ctx {
10+
public constructor CtxImpl()
11+
public final fun doJob(/*0*/ a: kotlin.Int): kotlin.Unit
12+
public final fun doJob(/*0*/ s: kotlin.String): kotlin.Unit
13+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
14+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
15+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
16+
}
17+
18+
public open class Test {
19+
public constructor Test(/*0*/ ctx: Ctx)
20+
public open val ctx: Ctx
21+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
22+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
23+
public final fun test(): kotlin.Unit
24+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// !DIAGNOSTICS: -UNUSED_PARAMETER
2+
3+
class A
4+
class B
5+
6+
fun A.foo(a: A) {}
7+
fun A.foo(b: B) {}
8+
var a: A? = null
9+
10+
fun smartCastInterference(b: B) {
11+
if (a != null) {
12+
<!SMARTCAST_IMPOSSIBLE!>a<!>.foo(b)
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package
2+
3+
public var a: A?
4+
public fun smartCastInterference(/*0*/ b: B): kotlin.Unit
5+
public fun A.foo(/*0*/ a: A): kotlin.Unit
6+
public fun A.foo(/*0*/ b: B): kotlin.Unit
7+
8+
public final class A {
9+
public constructor A()
10+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
11+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
12+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
13+
}
14+
15+
public final class B {
16+
public constructor B()
17+
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
18+
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
19+
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
20+
}

compiler/testData/diagnostics/tests/scopes/protectedVisibility/syntheticSAMExtensions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class B : A() {
2020
}
2121

2222
if (d.x is B) {
23-
<!SMARTCAST_IMPOSSIBLE!>d.x<!>.foo <!TYPE_MISMATCH!>{}<!>
23+
<!SMARTCAST_IMPOSSIBLE!>d.x<!>.foo {}
2424
}
2525
}
2626
}

compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13912,6 +13912,12 @@ public void testNullableNothingIsExactlyNull() throws Exception {
1391213912
doTest(fileName);
1391313913
}
1391413914

13915+
@TestMetadata("nullableReceiverWithOverloadedMethod.kt")
13916+
public void testNullableReceiverWithOverloadedMethod() throws Exception {
13917+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/nullableReceiverWithOverloadedMethod.kt");
13918+
doTest(fileName);
13919+
}
13920+
1391513921
@TestMetadata("PreferExtensionsOnNullableReceiver.kt")
1391613922
public void testPreferExtensionsOnNullableReceiver() throws Exception {
1391713923
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/PreferExtensionsOnNullableReceiver.kt");
@@ -13959,6 +13965,18 @@ public void testUnnecessaryNotNullAssertion() throws Exception {
1395913965
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unnecessaryNotNullAssertion.kt");
1396013966
doTest(fileName);
1396113967
}
13968+
13969+
@TestMetadata("unstableSmartcastWhenOpenGetterWithOverloading.kt")
13970+
public void testUnstableSmartcastWhenOpenGetterWithOverloading() throws Exception {
13971+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.kt");
13972+
doTest(fileName);
13973+
}
13974+
13975+
@TestMetadata("unstableSmartcastWithOverloadedExtensions.kt")
13976+
public void testUnstableSmartcastWithOverloadedExtensions() throws Exception {
13977+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.kt");
13978+
doTest(fileName);
13979+
}
1396213980
}
1396313981

1396413982
@TestMetadata("compiler/testData/diagnostics/tests/nullableTypes")

core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2010-2016 JetBrains s.r.o.
2+
* Copyright 2010-2017 JetBrains s.r.o.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,9 +16,11 @@
1616

1717
package org.jetbrains.kotlin.resolve;
1818

19+
import kotlin.Pair;
1920
import kotlin.Unit;
2021
import kotlin.collections.CollectionsKt;
2122
import kotlin.jvm.functions.Function1;
23+
import kotlin.jvm.functions.Function2;
2224
import org.jetbrains.annotations.Mutable;
2325
import org.jetbrains.annotations.NotNull;
2426
import org.jetbrains.annotations.Nullable;
@@ -33,7 +35,6 @@
3335
import org.jetbrains.kotlin.types.TypeConstructor;
3436
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
3537
import org.jetbrains.kotlin.types.checker.KotlinTypeCheckerImpl;
36-
import org.jetbrains.kotlin.utils.FunctionsKt;
3738
import org.jetbrains.kotlin.utils.SmartSet;
3839

3940
import java.util.*;
@@ -72,23 +73,29 @@ private OverridingUtil(KotlinTypeChecker.TypeConstructorEquality axioms) {
7273
*/
7374
@NotNull
7475
public static <D extends CallableDescriptor> Set<D> filterOutOverridden(@NotNull Set<D> candidateSet) {
75-
return filterOverrides(candidateSet, FunctionsKt.<CallableDescriptor>identity());
76+
return filterOverrides(candidateSet, new Function2<D, D, Pair<CallableDescriptor, CallableDescriptor>>() {
77+
@Override
78+
public Pair<CallableDescriptor, CallableDescriptor> invoke(D a, D b) {
79+
return new Pair<CallableDescriptor, CallableDescriptor>(a, b);
80+
}
81+
});
7682
}
7783

7884
@NotNull
7985
public static <D> Set<D> filterOverrides(
8086
@NotNull Set<D> candidateSet,
81-
@NotNull Function1<? super D, ? extends CallableDescriptor> transform
87+
@NotNull Function2<? super D, ? super D, Pair<CallableDescriptor, CallableDescriptor>> transformFirst
8288
) {
8389
if (candidateSet.size() <= 1) return candidateSet;
8490

8591
Set<D> result = new LinkedHashSet<D>();
8692
outerLoop:
8793
for (D meD : candidateSet) {
88-
CallableDescriptor me = transform.invoke(meD);
8994
for (Iterator<D> iterator = result.iterator(); iterator.hasNext(); ) {
9095
D otherD = iterator.next();
91-
CallableDescriptor other = transform.invoke(otherD);
96+
Pair<CallableDescriptor, CallableDescriptor> meAndOther = transformFirst.invoke(meD, otherD);
97+
CallableDescriptor me = meAndOther.component1();
98+
CallableDescriptor other = meAndOther.component2();
9299
if (overrides(me, other)) {
93100
iterator.remove();
94101
}

0 commit comments

Comments
 (0)