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( 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 final ReceiverValue explicitReceiver,
@NotNull Collection<ReceiverValue> implicitReceivers, @NotNull Collection<ReceiverValue> implicitReceivers,
@NotNull final TaskPrioritizerContext<D, F> c, @NotNull final TaskPrioritizerContext<D, F> c,
final boolean isExplicit final boolean isExplicit
) { ) {
addMembers(explicitReceiver, c, /*static=*/false, isExplicit);
addCandidatesForDynamicReceiver(explicitReceiver, c);
for (final CallableDescriptorCollector<D> callableDescriptorCollector : c.callableDescriptorCollectors) { for (final CallableDescriptorCollector<D> callableDescriptorCollector : c.callableDescriptorCollectors) {
//member extensions //member extensions
for (ReceiverValue implicitReceiver : implicitReceivers) { for (ReceiverValue implicitReceiver : implicitReceivers) {
@@ -191,14 +200,21 @@ public class TaskPrioritizer {
} }
private static <D extends CallableDescriptor, F extends D> void addCandidatesForDynamicReceiver( private static <D extends CallableDescriptor, F extends D> void addCandidatesForDynamicReceiver(
@NotNull final ReceiverValue receiver, @NotNull final ReceiverValue explicitReceiver,
@NotNull final TaskPrioritizerContext<D, F> c @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( c.result.addCandidates(
new Function0<Collection<? extends ResolutionCandidate<D>>>() { new Function0<Collection<? extends ResolutionCandidate<D>>>() {
@Override @Override
public Collection<? extends ResolutionCandidate<D>> invoke() { public Collection<? extends ResolutionCandidate<D>> invoke() {
if (!TypesPackage.isDynamic(receiver.getType())) return Collections.emptyList();
//noinspection unchecked //noinspection unchecked
D dynamicDescriptor = (D) DynamicCallableDescriptors.createCallableDescriptorForDynamicCall( D dynamicDescriptor = (D) DynamicCallableDescriptors.createCallableDescriptorForDynamicCall(
@@ -211,7 +227,7 @@ public class TaskPrioritizer {
c.context.call, c.context.call,
dynamicDescriptor dynamicDescriptor
); );
dynamicCandidate.setDispatchReceiver(receiver); dynamicCandidate.setDispatchReceiver(explicitReceiver);
dynamicCandidate.setExplicitReceiverKind(DISPATCH_RECEIVER); dynamicCandidate.setExplicitReceiverKind(DISPATCH_RECEIVER);
return Collections.singletonList(dynamicCandidate); return Collections.singletonList(dynamicCandidate);
@@ -473,5 +489,11 @@ public class TaskPrioritizer {
private TaskPrioritizerContext<D, F> replaceScope(JetScope newScope) { private TaskPrioritizerContext<D, F> replaceScope(JetScope newScope) {
return new TaskPrioritizerContext<D, F>(name, result, context, newScope, callableDescriptorCollectors); 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 kotlin.platform.platformStatic
import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver
import org.jetbrains.jet.lang.types.isDynamic 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 { object DynamicCallableDescriptors {
@@ -132,3 +136,14 @@ fun DeclarationDescriptor.isDynamic(): Boolean {
return dispatchReceiverParameter != null && dispatchReceiverParameter.getType().isDynamic() 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!>onStringVal<!> = 1
d.<!DEBUG_INFO_DYNAMIC!>onDynamicVal<!> = 1 <!VAL_REASSIGNMENT!>d.onDynamicVal<!> = 1
d?.<!DEBUG_INFO_DYNAMIC!>onDynamicVal<!> = 1
(d: String).onStringVal (d: String).onStringVal
(d: Any).onAnyVal (d: Any).onAnyVal
(d: Any?).onNullableAnyVal (d: Any?).onNullableAnyVal
(d: Any).onDynamicVal (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: String).onStringVal<!> = 1
<!VAL_REASSIGNMENT!>(d: Any).onAnyVal<!> = 1 <!VAL_REASSIGNMENT!>(d: Any).onAnyVal<!> = 1
<!VAL_REASSIGNMENT!>(d: Any?).onNullableAnyVal<!> = 1 <!VAL_REASSIGNMENT!>(d: Any?).onNullableAnyVal<!> = 1
@@ -40,7 +44,10 @@ val dynamic.onDynamicVal: Int get() = 1
class C { class C {
fun test(d: dynamic) { fun test(d: dynamic) {
d.<!DEBUG_INFO_DYNAMIC!>memberVal<!> d.<!DEBUG_INFO_DYNAMIC!>memberVal<!>
d.<!DEBUG_INFO_DYNAMIC!>memberVal<!> = 1
d.<!DEBUG_INFO_DYNAMIC!>memberExtensionVal<!> d.<!DEBUG_INFO_DYNAMIC!>memberExtensionVal<!>
d.<!DEBUG_INFO_DYNAMIC!>memberExtensionVal<!> = 1
} }
val memberVal = 1 val memberVal = 1
@@ -5,6 +5,8 @@ internal val dynamic.onDynamicVal: kotlin.Int
internal val kotlin.Any?.onNullableAnyVal: kotlin.Int internal val kotlin.Any?.onNullableAnyVal: kotlin.Int
internal val kotlin.String.onStringVal: kotlin.Int internal val kotlin.String.onStringVal: kotlin.Int
internal fun test(/*0*/ d: dynamic): kotlin.Unit 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 { internal final class C {
public constructor C() public constructor C()
@@ -17,9 +17,9 @@ fun test(d: dynamic) {
d.<!DEBUG_INFO_DYNAMIC!>onStringVar<!> = 1 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: String).onStringVar
(d: Any).onAnyVar (d: Any).onAnyVar
@@ -13,8 +13,8 @@ fun test(d: dynamic) {
d.<!DEBUG_INFO_DYNAMIC!>onNullableAny<!>() d.<!DEBUG_INFO_DYNAMIC!>onNullableAny<!>()
d.<!DEBUG_INFO_DYNAMIC!>onString<!>() d.<!DEBUG_INFO_DYNAMIC!>onString<!>()
d.<!DEBUG_INFO_DYNAMIC!>onDynamic<!>() d.onDynamic()
d?.<!DEBUG_INFO_DYNAMIC!>onDynamic<!>() d?.onDynamic()
(d: String).onString() (d: String).onString()
(d: Any).onAny() (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); 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") @TestMetadata("implicitDynamicReceiver.kt")
public void testImplicitDynamicReceiver() throws Exception { public void testImplicitDynamicReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.kt"); String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.kt");
@@ -3818,6 +3824,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName); 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") @TestMetadata("overrides.kt")
public void testOverrides() throws Exception { public void testOverrides() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/overrides.kt"); 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 ## 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 - 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 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. 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 - This means that an extension to a normal, non-dynamic type **can not** be called on a `dynamic` receiver.
teh receiver to a static type: `(d as Foo).bar()` If needed, one can force a call to an extension by casting the 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 ## Type Argument Inference