From 845f30975d7f724aa2a1249b5dee2c669f7f2cf3 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 13 Nov 2014 02:50:03 +0200 Subject: [PATCH] Basic support for dynamic calls --- .../resolve/calls/tasks/TaskPrioritizer.java | 26 ++++ .../lang/resolve/calls/tasks/dynamicCalls.kt | 127 ++++++++++++++++++ .../tests/dynamicTypes/dynamicCalls.kt | 21 +++ .../tests/dynamicTypes/dynamicCalls.txt | 3 + .../dynamicTypes/implicitDynamicReceiver.kt | 21 +++ .../dynamicTypes/implicitDynamicReceiver.txt | 3 + .../checkers/JetDiagnosticsTestGenerated.java | 12 ++ 7 files changed, 213 insertions(+) create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/dynamicCalls.kt create mode 100644 compiler/testData/diagnostics/tests/dynamicTypes/dynamicCalls.kt create mode 100644 compiler/testData/diagnostics/tests/dynamicTypes/dynamicCalls.txt create mode 100644 compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.kt create mode 100644 compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.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 07efa7445a5..955b568ec0e 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 @@ -38,11 +38,13 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.QualifierReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.jet.lang.types.ErrorUtils; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypesPackage; import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils; import org.jetbrains.jet.storage.StorageManager; import java.util.Collection; +import java.util.Collections; import java.util.List; import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.isOrOverridesSynthesized; @@ -129,6 +131,7 @@ 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); @@ -186,6 +189,28 @@ public class TaskPrioritizer { } } + private static void addCandidatesForDynamicReceiver( + @NotNull ReceiverValue receiver, + @NotNull TaskPrioritizerContext c + ) { + if (!TypesPackage.isDynamic(receiver.getType())) return; + + //noinspection unchecked + D dynamicDescriptor = (D) DynamicCallableDescriptors.createCallableDescriptorForDynamicCall( + c.context.call, + c.scope.getContainingDeclaration() + ); + if (dynamicDescriptor == null) return; + + ResolutionCandidate dynamicCandidate = ResolutionCandidate.create( + c.context.call, + dynamicDescriptor + ); + dynamicCandidate.setDispatchReceiver(receiver); + dynamicCandidate.setExplicitReceiverKind(DISPATCH_RECEIVER); + c.result.addCandidates(Collections.singletonList(dynamicCandidate)); + } + private static ExplicitReceiverKind createKind(ExplicitReceiverKind kind, boolean isExplicit) { if (isExplicit) return kind; return ExplicitReceiverKind.NO_EXPLICIT_RECEIVER; @@ -235,6 +260,7 @@ 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/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/dynamicCalls.kt b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/dynamicCalls.kt new file mode 100644 index 00000000000..65174f7dcc7 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/dynamicCalls.kt @@ -0,0 +1,127 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.lang.resolve.calls.tasks + +import org.jetbrains.jet.lang.psi.Call +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression +import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl +import org.jetbrains.jet.lang.descriptors.annotations.Annotations +import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor +import org.jetbrains.jet.lang.descriptors.SourceElement +import org.jetbrains.jet.lang.descriptors.impl.ReceiverParameterDescriptorImpl +import org.jetbrains.jet.lang.types.DynamicType +import org.jetbrains.jet.lang.descriptors.Modality +import org.jetbrains.jet.lang.descriptors.Visibilities +import org.jetbrains.jet.lang.descriptors.CallableDescriptor +import org.jetbrains.jet.lang.descriptors.impl.PropertyDescriptorImpl +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor +import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl +import org.jetbrains.jet.lang.types.Variance +import org.jetbrains.jet.lang.resolve.name.Name +import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor +import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.jet.lang.types.JetType +import kotlin.platform.platformStatic +import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver + +object DynamicCallableDescriptors { + + platformStatic fun createCallableDescriptorForDynamicCall(call: Call, owner: DeclarationDescriptor): CallableDescriptor? { + val callee = call.getCalleeExpression() + if (callee !is JetSimpleNameExpression) return null + val name = callee.getReferencedNameAsName() + + return if (call.getValueArgumentList() == null && call.getValueArguments().isEmpty()) { + val propertyDescriptor = PropertyDescriptorImpl.create( + owner, + Annotations.EMPTY, + Modality.FINAL, + Visibilities.PUBLIC, + true, + name, + CallableMemberDescriptor.Kind.DECLARATION, + SourceElement.NO_SOURCE + ) + propertyDescriptor.setType( + DynamicType, + createTypeParameters(propertyDescriptor, call), + createDynamicDispatchReceiverParameter(propertyDescriptor), + null: JetType? + ) + + propertyDescriptor + } + else { + val functionDescriptor = SimpleFunctionDescriptorImpl.create( + owner, + Annotations.EMPTY, + name, + CallableMemberDescriptor.Kind.DECLARATION, + SourceElement.NO_SOURCE + ) + functionDescriptor.initialize( + null, + createDynamicDispatchReceiverParameter(functionDescriptor), + createTypeParameters(functionDescriptor, call), + createValueParameters(functionDescriptor, call), + DynamicType, + Modality.FINAL, + Visibilities.PUBLIC + ) + functionDescriptor + } + } + + private fun createDynamicDispatchReceiverParameter(owner: CallableDescriptor): ReceiverParameterDescriptorImpl { + return ReceiverParameterDescriptorImpl( + owner, + DynamicType, + TransientReceiver(DynamicType) + ) + } + + private fun createTypeParameters(owner: DeclarationDescriptor, call: Call): List = call.getTypeArguments().indices.map { + index + -> + TypeParameterDescriptorImpl.createWithDefaultBound( + owner, + Annotations.EMPTY, + false, + Variance.INVARIANT, + Name.identifier("T$index"), + index + ) + } + + private fun createValueParameters(owner: DeclarationDescriptor, call: Call): List = + call.getValueArguments().indices.map { + index + -> + ValueParameterDescriptorImpl( + owner, + null, + index, + Annotations.EMPTY, + Name.identifier("p$index"), + DynamicType, + false, + null, + SourceElement.NO_SOURCE + ) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCalls.kt b/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCalls.kt new file mode 100644 index 00000000000..3b190301e5e --- /dev/null +++ b/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCalls.kt @@ -0,0 +1,21 @@ +// MODULE[js]: m1 +// FILE: k.kt + +fun test(d: dynamic) { + val v1 = d.foo() + v1.isDynamic() // to check that anything is resolvable + + val v2 = d.foo(1) + v2.isDynamic() // to check that anything is resolvable + + val v3 = d.foo(1, "") + v3.isDynamic() // to check that anything is resolvable + +// val v4 = d.foo() +// v4.isDynamic() // to check that anything is resolvable + + val v5 = d.foo + v5.isDynamic() // to check that anything is resolvable + + d.foo = 1 +} diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCalls.txt b/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCalls.txt new file mode 100644 index 00000000000..41a648f7f82 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCalls.txt @@ -0,0 +1,3 @@ +package + +internal fun test(/*0*/ d: dynamic): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.kt b/compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.kt new file mode 100644 index 00000000000..8fd422bb026 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.kt @@ -0,0 +1,21 @@ +// MODULE[js]: m1 +// FILE: k.kt + +fun (dynamic).test() { + val v1 = foo() + v1.isDynamic() // to check that anything is resolvable + + val v2 = foo(1) + v2.isDynamic() // to check that anything is resolvable + + val v3 = foo(1, "") + v3.isDynamic() // to check that anything is resolvable + +// val v4 = foo() +// v4.isDynamic() // to check that anything is resolvable + + val v5 = foo + v5.isDynamic() // to check that anything is resolvable + + foo = 1 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.txt b/compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.txt new file mode 100644 index 00000000000..70f5609daca --- /dev/null +++ b/compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.txt @@ -0,0 +1,3 @@ +package + +internal fun dynamic.test(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 81f94e5bdd4..e8b0e31d167 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -3722,6 +3722,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("dynamicCalls.kt") + public void testDynamicCalls() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/dynamicCalls.kt"); + doTest(fileName); + } + + @TestMetadata("implicitDynamicReceiver.kt") + public void testImplicitDynamicReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/implicitDynamicReceiver.kt"); + doTest(fileName); + } + @TestMetadata("nullable.kt") public void testNullable() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/nullable.kt");