Dynamic calls have priority over extensions

This commit is contained in:
Andrey Breslav
2014-11-14 19:07:08 +03:00
parent f58b30706e
commit 52f1e037c1
9 changed files with 99 additions and 43 deletions
@@ -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");
+10
View File
@@ -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.