From 52f1e037c11db70253877adea3254d389ab3b77c Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Fri, 14 Nov 2014 19:07:08 +0300 Subject: [PATCH] Dynamic calls have priority over extensions --- .../resolve/calls/tasks/TaskPrioritizer.java | 42 +++++++++++-------- .../tests/dynamicTypes/extensions.kt | 34 +++++++-------- .../tests/dynamicTypes/extensions.txt | 7 ++-- .../tests/dynamicTypes/membersOfAny.kt | 20 +++++++++ .../tests/dynamicTypes/membersOfAny.txt | 3 ++ .../tests/dynamicTypes/nullable.kt | 18 +++++--- .../tests/dynamicTypes/nullable.txt | 2 +- .../checkers/JetDiagnosticsTestGenerated.java | 6 +++ spec-docs/dynamic-types.md | 10 +++++ 9 files changed, 99 insertions(+), 43 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/dynamicTypes/membersOfAny.kt create mode 100644 compiler/testData/diagnostics/tests/dynamicTypes/membersOfAny.txt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TaskPrioritizer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TaskPrioritizer.java index 955b568ec0e..10f7dc83c53 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TaskPrioritizer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/TaskPrioritizer.java @@ -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 callableDescriptorCollector : c.callableDescriptorCollectors) { //member extensions for (ReceiverValue implicitReceiver : implicitReceivers) { @@ -190,25 +191,33 @@ public class TaskPrioritizer { } private static void addCandidatesForDynamicReceiver( - @NotNull ReceiverValue receiver, - @NotNull TaskPrioritizerContext c + @NotNull final ReceiverValue receiver, + @NotNull final TaskPrioritizerContext c ) { - if (!TypesPackage.isDynamic(receiver.getType())) return; + c.result.addCandidates( + new Function0>>() { + @Override + public Collection> 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 dynamicCandidate = ResolutionCandidate.create( - c.context.call, - dynamicDescriptor + ResolutionCandidate 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 diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/extensions.kt b/compiler/testData/diagnostics/tests/dynamicTypes/extensions.kt index 6ea31c946bf..797f2f65d42 100644 --- a/compiler/testData/diagnostics/tests/dynamicTypes/extensions.kt +++ b/compiler/testData/diagnostics/tests/dynamicTypes/extensions.kt @@ -4,33 +4,33 @@ // FILE: k.kt fun test(d: dynamic) { - d.foo() - d.foo(1) + d.onAny() + d?.onAny() + d!!.onAny() - d.bar() - d.baz() + d.onAny(1) - d == 1 + d.onNullableAny() + d.onString() - d.equals(1) - d?.equals(1) + d.onDynamic() + d?.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.member() - d.memberExtension() + d.memberExtension() } fun member() {} diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/extensions.txt b/compiler/testData/diagnostics/tests/dynamicTypes/extensions.txt index 2ba12ae16d3..be6045ec01d 100644 --- a/compiler/testData/diagnostics/tests/dynamicTypes/extensions.txt +++ b/compiler/testData/diagnostics/tests/dynamicTypes/extensions.txt @@ -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() diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/membersOfAny.kt b/compiler/testData/diagnostics/tests/dynamicTypes/membersOfAny.kt new file mode 100644 index 00000000000..bf86b574495 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dynamicTypes/membersOfAny.kt @@ -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() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/membersOfAny.txt b/compiler/testData/diagnostics/tests/dynamicTypes/membersOfAny.txt new file mode 100644 index 00000000000..41a648f7f82 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dynamicTypes/membersOfAny.txt @@ -0,0 +1,3 @@ +package + +internal fun test(/*0*/ d: dynamic): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/nullable.kt b/compiler/testData/diagnostics/tests/dynamicTypes/nullable.kt index c4f12537e2b..a91f9b2286f 100644 --- a/compiler/testData/diagnostics/tests/dynamicTypes/nullable.kt +++ b/compiler/testData/diagnostics/tests/dynamicTypes/nullable.kt @@ -1,11 +1,19 @@ -// !CHECK_TYPE +// !DIAGNOSTICS: -UNUSED_PARAMETER // MODULE[js]: m1 // FILE: k.kt fun foo(dn: dynamic?, d: dynamic, dnn: dynamic??) { - dn.checkType { it : _} - dn.checkType { it : _?>} - d.checkType { it : _} - d.checkType { it : _?>} + val a1 = dn.foo() + a1.isDynamic() + + val a2 = dn?.foo() + a2.isDynamic() + + val a3 = dn!!.foo() + a3.isDynamic() + + d.foo() + d?.foo() + d!!.foo() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/nullable.txt b/compiler/testData/diagnostics/tests/dynamicTypes/nullable.txt index 70883988245..524da3dd2a7 100644 --- a/compiler/testData/diagnostics/tests/dynamicTypes/nullable.txt +++ b/compiler/testData/diagnostics/tests/dynamicTypes/nullable.txt @@ -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 diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 36966474466..949dcbc043f 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -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"); diff --git a/spec-docs/dynamic-types.md b/spec-docs/dynamic-types.md index ab880b012fa..7fea4286fe3 100644 --- a/spec-docs/dynamic-types.md +++ b/spec-docs/dynamic-types.md @@ -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.