Dynamic calls have priority over extensions
This commit is contained in:
+25
-17
@@ -131,7 +131,6 @@ public class TaskPrioritizer {
|
||||
if (receiver.exists()) {
|
||||
addCandidatesForExplicitReceiver(receiver, implicitReceivers, c, /*isExplicit=*/true);
|
||||
addMembers(receiver, c, /*static=*/true, /*isExplicit=*/true);
|
||||
addCandidatesForDynamicReceiver(receiver, c);
|
||||
return;
|
||||
}
|
||||
addCandidatesForNoReceiver(implicitReceivers, c);
|
||||
@@ -145,6 +144,8 @@ public class TaskPrioritizer {
|
||||
) {
|
||||
addMembers(explicitReceiver, c, /*static=*/false, isExplicit);
|
||||
|
||||
addCandidatesForDynamicReceiver(explicitReceiver, c);
|
||||
|
||||
for (final CallableDescriptorCollector<D> callableDescriptorCollector : c.callableDescriptorCollectors) {
|
||||
//member extensions
|
||||
for (ReceiverValue implicitReceiver : implicitReceivers) {
|
||||
@@ -190,25 +191,33 @@ public class TaskPrioritizer {
|
||||
}
|
||||
|
||||
private static <D extends CallableDescriptor, F extends D> void addCandidatesForDynamicReceiver(
|
||||
@NotNull ReceiverValue receiver,
|
||||
@NotNull TaskPrioritizerContext<D, F> c
|
||||
@NotNull final ReceiverValue receiver,
|
||||
@NotNull final TaskPrioritizerContext<D, F> c
|
||||
) {
|
||||
if (!TypesPackage.isDynamic(receiver.getType())) return;
|
||||
c.result.addCandidates(
|
||||
new Function0<Collection<? extends ResolutionCandidate<D>>>() {
|
||||
@Override
|
||||
public Collection<? extends ResolutionCandidate<D>> invoke() {
|
||||
if (!TypesPackage.isDynamic(receiver.getType())) return Collections.emptyList();
|
||||
|
||||
//noinspection unchecked
|
||||
D dynamicDescriptor = (D) DynamicCallableDescriptors.createCallableDescriptorForDynamicCall(
|
||||
c.context.call,
|
||||
c.scope.getContainingDeclaration()
|
||||
);
|
||||
if (dynamicDescriptor == null) return;
|
||||
//noinspection unchecked
|
||||
D dynamicDescriptor = (D) DynamicCallableDescriptors.createCallableDescriptorForDynamicCall(
|
||||
c.context.call,
|
||||
c.scope.getContainingDeclaration()
|
||||
);
|
||||
if (dynamicDescriptor == null) return Collections.emptyList();
|
||||
|
||||
ResolutionCandidate<D> dynamicCandidate = ResolutionCandidate.create(
|
||||
c.context.call,
|
||||
dynamicDescriptor
|
||||
ResolutionCandidate<D> dynamicCandidate = ResolutionCandidate.create(
|
||||
c.context.call,
|
||||
dynamicDescriptor
|
||||
);
|
||||
dynamicCandidate.setDispatchReceiver(receiver);
|
||||
dynamicCandidate.setExplicitReceiverKind(DISPATCH_RECEIVER);
|
||||
|
||||
return Collections.singletonList(dynamicCandidate);
|
||||
}
|
||||
}
|
||||
);
|
||||
dynamicCandidate.setDispatchReceiver(receiver);
|
||||
dynamicCandidate.setExplicitReceiverKind(DISPATCH_RECEIVER);
|
||||
c.result.addCandidates(Collections.singletonList(dynamicCandidate));
|
||||
}
|
||||
|
||||
private static ExplicitReceiverKind createKind(ExplicitReceiverKind kind, boolean isExplicit) {
|
||||
@@ -260,7 +269,6 @@ public class TaskPrioritizer {
|
||||
//try all implicit receivers as explicit
|
||||
for (ReceiverValue implicitReceiver : implicitReceivers) {
|
||||
addCandidatesForExplicitReceiver(implicitReceiver, implicitReceivers, c, /*isExplicit=*/false);
|
||||
addCandidatesForDynamicReceiver(implicitReceiver, c);
|
||||
}
|
||||
|
||||
//nonlocals
|
||||
|
||||
@@ -4,33 +4,33 @@
|
||||
// FILE: k.kt
|
||||
|
||||
fun test(d: dynamic) {
|
||||
d.foo()
|
||||
d.<!DEBUG_INFO_DYNAMIC!>foo<!>(1)
|
||||
d.<!DEBUG_INFO_DYNAMIC!>onAny<!>()
|
||||
d?.<!DEBUG_INFO_DYNAMIC!>onAny<!>()
|
||||
d!!.<!DEBUG_INFO_DYNAMIC!>onAny<!>()
|
||||
|
||||
d.bar()
|
||||
d.baz()
|
||||
d.<!DEBUG_INFO_DYNAMIC!>onAny<!>(1)
|
||||
|
||||
d == 1
|
||||
d.<!DEBUG_INFO_DYNAMIC!>onNullableAny<!>()
|
||||
d.<!DEBUG_INFO_DYNAMIC!>onString<!>()
|
||||
|
||||
d.equals(1)
|
||||
d?.equals(1)
|
||||
d.<!DEBUG_INFO_DYNAMIC!>onDynamic<!>()
|
||||
d?.<!DEBUG_INFO_DYNAMIC!>onDynamic<!>()
|
||||
|
||||
d.hashCode()
|
||||
d?.hashCode()
|
||||
|
||||
d.toString()
|
||||
d?.toString()
|
||||
(d: String).onString()
|
||||
(d: Any).onAny()
|
||||
(d: Any?).onNullableAny()
|
||||
(d: Any).onDynamic()
|
||||
}
|
||||
|
||||
fun Any.foo() {}
|
||||
fun Any?.bar() {}
|
||||
|
||||
fun String.baz() {}
|
||||
fun Any.onAny() {}
|
||||
fun Any?.onNullableAny() {}
|
||||
fun String.onString() {}
|
||||
fun dynamic.onDynamic() {}
|
||||
|
||||
class C {
|
||||
fun test(d: dynamic) {
|
||||
d.<!DEBUG_INFO_DYNAMIC!>member<!>()
|
||||
d.memberExtension()
|
||||
d.<!DEBUG_INFO_DYNAMIC!>memberExtension<!>()
|
||||
}
|
||||
|
||||
fun member() {}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package
|
||||
|
||||
internal fun test(/*0*/ d: dynamic): kotlin.Unit
|
||||
internal fun kotlin.Any?.bar(): kotlin.Unit
|
||||
internal fun kotlin.String.baz(): kotlin.Unit
|
||||
internal fun kotlin.Any.foo(): kotlin.Unit
|
||||
internal fun kotlin.Any.onAny(): kotlin.Unit
|
||||
internal fun dynamic.onDynamic(): kotlin.Unit
|
||||
internal fun kotlin.Any?.onNullableAny(): kotlin.Unit
|
||||
internal fun kotlin.String.onString(): kotlin.Unit
|
||||
|
||||
internal final class C {
|
||||
public constructor C()
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// !MARK_DYNAMIC_CALLS
|
||||
|
||||
// MODULE[js]: m1
|
||||
// FILE: k.kt
|
||||
|
||||
fun test(d: dynamic) {
|
||||
d == 1
|
||||
|
||||
d.equals(1)
|
||||
d?.equals(1)
|
||||
d!!.equals(1)
|
||||
|
||||
d.hashCode()
|
||||
d?.hashCode()
|
||||
d!!.hashCode()
|
||||
|
||||
d.toString()
|
||||
d?.toString()
|
||||
d!!.toString()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
internal fun test(/*0*/ d: dynamic): kotlin.Unit
|
||||
@@ -1,11 +1,19 @@
|
||||
// !CHECK_TYPE
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
// MODULE[js]: m1
|
||||
// FILE: k.kt
|
||||
|
||||
fun foo(dn: dynamic<!REDUNDANT_NULLABLE!>?<!>, d: dynamic, dnn: dynamic<!REDUNDANT_NULLABLE!>?<!><!REDUNDANT_NULLABLE!>?<!>) {
|
||||
dn.checkType { it : _<dynamic>}
|
||||
dn.checkType { it : _<dynamic<!REDUNDANT_NULLABLE!>?<!>>}
|
||||
d.checkType { it : _<dynamic>}
|
||||
d.checkType { it : _<dynamic<!REDUNDANT_NULLABLE!>?<!>>}
|
||||
val a1 = dn.foo()
|
||||
a1.isDynamic()
|
||||
|
||||
val a2 = dn?.foo()
|
||||
a2.isDynamic()
|
||||
|
||||
val a3 = dn!!.foo()
|
||||
a3.isDynamic()
|
||||
|
||||
d.foo()
|
||||
d?.foo()
|
||||
d!!.foo()
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
package
|
||||
|
||||
internal fun foo(/*0*/ dn: dynamic, /*1*/ d: dynamic): kotlin.Unit
|
||||
internal fun foo(/*0*/ dn: dynamic, /*1*/ d: dynamic, /*2*/ dnn: dynamic): kotlin.Unit
|
||||
|
||||
@@ -3758,6 +3758,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("membersOfAny.kt")
|
||||
public void testMembersOfAny() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/membersOfAny.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("namedArguments.kt")
|
||||
public void testNamedArguments() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/namedArguments.kt");
|
||||
|
||||
@@ -61,6 +61,16 @@ Internally, `dynamic` is represented as a flexible type `Nothing..Any?`, with th
|
||||
|
||||
`Nothing` being mentioned, there's a risk of taking `dynamic` for a bottom type in some contexts, this is not intended and should be tested carefully.
|
||||
|
||||
## Resolution rules
|
||||
|
||||
- If a receiver is `dynamic` a call is resolved as dynamic if no members match the signature (these are members of `Any`, unless we implement bounded `dynamic`)
|
||||
- Motivation: otherwise, **any** extension to **any** type that simply happens to be in scope and match the name and arguments
|
||||
will be bound for a call with a `dynamic` receiver, i.e. there's no way to force a call to be dynamic, and in the case of a `*`-import
|
||||
the code may change its semantics just because somebody added some extension in another file.
|
||||
- This means that an extension **can not** be called on a `dynamic` receiver. If needed, one can force a call to an extension by casting
|
||||
teh receiver to a static type: `(d as Foo).bar()`
|
||||
- This also means that an extension whose receiver type is `dynamic` can not be called on a `dynamic` variable without a cast
|
||||
|
||||
## Type Argument Inference
|
||||
|
||||
When expected type of a call is `dynamic`, it does not automatically provide type arguments for nested calls.
|
||||
|
||||
Reference in New Issue
Block a user