Resolve dynamic.extensionForDynamic() statically

This commit is contained in:
Andrey Breslav
2014-11-24 18:00:20 +03:00
parent 8ee00b70ee
commit d5fdfcb797
12 changed files with 175 additions and 19 deletions
@@ -137,15 +137,24 @@ public class TaskPrioritizer {
}
private static <D extends CallableDescriptor, F extends D> void addCandidatesForExplicitReceiver(
@NotNull ReceiverValue explicitReceiver,
@NotNull Collection<ReceiverValue> implicitReceivers,
@NotNull TaskPrioritizerContext<D, F> c,
boolean isExplicit
) {
addMembers(explicitReceiver, c, /*static=*/false, isExplicit);
addCandidatesForDynamicReceiver(explicitReceiver, implicitReceivers, c, isExplicit);
addExtensionCandidates(explicitReceiver, implicitReceivers, c, isExplicit);
}
private static <D extends CallableDescriptor, F extends D> void addExtensionCandidates(
@NotNull final ReceiverValue explicitReceiver,
@NotNull Collection<ReceiverValue> implicitReceivers,
@NotNull final TaskPrioritizerContext<D, F> c,
final boolean isExplicit
) {
addMembers(explicitReceiver, c, /*static=*/false, isExplicit);
addCandidatesForDynamicReceiver(explicitReceiver, c);
for (final CallableDescriptorCollector<D> callableDescriptorCollector : c.callableDescriptorCollectors) {
//member extensions
for (ReceiverValue implicitReceiver : implicitReceivers) {
@@ -191,14 +200,21 @@ public class TaskPrioritizer {
}
private static <D extends CallableDescriptor, F extends D> void addCandidatesForDynamicReceiver(
@NotNull final ReceiverValue receiver,
@NotNull final TaskPrioritizerContext<D, F> c
@NotNull final ReceiverValue explicitReceiver,
@NotNull Collection<ReceiverValue> implicitReceivers,
@NotNull final TaskPrioritizerContext<D, F> c,
boolean isExplicit
) {
if (!TypesPackage.isDynamic(explicitReceiver.getType())) return;
TaskPrioritizerContext<D, F> onlyDynamicReceivers = c.replaceCollectors(TasksPackage.onlyDynamicReceivers(c.callableDescriptorCollectors));
addExtensionCandidates(explicitReceiver, implicitReceivers, onlyDynamicReceivers, isExplicit);
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(
@@ -211,7 +227,7 @@ public class TaskPrioritizer {
c.context.call,
dynamicDescriptor
);
dynamicCandidate.setDispatchReceiver(receiver);
dynamicCandidate.setDispatchReceiver(explicitReceiver);
dynamicCandidate.setExplicitReceiverKind(DISPATCH_RECEIVER);
return Collections.singletonList(dynamicCandidate);
@@ -473,5 +489,11 @@ public class TaskPrioritizer {
private TaskPrioritizerContext<D, F> replaceScope(JetScope newScope) {
return new TaskPrioritizerContext<D, F>(name, result, context, newScope, callableDescriptorCollectors);
}
private TaskPrioritizerContext<D, F> replaceCollectors(CallableDescriptorCollectors<D> newCollectors) {
return new TaskPrioritizerContext<D, F>(name, result, context, scope, newCollectors);
}
}
}
@@ -39,6 +39,10 @@ import org.jetbrains.jet.lang.types.JetType
import kotlin.platform.platformStatic
import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver
import org.jetbrains.jet.lang.types.isDynamic
import org.jetbrains.jet.lang.resolve.calls.tasks.collectors.CallableDescriptorCollector
import org.jetbrains.jet.lang.resolve.scopes.JetScope
import org.jetbrains.jet.lang.resolve.BindingTrace
import org.jetbrains.jet.lang.resolve.calls.tasks.collectors.CallableDescriptorCollectors
object DynamicCallableDescriptors {
@@ -132,3 +136,14 @@ fun DeclarationDescriptor.isDynamic(): Boolean {
return dispatchReceiverParameter != null && dispatchReceiverParameter.getType().isDynamic()
}
class CollectorForDynamicReceivers<D: CallableDescriptor>(val delegate: CallableDescriptorCollector<D>) : CallableDescriptorCollector<D> by delegate {
override fun getExtensionsByName(scope: JetScope, name: Name, bindingTrace: BindingTrace): Collection<D> {
return delegate.getExtensionsByName(scope, name, bindingTrace).filter {
it.getExtensionReceiverParameter()?.getType()?.isDynamic() ?: false
}
}
}
fun <D: CallableDescriptor> CallableDescriptorCollectors<D>.onlyDynamicReceivers(): CallableDescriptorCollectors<D> {
return CallableDescriptorCollectors(* this.map { CollectorForDynamicReceivers(it) }.copyToArray())
}
@@ -17,15 +17,19 @@ fun test(d: dynamic) {
d.<!DEBUG_INFO_DYNAMIC!>onStringVal<!> = 1
d.<!DEBUG_INFO_DYNAMIC!>onDynamicVal<!> = 1
d?.<!DEBUG_INFO_DYNAMIC!>onDynamicVal<!> = 1
<!VAL_REASSIGNMENT!>d.onDynamicVal<!> = 1
(d: String).onStringVal
(d: Any).onAnyVal
(d: Any?).onNullableAnyVal
(d: Any).onDynamicVal
}
fun testReassignmentWithSafeCall(d: dynamic) {
<!VAL_REASSIGNMENT!>d?.onDynamicVal<!> = 1
}
fun testReassignmentWithStaticCalls(d: dynamic) {
<!VAL_REASSIGNMENT!>(d: String).onStringVal<!> = 1
<!VAL_REASSIGNMENT!>(d: Any).onAnyVal<!> = 1
<!VAL_REASSIGNMENT!>(d: Any?).onNullableAnyVal<!> = 1
@@ -40,7 +44,10 @@ val dynamic.onDynamicVal: Int get() = 1
class C {
fun test(d: dynamic) {
d.<!DEBUG_INFO_DYNAMIC!>memberVal<!>
d.<!DEBUG_INFO_DYNAMIC!>memberVal<!> = 1
d.<!DEBUG_INFO_DYNAMIC!>memberExtensionVal<!>
d.<!DEBUG_INFO_DYNAMIC!>memberExtensionVal<!> = 1
}
val memberVal = 1
@@ -5,6 +5,8 @@ internal val dynamic.onDynamicVal: kotlin.Int
internal val kotlin.Any?.onNullableAnyVal: kotlin.Int
internal val kotlin.String.onStringVal: kotlin.Int
internal fun test(/*0*/ d: dynamic): kotlin.Unit
internal fun testReassignmentWithSafeCall(/*0*/ d: dynamic): kotlin.Unit
internal fun testReassignmentWithStaticCalls(/*0*/ d: dynamic): kotlin.Unit
internal final class C {
public constructor C()
@@ -17,9 +17,9 @@ fun test(d: dynamic) {
d.<!DEBUG_INFO_DYNAMIC!>onStringVar<!> = 1
d.<!DEBUG_INFO_DYNAMIC!>onDynamicVar<!> = 1
d.onDynamicVar = 1
d?.<!DEBUG_INFO_DYNAMIC!>onDynamicVar<!> = 1
d?.onDynamicVar = 1
(d: String).onStringVar
(d: Any).onAnyVar
@@ -13,8 +13,8 @@ fun test(d: dynamic) {
d.<!DEBUG_INFO_DYNAMIC!>onNullableAny<!>()
d.<!DEBUG_INFO_DYNAMIC!>onString<!>()
d.<!DEBUG_INFO_DYNAMIC!>onDynamic<!>()
d?.<!DEBUG_INFO_DYNAMIC!>onDynamic<!>()
d.onDynamic()
d?.onDynamic()
(d: String).onString()
(d: Any).onAny()
@@ -0,0 +1,61 @@
// !MARK_DYNAMIC_CALLS
// MODULE[js]: m1
// FILE: k.kt
fun test(d: dynamic) {
d.onDynamic()
d.onNullableDynamic()
d.valOnDynamic
<!VAL_REASSIGNMENT!>d.valOnDynamic<!> = 1
d.varOnDynamic
d.varOnDynamic = 1
}
fun dynamic.extTest() {
onDynamic()
onNullableDynamic()
valOnDynamic
<!VAL_REASSIGNMENT!>valOnDynamic<!> = 1
varOnDynamic
varOnDynamic = 1
this.onDynamic()
this.onNullableDynamic()
this.valOnDynamic
this.valOnDynamic = 1
this.varOnDynamic
this.varOnDynamic = 1
}
fun dynamic.onDynamic() {}
fun dynamic<!REDUNDANT_NULLABLE!>?<!>.onNullableDynamic() {}
val dynamic.valOnDynamic: Int get() = 1
var dynamic.varOnDynamic: Int
get() = 1
set(v) {}
class ForMemberExtensions {
fun test(d: dynamic) {
d.memberExtensionVar
d.memberExtensionVar = 1
d.memberExtensionVal
<!VAL_REASSIGNMENT!>d.memberExtensionVal<!> = 1
}
val dynamic.memberExtensionVal: Int get() = 1
var dynamic.memberExtensionVar: Int
get() = 1
set(v) {}
}
@@ -0,0 +1,18 @@
package
internal val dynamic.valOnDynamic: kotlin.Int
internal var dynamic.varOnDynamic: kotlin.Int
internal fun test(/*0*/ d: dynamic): kotlin.Unit
internal fun dynamic.extTest(): kotlin.Unit
internal fun dynamic.onDynamic(): kotlin.Unit
internal fun dynamic.onNullableDynamic(): kotlin.Unit
internal final class ForMemberExtensions {
public constructor ForMemberExtensions()
internal final val dynamic.memberExtensionVal: kotlin.Int
internal final var dynamic.memberExtensionVar: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
internal final fun test(/*0*/ d: dynamic): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,14 @@
// !MARK_DYNAMIC_CALLS
// !DIAGNOSTICS: -UNUSED_PARAMETER
// MODULE[js]: m1
// FILE: k.kt
fun dynamic.foo(s: String, a: Any) {}
fun dynamic.foo(s: Any, a: String) {}
fun test(d: dynamic) {
d.foo(1, "")
d.<!OVERLOAD_RESOLUTION_AMBIGUITY!>foo<!>("", "")
d.<!DEBUG_INFO_DYNAMIC!>foo<!>(1, 1)
}
@@ -0,0 +1,5 @@
package
internal fun test(/*0*/ d: dynamic): kotlin.Unit
internal fun dynamic.foo(/*0*/ s: kotlin.Any, /*1*/ a: kotlin.String): kotlin.Unit
internal fun dynamic.foo(/*0*/ s: kotlin.String, /*1*/ a: kotlin.Any): kotlin.Unit
@@ -3782,6 +3782,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("extensionsToDynamic.kt")
public void testExtensionsToDynamic() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/extensionsToDynamic.kt");
doTest(fileName);
}
@TestMetadata("implicitDynamicReceiver.kt")
public void testImplicitDynamicReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.kt");
@@ -3818,6 +3824,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("overloadingAmbiguity.kt")
public void testOverloadingAmbiguity() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/overloadingAmbiguity.kt");
doTest(fileName);
}
@TestMetadata("overrides.kt")
public void testOverrides() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/overrides.kt");
+4 -4
View File
@@ -63,13 +63,13 @@ Internally, `dynamic` is represented as a flexible type `Nothing..Any?`, with th
## 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`)
- If a receiver is `dynamic` a call is resolved as dynamic if no members (these are members of `Any`, unless we implement bounded `dynamic`)
and no extensions with dynamic receivers match the signature.
- 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
- This means that an extension to a normal, non-dynamic type **can not** be called on a `dynamic` receiver.
If needed, one can force a call to an extension by casting the receiver to a static type: `(d as Foo).bar()`
## Type Argument Inference