diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 90ad6512fed..2c04d086ad5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -828,6 +828,8 @@ public interface Errors { DiagnosticFactory0 ABBREVIATED_NOTHING_PROPERTY_TYPE = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 IMPLICIT_INTERSECTION_TYPE = DiagnosticFactory1.create(ERROR); + DiagnosticFactory0 DYNAMIC_RECEIVER_NOT_ALLOWED = DiagnosticFactory0.create(ERROR, PositioningStrategies.RECEIVER); + // Context tracking DiagnosticFactory1 EXPRESSION_EXPECTED = DiagnosticFactory1.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index 80f2c63bd1f..6da707ad8b0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -492,4 +492,11 @@ object PositioningStrategies { return markElement(element.returnKeyword) } } + + @JvmField val RECEIVER: PositioningStrategy = object : DeclarationHeader() { + override fun mark(element: KtCallableDeclaration): List { + element.receiverTypeReference?.let { return markElement(it) } + return DEFAULT.mark(element) + } + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 6889c7fbd01..ad6964d45ae 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -635,6 +635,8 @@ public class DefaultErrorMessages { MAP.put(IMPLICIT_INTERSECTION_TYPE, "Inferred type {0} is an intersection, please specify the required type explicitly", RENDER_TYPE); MAP.put(EXPECTED_CONDITION, "Expected condition of type Boolean"); + MAP.put(DYNAMIC_RECEIVER_NOT_ALLOWED, "Dynamic receiver is prohibited"); + MAP.put(CANNOT_CHECK_FOR_ERASED, "Cannot check for instance of erased type: {0}", RENDER_TYPE); MAP.put(UNCHECKED_CAST, "Unchecked cast: {0} to {1}", RENDER_TYPE, RENDER_TYPE); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index df25d5852a6..80bd939d202 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -75,7 +75,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf( InfixModifierChecker(), CoroutineModifierChecker, SinceKotlinAnnotationValueChecker, - ReifiedTypeParameterAnnotationChecker() + ReifiedTypeParameterAnnotationChecker(), + DynamicReceiverChecker ) private val DEFAULT_CALL_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt index 2d976d95678..a22468ab9de 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/NewResolutionOldInference.kt @@ -43,6 +43,7 @@ import org.jetbrains.kotlin.resolve.calls.results.ResolutionResultsHandler import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.tasks.* +import org.jetbrains.kotlin.resolve.descriptorUtil.hasDynamicExtensionAnnotation import org.jetbrains.kotlin.resolve.isHiddenInResolution import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.MemberScope @@ -347,10 +348,21 @@ class NewResolutionOldInference( basicCallContext.dataFlowInfoForArguments // todo may be we should create new mutable info for arguments ) - // see spec-docs/dynamic-types.md - if (extensionReceiver != null && extensionReceiver.receiverValue.type.isDynamic() - && !towerCandidate.descriptor.extensionReceiverParameter!!.value.type.isDynamic()) { - return MyCandidate(ResolutionCandidateStatus(listOf(ExtensionWithStaticTypeWithDynamicReceiver)), candidateCall) + /** + * See https://jetbrains.quip.com/qcTDAFcgFLEM + * + * For now we have only 2 functions with dynamic receivers: iterator() and unsafeCast() + * Both this function are marked via @kotlin.internal.DynamicExtension. + */ + if (extensionReceiver != null) { + val parameterIsDynamic = towerCandidate.descriptor.extensionReceiverParameter!!.value.type.isDynamic() + val argumentIsDynamic = extensionReceiver.receiverValue.type.isDynamic() + + if (parameterIsDynamic != argumentIsDynamic || + (parameterIsDynamic && !towerCandidate.descriptor.hasDynamicExtensionAnnotation()) + ) { + return MyCandidate(ResolutionCandidateStatus(listOf(HiddenExtensionRelatedToDynamicTypes)), candidateCall) + } } if (towerCandidate.descriptor.isHiddenInResolution(languageVersionSettings, basicCallContext.isSuperCall)) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DynamicReceiverChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DynamicReceiverChecker.kt new file mode 100644 index 00000000000..10248c1da3a --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DynamicReceiverChecker.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2016 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.kotlin.resolve.checkers + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.diagnostics.DiagnosticSink +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtCallableDeclaration +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.hasDynamicExtensionAnnotation +import org.jetbrains.kotlin.types.isDynamic + +object DynamicReceiverChecker : SimpleDeclarationChecker { + override fun check( + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + diagnosticHolder: DiagnosticSink, + bindingContext: BindingContext + ) { + if (descriptor !is CallableDescriptor || declaration !is KtCallableDeclaration || descriptor.hasDynamicExtensionAnnotation()) return + + // function expression + if (declaration is KtNamedFunction && declaration.name == null) return + + if (descriptor.extensionReceiverParameter?.value?.type?.isDynamic() == true) { + diagnosticHolder.report(Errors.DYNAMIC_RECEIVER_NOT_ALLOWED.on(declaration)) + } + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt index 36fea4def76..6c1c74c20be 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt @@ -90,7 +90,7 @@ object ErrorDescriptorDiagnostic : ResolutionDiagnostic(ResolutionCandidateAppli object LowPriorityDescriptorDiagnostic : ResolutionDiagnostic(ResolutionCandidateApplicability.RESOLVED_LOW_PRIORITY) object DynamicDescriptorDiagnostic: ResolutionDiagnostic(ResolutionCandidateApplicability.RESOLVED_LOW_PRIORITY) object UnstableSmartCastDiagnostic: ResolutionDiagnostic(ResolutionCandidateApplicability.MAY_THROW_RUNTIME_ERROR) -object ExtensionWithStaticTypeWithDynamicReceiver: ResolutionDiagnostic(ResolutionCandidateApplicability.HIDDEN) +object HiddenExtensionRelatedToDynamicTypes : ResolutionDiagnostic(ResolutionCandidateApplicability.HIDDEN) object HiddenDescriptor: ResolutionDiagnostic(ResolutionCandidateApplicability.HIDDEN) object InvokeConventionCallNoOperatorModifier : ResolutionDiagnostic(ResolutionCandidateApplicability.CONVENTION_ERROR) diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.kt b/compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.kt index 9f899effa0d..00ad568f6a1 100644 --- a/compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.kt +++ b/compiler/testData/diagnostics/tests/dynamicTypes/withInvisibleSynthesized.kt @@ -20,7 +20,7 @@ public class J { import p.* class K: J.C() { - fun dynamic.test() { + fun dynamic.test() { sam(null) sam( name = null, diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.dynamic.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.dynamic.txt new file mode 100644 index 00000000000..6bef7452d52 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.dynamic.txt @@ -0,0 +1,8 @@ +public final var foo: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var bar: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final fun dynamicProperty(): dynamic +public final fun dynamicParameter(): dynamic diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.kt new file mode 100644 index 00000000000..016e54a4b41 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.kt @@ -0,0 +1,17 @@ +// !MARK_DYNAMIC_CALLS + +fun withDynamicReceiver(d: dynamic.() -> Unit) {} + +fun test() = withDynamicReceiver { + foo + bar = 1 +} + +fun test2() = withDynamicReceiver(fun dynamic.() {}) + +val dynamicProperty: dynamic.() -> Unit = TODO() + +fun test(d: dynamic, dynamicParameter: dynamic.() -> Unit) { + d.dynamicProperty() + d.dynamicParameter() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.txt new file mode 100644 index 00000000000..83cc3e165c1 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.txt @@ -0,0 +1,7 @@ +package + +public val dynamicProperty: dynamic.() -> kotlin.Unit +public fun test(): kotlin.Unit +public fun test(/*0*/ d: dynamic, /*1*/ dynamicParameter: dynamic.() -> kotlin.Unit): kotlin.Unit +public fun test2(): kotlin.Unit +public fun withDynamicReceiver(/*0*/ d: dynamic.() -> kotlin.Unit): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.kt index b596dadb1bd..9ec98d4ede6 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/block.kt @@ -7,7 +7,7 @@ fun test() { } } -fun dynamic(body: dynamic.() -> T): T { +fun dynamic(body: dynamic.() -> T): T { val topLevel = null - return topLevel.body() + return topLevel.body() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicExtension.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicExtension.kt new file mode 100644 index 00000000000..f32484d4769 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicExtension.kt @@ -0,0 +1,28 @@ +// !MARK_DYNAMIC_CALLS + +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@kotlin.internal.DynamicExtension +fun dynamic.onDynamicFun() = 1 + +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@kotlin.internal.DynamicExtension +var dynamic.onDynamicProperty + get() = "" + set(value) {} + + +fun test(d: dynamic, a: Any?) { + eatT(d.onDynamicFun()) + eatT(d.onDynamicProperty) + d.onDynamicProperty = "" + eatT>(d.iterator()) + + for (item in d) { + println(item) + } + + a.onDynamicFun() + a.onDynamicProperty +} + +fun eatT(t: T) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicExtension.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicExtension.txt new file mode 100644 index 00000000000..8dc5f1e42e5 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicExtension.txt @@ -0,0 +1,6 @@ +package + +@kotlin.Suppress(names = {"INVISIBLE_MEMBER", "INVISIBLE_REFERENCE"}) @kotlin.internal.DynamicExtension public var dynamic.onDynamicProperty: kotlin.String +public fun eatT(/*0*/ t: T): kotlin.Unit +public fun test(/*0*/ d: dynamic, /*1*/ a: kotlin.Any?): kotlin.Unit +@kotlin.Suppress(names = {"INVISIBLE_MEMBER", "INVISIBLE_REFERENCE"}) @kotlin.internal.DynamicExtension public fun dynamic.onDynamicFun(): kotlin.Int diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicVsGeneric.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicVsGeneric.kt index 3fa50ae9805..de608354e73 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicVsGeneric.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicVsGeneric.kt @@ -3,7 +3,7 @@ object X2 class Inv -fun dynamic.foo() = X1 +fun dynamic.foo() = X1 fun Inv.foo() = X2 fun test(): X2 = Inv().foo() diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.dynamic.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.dynamic.txt index 11e9d7506e6..fbbbbcdf74b 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.dynamic.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.dynamic.txt @@ -22,6 +22,12 @@ public final var onNullableAnyVal: dynamic public final var onStringVal: dynamic public final fun (): dynamic public final fun (/*0*/ : dynamic): kotlin.Unit +public final var onDynamicVal: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var onDynamicVal: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit public final var memberVal: dynamic public final fun (): dynamic public final fun (/*0*/ : dynamic): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.kt index e97d2c10900..f5d1dd34af7 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVals.kt @@ -19,29 +19,29 @@ fun test(d: dynamic) { d.onStringVal = 1 - d.onDynamicVal = 1 + d.onDynamicVal = 1 (d as String).onStringVal (d as Any).onAnyVal (d as Any?).onNullableAnyVal - (d as Any).onDynamicVal + (d as Any).onDynamicVal } fun testReassignmentWithSafeCall(d: dynamic) { - d?.onDynamicVal = 1 + d?.onDynamicVal = 1 } fun testReassignmentWithStaticCalls(d: dynamic) { (d as String).onStringVal = 1 (d as Any).onAnyVal = 1 (d as Any?).onNullableAnyVal = 1 - (d as Any).onDynamicVal = 1 + (d as Any).onDynamicVal = 1 } val Any.onAnyVal: Int get() = 1 val Any?.onNullableAnyVal: Int get() = 1 val String.onStringVal: Int get() = 1 -val dynamic.onDynamicVal: Int get() = 1 +val dynamic.onDynamicVal: Int get() = 1 class C { fun test(d: dynamic) { diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.dynamic.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.dynamic.txt index 08a5d948baf..d56510813f0 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.dynamic.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.dynamic.txt @@ -22,6 +22,12 @@ public final var onNullableAnyVar: dynamic public final var onStringVar: dynamic public final fun (): dynamic public final fun (/*0*/ : dynamic): kotlin.Unit +public final var onDynamicVar: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var onDynamicVar: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit public final var memberVar: dynamic public final fun (): dynamic public final fun (/*0*/ : dynamic): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.kt index 2698a9a9772..a82a04fe099 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionVars.kt @@ -20,19 +20,19 @@ fun test(d: dynamic) { d.onStringVar = 1 - d.onDynamicVar = 1 + d.onDynamicVar = 1 - d?.onDynamicVar = 1 + d?.onDynamicVar = 1 (d as String).onStringVar (d as Any).onAnyVar (d as Any?).onNullableAnyVar - (d as Any).onDynamicVar + (d as Any).onDynamicVar (d as String).onStringVar = 1 (d as Any).onAnyVar = 1 (d as Any?).onNullableAnyVar = 1 - (d as Any).onDynamicVar = 1 + (d as Any).onDynamicVar = 1 } var Any.onAnyVar: Int @@ -47,7 +47,7 @@ var String.onStringVar: Int get() = 1 set(v) {} -var dynamic.onDynamicVar: Int +var dynamic.onDynamicVar: Int get() = 1 set(v) {} diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.dynamic.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.dynamic.txt index 6fe443cd2e7..4b580cc6ebf 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.dynamic.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.dynamic.txt @@ -4,5 +4,7 @@ public final fun onAny(): dynamic public final fun onAny(/*0*/ p0: dynamic): dynamic public final fun onNullableAny(): dynamic public final fun onString(): dynamic +public final fun onDynamic(): dynamic +public final fun onDynamic(): dynamic public final fun member(): dynamic public final fun memberExtension(): dynamic diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.kt index 570a8aa0fe2..8ee0557e32d 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensions.kt @@ -13,19 +13,19 @@ fun test(d: dynamic) { d.onNullableAny() d.onString() - d.onDynamic() - d?.onDynamic() + d.onDynamic() + d?.onDynamic() (d as String).onString() (d as Any).onAny() (d as Any?).onNullableAny() - (d as Any).onDynamic() + (d as Any).onDynamic() } fun Any.onAny() {} fun Any?.onNullableAny() {} fun String.onString() {} -fun dynamic.onDynamic() {} +fun dynamic.onDynamic() {} class C { fun test(d: dynamic) { diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionsToDynamic.dynamic.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionsToDynamic.dynamic.txt new file mode 100644 index 00000000000..e247f4e66e0 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionsToDynamic.dynamic.txt @@ -0,0 +1,54 @@ +public final fun onDynamic(): dynamic +public final fun onNullableDynamic(): dynamic +public final var valOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var valOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var varOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var varOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final fun onDynamic(): dynamic +public final fun onNullableDynamic(): dynamic +public final var valOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var valOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var varOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var varOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final fun onDynamic(): dynamic +public final fun onNullableDynamic(): dynamic +public final var valOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var valOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var varOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var varOnDynamic: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var memberExtensionVar: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var memberExtensionVar: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var memberExtensionVal: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var memberExtensionVal: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionsToDynamic.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionsToDynamic.kt index 1d22a524268..4f1638cd84d 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionsToDynamic.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/extensionsToDynamic.kt @@ -1,58 +1,58 @@ // !MARK_DYNAMIC_CALLS fun test(d: dynamic) { - d.onDynamic() - d.onNullableDynamic() + d.onDynamic() + d.onNullableDynamic() - d.valOnDynamic - d.valOnDynamic = 1 + d.valOnDynamic + d.valOnDynamic = 1 - d.varOnDynamic - d.varOnDynamic = 1 + d.varOnDynamic + d.varOnDynamic = 1 } -fun dynamic.extTest() { - onDynamic() - onNullableDynamic() +fun dynamic.extTest() { + onDynamic() + onNullableDynamic() - valOnDynamic - valOnDynamic = 1 + valOnDynamic + valOnDynamic = 1 - varOnDynamic - varOnDynamic = 1 + varOnDynamic + varOnDynamic = 1 - this.onDynamic() - this.onNullableDynamic() + this.onDynamic() + this.onNullableDynamic() - this.valOnDynamic - this.valOnDynamic = 1 + this.valOnDynamic + this.valOnDynamic = 1 - this.varOnDynamic - this.varOnDynamic = 1 + this.varOnDynamic + this.varOnDynamic = 1 } -fun dynamic.onDynamic() {} -fun dynamic?.onNullableDynamic() {} +fun dynamic.onDynamic() {} +fun dynamic?.onNullableDynamic() {} -val dynamic.valOnDynamic: Int get() = 1 +val dynamic.valOnDynamic: Int get() = 1 -var dynamic.varOnDynamic: Int +var dynamic.varOnDynamic: Int get() = 1 set(v) {} class ForMemberExtensions { fun test(d: dynamic) { - d.memberExtensionVar - d.memberExtensionVar = 1 + d.memberExtensionVar + d.memberExtensionVar = 1 - d.memberExtensionVal - d.memberExtensionVal = 1 + d.memberExtensionVal + d.memberExtensionVal = 1 } - val dynamic.memberExtensionVal: Int get() = 1 - var dynamic.memberExtensionVar: Int + val dynamic.memberExtensionVal: Int get() = 1 + var dynamic.memberExtensionVar: Int get() = 1 set(v) {} } \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/implicitDynamicReceiver.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/implicitDynamicReceiver.kt index c7baa5706b6..0c82222d14b 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/implicitDynamicReceiver.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/implicitDynamicReceiver.kt @@ -1,4 +1,4 @@ -fun dynamic.test() { +fun dynamic.test() { val v1 = foo() v1.isDynamic() // to check that anything is resolvable diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.dynamic.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.dynamic.txt index 1c3db620d53..36e3e087d19 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.dynamic.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.dynamic.txt @@ -1 +1,3 @@ public final fun foo(/*0*/ p0: dynamic, /*1*/ p1: dynamic): dynamic +public final fun foo(/*0*/ p0: dynamic, /*1*/ p1: dynamic): dynamic +public final fun foo(/*0*/ p0: dynamic, /*1*/ p1: dynamic): dynamic diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.kt index 99086046eba..57612fb6669 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/overloadingAmbiguity.kt @@ -1,11 +1,11 @@ // !MARK_DYNAMIC_CALLS // !DIAGNOSTICS: -UNUSED_PARAMETER -fun dynamic.foo(s: String, a: Any) {} -fun dynamic.foo(s: Any, a: String) {} +fun dynamic.foo(s: String, a: Any) {} +fun dynamic.foo(s: Any, a: String) {} fun test(d: dynamic) { - d.foo(1, "") - d.foo("", "") + d.foo(1, "") + d.foo("", "") d.foo(1, 1) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.kt index b5b9a1399f2..0aed9b1f66b 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticCallsInDynamicContext.kt @@ -1,7 +1,7 @@ // !MARK_DYNAMIC_CALLS // !DIAGNOSTICS: -UNUSED_PARAMETER -fun dynamic.test() { +fun dynamic.test() { foo() ext() @@ -57,7 +57,7 @@ class C { val withInvoke = WithInvoke() - fun dynamic.test() { + fun dynamic.test() { s() this() diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.dynamic.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.dynamic.txt new file mode 100644 index 00000000000..fcb30265c5a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.dynamic.txt @@ -0,0 +1,7 @@ +public final fun staticExtension(): dynamic +public final var staticProperty: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit +public final var staticParameter: dynamic + public final fun (): dynamic + public final fun (/*0*/ : dynamic): kotlin.Unit diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.kt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.kt new file mode 100644 index 00000000000..28dab20afc8 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.kt @@ -0,0 +1,12 @@ +// !MARK_DYNAMIC_CALLS + + +fun Any?.staticExtension() = 1 + +val Any?.staticProperty get() = 2 + +fun test(d: dynamic, staticParameter: Any?.() -> Unit) { + d.staticExtension() + d.staticProperty + d.staticParameter +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.txt b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.txt new file mode 100644 index 00000000000..962168503a3 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.txt @@ -0,0 +1,5 @@ +package + +public val kotlin.Any?.staticProperty: kotlin.Int +public fun test(/*0*/ d: dynamic, /*1*/ staticParameter: kotlin.Any?.() -> kotlin.Unit): kotlin.Unit +public fun kotlin.Any?.staticExtension(): kotlin.Int diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java index c040ca9743b..3f5efe572f0 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -50,6 +50,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("allowedDynamicFunctionType.kt") + public void testAllowedDynamicFunctionType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/allowedDynamicFunctionType.kt"); + doTest(fileName); + } + @TestMetadata("assignment.kt") public void testAssignment() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/assignment.kt"); @@ -110,6 +116,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes doTest(fileName); } + @TestMetadata("dynamicExtension.kt") + public void testDynamicExtension() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicExtension.kt"); + doTest(fileName); + } + @TestMetadata("dynamicSafeCalls.kt") public void testDynamicSafeCalls() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/dynamicSafeCalls.kt"); @@ -242,6 +254,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes doTest(fileName); } + @TestMetadata("staticExtensions.kt") + public void testStaticExtensions() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/staticExtensions.kt"); + doTest(fileName); + } + @TestMetadata("substitution.kt") public void testSubstitution() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes/substitution.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt index 9e3a6c09605..edbe29c6b87 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt @@ -30,6 +30,7 @@ private val EXACT_ANNOTATION_FQ_NAME = FqName("kotlin.internal.Exact") val LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_FQ_NAME = FqName("kotlin.internal.LowPriorityInOverloadResolution") private val HIDES_MEMBERS_ANNOTATION_FQ_NAME = FqName("kotlin.internal.HidesMembers") private val ONLY_INPUT_TYPES_FQ_NAME = FqName("kotlin.internal.OnlyInputTypes") +private val DYNAMIC_EXTENSION_FQ_NAME = FqName("kotlin.internal.DynamicExtension") // @HidesMembers annotation only has effect for members with these names val HIDES_MEMBERS_NAME_LIST = setOf(Name.identifier("forEach")) @@ -46,6 +47,7 @@ fun FqName.isInternalAnnotationForResolve() = this == NO_INFER_ANNOTATION_FQ_NAM fun CallableDescriptor.hasLowPriorityInOverloadResolution(): Boolean = annotations.hasAnnotation(LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_FQ_NAME) fun CallableDescriptor.hasHidesMembersAnnotation(): Boolean = annotations.hasAnnotation(HIDES_MEMBERS_ANNOTATION_FQ_NAME) +fun CallableDescriptor.hasDynamicExtensionAnnotation(): Boolean = annotations.hasAnnotation(DYNAMIC_EXTENSION_FQ_NAME) fun TypeParameterDescriptor.hasOnlyInputTypesAnnotation(): Boolean = annotations.hasAnnotation(ONLY_INPUT_TYPES_FQ_NAME) diff --git a/js/js.libraries/src/core/dynamic.kt b/js/js.libraries/src/core/dynamic.kt index 3424b10f957..66eeaf2f704 100644 --- a/js/js.libraries/src/core/dynamic.kt +++ b/js/js.libraries/src/core/dynamic.kt @@ -24,6 +24,7 @@ public inline fun Any?.asDynamic(): dynamic = this public inline fun Any?.unsafeCast(): @kotlin.internal.NoInfer T = this.asDynamic() // TODO add the support ES6 iterators +@kotlin.internal.DynamicExtension public operator fun dynamic.iterator(): Iterator { val r: Any? = this diff --git a/libraries/stdlib/src/kotlin/internal/Annotations.kt b/libraries/stdlib/src/kotlin/internal/Annotations.kt index 1c2a8d3f30b..6eae8ff6127 100644 --- a/libraries/stdlib/src/kotlin/internal/Annotations.kt +++ b/libraries/stdlib/src/kotlin/internal/Annotations.kt @@ -57,4 +57,11 @@ internal annotation class OnlyInputTypes */ @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.BINARY) -internal annotation class InlineOnly \ No newline at end of file +internal annotation class InlineOnly + +/** + * Specifies that this declaration can have dynamic receiver type. + */ +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.BINARY) +internal annotation class DynamicExtension \ No newline at end of file