Discriminate calls with implicit dynamic receivers

This commit is contained in:
Andrey Breslav
2014-12-10 15:28:06 +03:00
parent fbb4ed181a
commit d17edacb65
12 changed files with 192 additions and 20 deletions
@@ -22,6 +22,7 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
@@ -33,6 +34,7 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.tasks.TasksPackage;
import org.jetbrains.jet.lang.types.ErrorUtils;
@@ -128,6 +130,13 @@ public class DebugInfoUtil {
super.visitProperty(property);
}
@Override
public void visitThisExpression(@NotNull JetThisExpression expression) {
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilPackage.getResolvedCall(expression, bindingContext);
reportIfDynamic(expression, resolvedCall.getResultingDescriptor(), debugInfoReporter);
super.visitThisExpression(expression);
}
@Override
public void visitReferenceExpression(@NotNull JetReferenceExpression expression) {
super.visitReferenceExpression(expression);
@@ -218,8 +227,8 @@ public class DebugInfoUtil {
});
}
private static boolean reportIfDynamic(JetElement element, DeclarationDescriptor declarationDescriptor, DebugInfoReporter debugInfoReporter) {
if (TasksPackage.isDynamic(declarationDescriptor)) {
private static boolean reportIfDynamic(JetElement element, @Nullable DeclarationDescriptor declarationDescriptor, DebugInfoReporter debugInfoReporter) {
if (declarationDescriptor != null && TasksPackage.isDynamic(declarationDescriptor)) {
debugInfoReporter.reportDynamicCall(element);
return true;
}
@@ -19,12 +19,8 @@ package org.jetbrains.jet.lang.resolve.calls.tasks
import com.google.common.collect.Lists
import com.google.common.collect.Sets
import com.intellij.openapi.progress.ProgressIndicatorProvider
import kotlin.Function0
import kotlin.Function1
import org.jetbrains.jet.lang.descriptors.*
import org.jetbrains.jet.lang.psi.Call
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.psi.JetSuperExpression
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.jet.lang.resolve.calls.smartcasts.SmartCastUtils
import org.jetbrains.jet.lang.resolve.calls.tasks.collectors.CallableDescriptorCollector
@@ -32,11 +28,8 @@ import org.jetbrains.jet.lang.resolve.calls.tasks.collectors.CallableDescriptorC
import org.jetbrains.jet.lang.resolve.name.Name
import org.jetbrains.jet.lang.resolve.scopes.JetScope
import org.jetbrains.jet.lang.resolve.scopes.JetScopeUtils
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver
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.*
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils
@@ -405,22 +398,28 @@ public class TaskPrioritizer(private val storageManager: StorageManager) {
ResolutionTaskHolder.PriorityProvider<ResolutionCandidate<D>> {
override fun getPriority(candidate: ResolutionCandidate<D>)
= (if (isVisible(candidate)) 2 else 0) + (if (isSynthesized(candidate)) 0 else 1)
= if (hasImplicitDynamicReceiver(candidate)) 0
else (if (isVisible(candidate)) 2 else 0) + (if (isSynthesized(candidate)) 0 else 1)
override fun getMaxPriority() = 3
private fun isVisible(call: ResolutionCandidate<D>?): Boolean {
if (call == null) return false
val candidateDescriptor = call.getDescriptor()
private fun isVisible(candidate: ResolutionCandidate<D>?): Boolean {
if (candidate == null) return false
val candidateDescriptor = candidate.getDescriptor()
if (ErrorUtils.isError(candidateDescriptor)) return true
val receiverValue = ExpressionTypingUtils.normalizeReceiverValueForVisibility(call.getDispatchReceiver(), context.trace.getBindingContext())
val receiverValue = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidate.getDispatchReceiver(), context.trace.getBindingContext())
return Visibilities.isVisible(receiverValue, candidateDescriptor, context.scope.getContainingDeclaration())
}
private fun isSynthesized(call: ResolutionCandidate<D>): Boolean {
val descriptor = call.getDescriptor()
private fun isSynthesized(candidate: ResolutionCandidate<D>): Boolean {
val descriptor = candidate.getDescriptor()
return descriptor is CallableMemberDescriptor && isOrOverridesSynthesized(descriptor : CallableMemberDescriptor)
}
fun hasImplicitDynamicReceiver(candidate: ResolutionCandidate<D>): Boolean {
return (!candidate.getExplicitReceiverKind().isDispatchReceiver() || !candidate.getCall().getExplicitReceiver().exists())
&& candidate.getDescriptor().isDynamic()
}
}
private class TaskPrioritizerContext<D : CallableDescriptor, F : D>(
@@ -0,0 +1,35 @@
// !MARK_DYNAMIC_CALLS
// FILE: p/J.java
package p;
public class J {
public static class C {
private void sam(Sam sam) {}
}
public interface Sam {
void sam();
}
}
// FILE: k.kt
import p.*
class K: J.C() {
fun <!UNSUPPORTED!>dynamic<!>.test() {
<!DEBUG_INFO_DYNAMIC!>sam<!>(null)
<!INVISIBLE_MEMBER!>sam<!>(
<!NAMED_ARGUMENTS_NOT_ALLOWED, NAMED_PARAMETER_NOT_FOUND!>name<!> = null,
<!NAMED_ARGUMENTS_NOT_ALLOWED, NAMED_PARAMETER_NOT_FOUND!>name<!> = null
<!NO_VALUE_FOR_PARAMETER!>)<!>
}
fun test() {
<!INVISIBLE_MEMBER!>sam<!>(null)
}
}
@@ -0,0 +1,12 @@
package
internal final class K : p.J.C {
public constructor K()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
invisible_fake final override /*1*/ /*fake_override*/ fun sam(/*0*/ sam: (() -> kotlin.Unit)!): kotlin.Unit
invisible_fake open override /*1*/ /*fake_override*/ fun sam(/*0*/ sam: p.J.Sam!): kotlin.Unit
internal final fun test(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
internal final fun dynamic.test(): kotlin.Unit
}
@@ -1,6 +1,6 @@
// !MARK_DYNAMIC_CALLS
fun foo() {
fun test() {
dynamic {
<!DEBUG_INFO_DYNAMIC!>foo<!>()
<!DEBUG_INFO_DYNAMIC!>bar<!>.<!DEBUG_INFO_DYNAMIC!>baz<!>(0)
@@ -1,4 +1,4 @@
package
internal fun </*0*/ T> dynamic(/*0*/ body: dynamic.() -> T): T
internal fun foo(): kotlin.Unit
internal fun test(): kotlin.Unit
@@ -0,0 +1,74 @@
// !MARK_DYNAMIC_CALLS
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun dynamic.test() {
<!DEBUG_INFO_DYNAMIC!>foo<!>()
<!DEBUG_INFO_DYNAMIC!>ext<!>()
bar()
this.<!DEBUG_INFO_DYNAMIC!>bar<!>()
baz = 2
this.<!DEBUG_INFO_DYNAMIC!>baz<!> = 2
"".ext()
<!DEBUG_INFO_DYNAMIC!>ext<!>()
"".extValFun()
<!DEBUG_INFO_DYNAMIC!>extValFun<!>()
"".extVal
<!DEBUG_INFO_DYNAMIC!>extVal<!>
baz.extExtVal()
<!DEBUG_INFO_DYNAMIC!>extExtVal<!>()
""()
<!DEBUG_INFO_DYNAMIC!>this<!>()
C() + C()
<!NO_VALUE_FOR_PARAMETER!>+<!>C()
this <!DEBUG_INFO_DYNAMIC!>+<!> C()
0.<!UNRESOLVED_REFERENCE!>missing<!>()
}
fun bar() {}
var baz = 1
fun Any.ext() {}
val Any.extValFun: () -> Unit get() = null!!
val Any.extVal: () -> Unit get() = null!!
val Any.extExtVal: Any.() -> Unit get() = null!!
fun Any.invoke() {}
fun Any.plus(a: Any) {}
class C {
fun String.invoke() {}
val foo: String.() -> Unit = null!!
val s: String = ""
val withInvoke = WithInvoke()
fun dynamic.test() {
s()
<!DEBUG_INFO_DYNAMIC!>this<!>()
s.foo()
this.<!DEBUG_INFO_DYNAMIC!>foo<!>()
withInvoke()
this@C.withInvoke()
}
}
class WithInvoke {
fun invoke() {}
}
@@ -0,0 +1,31 @@
package
internal var baz: kotlin.Int
internal val kotlin.Any.extExtVal: kotlin.Any.() -> kotlin.Unit
internal val kotlin.Any.extVal: () -> kotlin.Unit
internal val kotlin.Any.extValFun: () -> kotlin.Unit
internal fun bar(): kotlin.Unit
internal fun kotlin.Any.ext(): kotlin.Unit
internal fun kotlin.Any.invoke(): kotlin.Unit
internal fun kotlin.Any.plus(/*0*/ a: kotlin.Any): kotlin.Unit
internal fun dynamic.test(): kotlin.Unit
internal final class C {
public constructor C()
internal final val foo: kotlin.String.() -> kotlin.Unit
internal final val s: kotlin.String = ""
internal final val withInvoke: WithInvoke
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
internal final fun kotlin.String.invoke(): kotlin.Unit
internal final fun dynamic.test(): kotlin.Unit
}
internal final class WithInvoke {
public constructor WithInvoke()
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 invoke(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -84,7 +84,6 @@ public abstract class AbstractJetDiagnosticsTest extends BaseDiagnosticsTest {
}
);
List<JetFile> allJetFiles = new ArrayList<JetFile>();
Map<TestModule, ModuleDescriptorImpl> modules = createModules(groupedByModule);
Map<TestModule, BindingContext> moduleBindings = new HashMap<TestModule, BindingContext>();
@@ -95,7 +94,6 @@ public abstract class AbstractJetDiagnosticsTest extends BaseDiagnosticsTest {
List<? extends TestFile> testFilesInModule = entry.getValue();
List<JetFile> jetFiles = getJetFiles(testFilesInModule, true);
allJetFiles.addAll(jetFiles);
ModuleDescriptorImpl module = modules.get(testModule);
BindingTrace moduleTrace = new CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace();
@@ -3781,6 +3781,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/unsupported.kt");
doTest(fileName);
}
@TestMetadata("withInvisibleSynthesized.kt")
public void testWithInvisibleSynthesized() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/enum")
@@ -195,6 +195,12 @@ public class JetDiagnosticsTestWithJsStdLibGenerated extends AbstractJetDiagnost
doTest(fileName);
}
@TestMetadata("staticCallsInDynamicContext.kt")
public void testStaticCallsInDynamicContext() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.kt");
doTest(fileName);
}
@TestMetadata("substitution.kt")
public void testSubstitution() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/substitution.kt");
+2
View File
@@ -81,6 +81,8 @@ If a receiver of a call is dynamic, the following resolution rules apply:
this permits calling them on vals (e.g. those holding collection-like objects)
- The invoke convention is limited so that for calls like `dyn.foo()` we do not look for property `foo` that has `invoke` defined on it
(same for other cases like `+dyn` etc)
- dynamic candidates with no explicit dispatch receiver are discriminated against all other candidate, i.e. for a call `foo()`, we first try
to match static candidates, and only then dynamic ones.
NOTE: we do not even try to resolve extensions declared for static types if the receiver is dynamic. As a workaround, one may use an upcast
to a static type: `(dyn as Foo).extensionForFoo()`.