diff --git a/compiler/container/src/org/jetbrains/kotlin/container/Container.kt b/compiler/container/src/org/jetbrains/kotlin/container/Container.kt index 7d6d0229cf4..12ea40228e7 100644 --- a/compiler/container/src/org/jetbrains/kotlin/container/Container.kt +++ b/compiler/container/src/org/jetbrains/kotlin/container/Container.kt @@ -102,6 +102,11 @@ class StorageComponentContainer( return this } + internal fun registerClashResolvers(resolvers: List>): StorageComponentContainer { + componentStorage.registerClashResolvers(resolvers) + return this + } + override fun create(request: Class): T { val constructorBinding = request.bindToConstructor(unknownContext) val args = constructorBinding.argumentDescriptors.map { it.getValue() }.toTypedArray() diff --git a/compiler/container/src/org/jetbrains/kotlin/container/Dsl.kt b/compiler/container/src/org/jetbrains/kotlin/container/Dsl.kt index e600743e870..3f90482fdce 100644 --- a/compiler/container/src/org/jetbrains/kotlin/container/Dsl.kt +++ b/compiler/container/src/org/jetbrains/kotlin/container/Dsl.kt @@ -50,6 +50,10 @@ fun StorageComponentContainer.useInstanceIfNotNull(instance: Any?) { if (instance != null) registerInstance(instance) } +fun StorageComponentContainer.useClashResolver(clashResolver: PlatformExtensionsClashResolver<*>) { + registerClashResolvers(listOf(clashResolver)) +} + inline operator fun ComponentProvider.getValue(thisRef: Any?, desc: KProperty<*>): T { return getService(T::class.java) } diff --git a/compiler/container/src/org/jetbrains/kotlin/container/Registry.kt b/compiler/container/src/org/jetbrains/kotlin/container/Registry.kt index 69e16ed1b16..1d8ffb26f8d 100644 --- a/compiler/container/src/org/jetbrains/kotlin/container/Registry.kt +++ b/compiler/container/src/org/jetbrains/kotlin/container/Registry.kt @@ -70,4 +70,20 @@ internal class ComponentRegistry { } registrationMap += other.registrationMap } + + fun resolveClashesIfAny(container: ComponentContainer, clashResolvers: List>) { + /* + The idea is to create descriptor, which is very similar to other SingletonDescriptor, but instead of calling + constructor we call 'resolveExtensionsClash' with values of clashed components as arguments. + + By mimicking the usual descriptors we get lazy evaluation and consistency checks for free. + */ + for (resolver in clashResolvers) { + val clashedComponents = registrationMap[resolver.applicableTo] as? Collection ?: continue + if (clashedComponents.size <= 1) continue + + val substituteDescriptor = ClashResolutionDescriptor(container, resolver, clashedComponents.toList()) + registrationMap[resolver.applicableTo] = substituteDescriptor + } + } } \ No newline at end of file diff --git a/compiler/container/src/org/jetbrains/kotlin/container/Singletons.kt b/compiler/container/src/org/jetbrains/kotlin/container/Singletons.kt index 59f87672a0c..775753e885b 100644 --- a/compiler/container/src/org/jetbrains/kotlin/container/Singletons.kt +++ b/compiler/container/src/org/jetbrains/kotlin/container/Singletons.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.container import java.io.Closeable +import java.lang.IllegalStateException import java.lang.reflect.Type import java.util.* @@ -144,6 +145,29 @@ open class SingletonTypeComponentDescriptor(container: ComponentContainer, val k override fun toString(): String = "Singleton: ${klass.simpleName}" } +internal class ClashResolutionDescriptor>( + container: ComponentContainer, + private val resolver: PlatformExtensionsClashResolver, + private val clashedComponents: List +) : SingletonDescriptor(container) { + + override fun createInstance(context: ValueResolveContext): Any { + state = ComponentState.Initializing + val extensions = computeArguments(clashedComponents) as List + val resolution = resolver.resolveExtensionsClash(extensions) + state = ComponentState.Initialized + return resolution + } + + override fun getRegistrations(): Iterable { + throw IllegalStateException("Shouldn't be called") + } + + override fun getDependencies(context: ValueResolveContext): Collection { + throw IllegalStateException("Shouldn't be called") + } +} + class ImplicitSingletonTypeComponentDescriptor(container: ComponentContainer, klass: Class<*>) : SingletonTypeComponentDescriptor(container, klass) { override fun toString(): String { return "Implicit: ${klass.simpleName}" diff --git a/compiler/container/src/org/jetbrains/kotlin/container/Storage.kt b/compiler/container/src/org/jetbrains/kotlin/container/Storage.kt index bcd8e06dc8f..8f7deff93bc 100644 --- a/compiler/container/src/org/jetbrains/kotlin/container/Storage.kt +++ b/compiler/container/src/org/jetbrains/kotlin/container/Storage.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.container import com.intellij.util.containers.MultiMap import java.io.Closeable import java.io.PrintStream +import java.lang.IllegalStateException import java.lang.reflect.Modifier import java.lang.reflect.ParameterizedType import java.lang.reflect.Type @@ -37,13 +38,19 @@ internal class InvalidCardinalityException(message: String) : Exception(message) class ComponentStorage(private val myId: String, parent: ComponentStorage?) : ValueResolver { var state = ComponentStorageState.Initial - private val registry = ComponentRegistry() - init { - parent?.let { registry.addAll(it.registry) } - } private val descriptors = LinkedHashSet() private val dependencies = MultiMap.createLinkedSet() + private val clashResolvers = ArrayList>() + private val registry = ComponentRegistry() + + init { + parent?.let { + registry.addAll(it.registry) + clashResolvers.addAll(it.clashResolvers) + } + } + override fun resolve(request: Type, context: ValueResolveContext): ValueDescriptor? { fun ComponentDescriptor.isDefaultComponent(): Boolean = @@ -107,6 +114,10 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va return registry.tryGetEntry(request) } + internal fun registerClashResolvers(resolvers: List>) { + clashResolvers.addAll(resolvers) + } + internal fun registerDescriptors(context: ComponentResolveContext, items: List) { if (state == ComponentStorageState.Disposed) { throw ContainerConsistencyException("Cannot register descriptors in $state state") @@ -135,6 +146,7 @@ class ComponentStorage(private val myId: String, parent: ComponentStorage?) : Va val implicits = inspectDependenciesAndRegisterAdhoc(context, descriptors) + registry.resolveClashesIfAny(context.container, clashResolvers) injectProperties(context, descriptors + implicits) } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt index 76dfa0b0bfb..216fc185ae3 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt @@ -6,9 +6,11 @@ package org.jetbrains.kotlin.resolve.jvm.platform import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap +import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver import org.jetbrains.kotlin.container.StorageComponentContainer import org.jetbrains.kotlin.container.useImpl import org.jetbrains.kotlin.container.useInstance +import org.jetbrains.kotlin.load.java.components.SamConversionResolver import org.jetbrains.kotlin.load.java.sam.JvmSamConversionTransformer import org.jetbrains.kotlin.load.java.sam.SamConversionResolverImpl import org.jetbrains.kotlin.resolve.PlatformConfiguratorBase @@ -18,7 +20,6 @@ import org.jetbrains.kotlin.resolve.jvm.* import org.jetbrains.kotlin.resolve.jvm.checkers.* import org.jetbrains.kotlin.resolve.jvm.multiplatform.JavaActualAnnotationArgumentExtractor import org.jetbrains.kotlin.synthetic.JavaSyntheticScopes -import org.jetbrains.kotlin.types.DynamicTypesSettings import org.jetbrains.kotlin.types.expressions.FunctionWithBigAritySupport object JvmPlatformConfigurator : PlatformConfiguratorBase( @@ -73,6 +74,10 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase( ExplicitMetadataChecker ), + additionalClashResolvers = listOf( + PlatformExtensionsClashResolver.FallbackToDefault(SamConversionResolver.Empty, SamConversionResolver::class.java) + ), + identifierChecker = JvmSimpleNameBacktickChecker, overloadFilter = JvmOverloadFilter, @@ -98,6 +103,6 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase( container.useImpl() container.useImpl() container.useImpl() - container.useInstance(FunctionWithBigAritySupport.LANGUAGE_VERSION_DEPENDENT) + container.useInstance(FunctionWithBigAritySupport.LanguageVersionDependent) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/IdentifierChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/IdentifierChecker.kt index b440bd9f76f..83ffc2d425b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/IdentifierChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/IdentifierChecker.kt @@ -17,12 +17,14 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.container.DefaultImplementation +import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver +import org.jetbrains.kotlin.container.PlatformSpecificExtension import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtSimpleNameExpression @DefaultImplementation(impl = IdentifierChecker.Default::class) -interface IdentifierChecker { +interface IdentifierChecker : PlatformSpecificExtension { fun checkIdentifier(simpleNameExpression: KtSimpleNameExpression, diagnosticHolder: DiagnosticSink) fun checkDeclaration(declaration: KtDeclaration, diagnosticHolder: DiagnosticSink) @@ -31,3 +33,18 @@ interface IdentifierChecker { override fun checkDeclaration(declaration: KtDeclaration, diagnosticHolder: DiagnosticSink) {} } } + +class IdentifierCheckerClashesResolver : PlatformExtensionsClashResolver(IdentifierChecker::class.java) { + override fun resolveExtensionsClash(extensions: List): IdentifierChecker = CompositeIdentifierChecker(extensions) +} + +// Launches every underlying checker +class CompositeIdentifierChecker(private val identifierCheckers: List) : IdentifierChecker { + override fun checkIdentifier(simpleNameExpression: KtSimpleNameExpression, diagnosticHolder: DiagnosticSink) { + identifierCheckers.forEach { it.checkIdentifier(simpleNameExpression, diagnosticHolder) } + } + + override fun checkDeclaration(declaration: KtDeclaration, diagnosticHolder: DiagnosticSink) { + identifierCheckers.forEach { it.checkDeclaration(declaration, diagnosticHolder) } + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index 1ece40f388c..0549370e12f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -6,12 +6,9 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMap -import org.jetbrains.kotlin.container.StorageComponentContainer -import org.jetbrains.kotlin.container.composeContainer -import org.jetbrains.kotlin.container.useImpl -import org.jetbrains.kotlin.container.useInstance -import org.jetbrains.kotlin.resolve.calls.checkers.* import org.jetbrains.kotlin.container.* +import org.jetbrains.kotlin.resolve.calls.checkers.* +import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator import org.jetbrains.kotlin.resolve.checkers.* import org.jetbrains.kotlin.resolve.lazy.DelegationFilter import org.jetbrains.kotlin.types.DynamicTypesSettings @@ -55,6 +52,20 @@ private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf( ) private val DEFAULT_ANNOTATION_CHECKERS = listOf() +private val DEFAULT_CLASH_RESOLVERS = listOf>( + IdentifierCheckerClashesResolver(), + + /** + * We should use NONE for clash resolution, because: + * - JvmTypeSpecificityComparator covers cases with flexible types and primitive types loaded from Java, and all this is irrelevant for + * non-JVM modules + * - JsTypeSpecificityComparator covers case with dynamics, which are not allowed in non-JS modules either + */ + PlatformExtensionsClashResolver.FallbackToDefault(TypeSpecificityComparator.NONE, TypeSpecificityComparator::class.java), + + PlatformExtensionsClashResolver.FallbackToDefault(DynamicTypesSettings(), DynamicTypesSettings::class.java) +) + abstract class PlatformConfiguratorBase( private val dynamicTypesSettings: DynamicTypesSettings? = null, @@ -63,6 +74,7 @@ abstract class PlatformConfiguratorBase( additionalTypeCheckers: List = emptyList(), additionalClassifierUsageCheckers: List = emptyList(), additionalAnnotationCheckers: List = emptyList(), + additionalClashResolvers: List> = emptyList(), private val identifierChecker: IdentifierChecker? = null, private val overloadFilter: OverloadFilter? = null, private val platformToKotlinClassMap: PlatformToKotlinClassMap? = null, @@ -76,6 +88,7 @@ abstract class PlatformConfiguratorBase( private val classifierUsageCheckers: List = DEFAULT_CLASSIFIER_USAGE_CHECKERS + additionalClassifierUsageCheckers private val annotationCheckers: List = DEFAULT_ANNOTATION_CHECKERS + additionalAnnotationCheckers + private val clashResolvers: List> = DEFAULT_CLASH_RESOLVERS + additionalClashResolvers override val platformSpecificContainer = composeContainer(this::class.java.simpleName) { useInstanceIfNotNull(dynamicTypesSettings) @@ -84,6 +97,7 @@ abstract class PlatformConfiguratorBase( typeCheckers.forEach { useInstance(it) } classifierUsageCheckers.forEach { useInstance(it) } annotationCheckers.forEach { useInstance(it) } + clashResolvers.forEach { useClashResolver(it) } useInstanceIfNotNull(identifierChecker) useInstanceIfNotNull(overloadFilter) useInstanceIfNotNull(platformToKotlinClassMap) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PlatformDiagnosticSuppressor.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PlatformDiagnosticSuppressor.kt index 6fa7a9e47ab..6dd3ac1cad4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PlatformDiagnosticSuppressor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/PlatformDiagnosticSuppressor.kt @@ -17,11 +17,13 @@ package org.jetbrains.kotlin.resolve.checkers import org.jetbrains.kotlin.container.DefaultImplementation +import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver +import org.jetbrains.kotlin.container.PlatformSpecificExtension import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor @DefaultImplementation(PlatformDiagnosticSuppressor.Default::class) -interface PlatformDiagnosticSuppressor { +interface PlatformDiagnosticSuppressor : PlatformSpecificExtension{ fun shouldReportUnusedParameter(parameter: VariableDescriptor): Boolean fun shouldReportNoBody(descriptor: CallableMemberDescriptor): Boolean @@ -32,3 +34,18 @@ interface PlatformDiagnosticSuppressor { override fun shouldReportNoBody(descriptor: CallableMemberDescriptor): Boolean = true } } + +class CompositePlatformDiagnosticSuppressor(private val suppressors: List) : PlatformDiagnosticSuppressor { + override fun shouldReportUnusedParameter(parameter: VariableDescriptor): Boolean = + suppressors.all { it.shouldReportUnusedParameter(parameter) } + + override fun shouldReportNoBody(descriptor: CallableMemberDescriptor): Boolean = + suppressors.all { it.shouldReportNoBody(descriptor) } +} + +class PlatformDiagnosticSuppressorClashesResolver : PlatformExtensionsClashResolver( + PlatformDiagnosticSuppressor::class.java +) { + override fun resolveExtensionsClash(extensions: List): PlatformDiagnosticSuppressor = + CompositePlatformDiagnosticSuppressor(extensions) +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/deprecationUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/deprecationUtil.kt index 2c6f9fb0981..bb2f9712fad 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/deprecationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecation/deprecationUtil.kt @@ -8,6 +8,8 @@ package org.jetbrains.kotlin.resolve.deprecation import com.intellij.psi.PsiElement import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.container.DefaultImplementation +import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver +import org.jetbrains.kotlin.container.PlatformSpecificExtension import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors @@ -61,7 +63,7 @@ internal fun createDeprecationDiagnostic( } @DefaultImplementation(CoroutineCompatibilitySupport::class) -class CoroutineCompatibilitySupport private constructor(val enabled: Boolean) { +class CoroutineCompatibilitySupport private constructor(val enabled: Boolean) : PlatformSpecificExtension{ @Suppress("unused") constructor() : this(true) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt index 665372c50d7..8ffbca094c5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -11,6 +11,8 @@ import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.container.DefaultImplementation +import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver +import org.jetbrains.kotlin.container.PlatformSpecificExtension import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor @@ -820,14 +822,19 @@ class DoubleColonExpressionResolver( } } -// By default, function types with big arity are supported. On platforms where they are not supported by default (e.g. JVM), -// LANGUAGE_VERSION_DEPENDENT should be used which makes the code check if the corresponding language feature is enabled. -@DefaultImplementation(FunctionWithBigAritySupport::class) -class FunctionWithBigAritySupport private constructor(val shouldCheckLanguageVersionSettings: Boolean) { - constructor() : this(false) +/** + * By default, function types with big arity are supported. On platforms where they are not supported by default (e.g. JVM), + * [LanguageVersionDependent] should be used which makes the code check if the corresponding language feature is enabled. + */ +@DefaultImplementation(FunctionWithBigAritySupport.Enabled::class) +interface FunctionWithBigAritySupport { + val shouldCheckLanguageVersionSettings: Boolean - companion object { - @JvmField - val LANGUAGE_VERSION_DEPENDENT = FunctionWithBigAritySupport(true) + object Enabled : FunctionWithBigAritySupport { + override val shouldCheckLanguageVersionSettings: Boolean = false } -} + + object LanguageVersionDependent : FunctionWithBigAritySupport { + override val shouldCheckLanguageVersionSettings: Boolean = true + } +} \ No newline at end of file diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignature.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignature.kt index c0d53aa14e8..465d736d301 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignature.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/FlatSignature.kt @@ -17,6 +17,8 @@ package org.jetbrains.kotlin.resolve.calls.results import org.jetbrains.kotlin.container.DefaultImplementation +import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver +import org.jetbrains.kotlin.container.PlatformSpecificExtension import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.MemberDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor @@ -30,7 +32,7 @@ interface SpecificityComparisonCallbacks { } @DefaultImplementation(impl = TypeSpecificityComparator.NONE::class) -interface TypeSpecificityComparator { +interface TypeSpecificityComparator : PlatformSpecificExtension { fun isDefinitelyLessSpecific(specific: KotlinTypeMarker, general: KotlinTypeMarker): Boolean object NONE : TypeSpecificityComparator { diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/SamConversionResolver.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/SamConversionResolver.kt index 93f8ff29c0a..ccb97c0ec79 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/SamConversionResolver.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/components/SamConversionResolver.kt @@ -17,11 +17,13 @@ package org.jetbrains.kotlin.load.java.components import org.jetbrains.kotlin.container.DefaultImplementation +import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver +import org.jetbrains.kotlin.container.PlatformSpecificExtension import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.types.SimpleType @DefaultImplementation(impl = SamConversionResolver.Empty::class) -interface SamConversionResolver { +interface SamConversionResolver : PlatformSpecificExtension { object Empty : SamConversionResolver { override fun resolveFunctionTypeIfSamInterface(classDescriptor: JavaClassDescriptor): SimpleType? = null } diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/dynamicTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/types/dynamicTypes.kt index 04e4ff7fa08..0fd5245fa39 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/dynamicTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/dynamicTypes.kt @@ -18,6 +18,8 @@ package org.jetbrains.kotlin.types import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.container.DefaultImplementation +import org.jetbrains.kotlin.container.PlatformExtensionsClashResolver +import org.jetbrains.kotlin.container.PlatformSpecificExtension import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRendererOptions @@ -25,7 +27,7 @@ import org.jetbrains.kotlin.types.model.DynamicTypeMarker import org.jetbrains.kotlin.types.typeUtil.builtIns @DefaultImplementation(impl = DynamicTypesSettings::class) -open class DynamicTypesSettings { +open class DynamicTypesSettings : PlatformSpecificExtension { open val dynamicTypesAllowed: Boolean get() = false } diff --git a/core/util.runtime/src/org/jetbrains/kotlin/container/DefaultImplementation.kt b/core/util.runtime/src/org/jetbrains/kotlin/container/DefaultImplementation.kt index a919b517603..4b5fec1f665 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/container/DefaultImplementation.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/container/DefaultImplementation.kt @@ -29,5 +29,7 @@ import kotlin.reflect.KClass * * Such configurations may arise, for example, for multiplatform modules: consider analyzing JVM+JS module, where JS contributes * default implementation of some particular service, and JVM contributes non-default. + * + * If you need more fine-grained control of clashes resolution, consider using [PlatformExtensionsClashResolver] **/ annotation class DefaultImplementation(val impl: KClass<*>) \ No newline at end of file diff --git a/core/util.runtime/src/org/jetbrains/kotlin/container/PlatformSpecificExtension.kt b/core/util.runtime/src/org/jetbrains/kotlin/container/PlatformSpecificExtension.kt new file mode 100644 index 00000000000..4777398c48d --- /dev/null +++ b/core/util.runtime/src/org/jetbrains/kotlin/container/PlatformSpecificExtension.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.container + +/** + * This is a marker-interface for components which are needed for common resolve + * facilities (like resolve, or deserialization), but are platform-specific. + * + * PlatformSpecificExtensions has to be present in the container in exactly one + * instance (hence common pattern with providing no-op DEFAULT/EMPTY implementation + * in the corresponding interface) + * + * In multiplatform modules such components require special treatment. Namely, + * if several components of the same type are provided, then it's not an illegal state; + * rather, we have to carefully resolve clash on case-by-case basis. + * See also [PlatformExtensionsClashResolver]. + */ +interface PlatformSpecificExtension> + +/** + * Allows to specify which [PlatformSpecificExtension] should be used if there were two or more registrations + * for [applicableTo] class in the container. + * + * [PlatformExtensionsClashResolver] should be registred in the container via [useClashResolver]-extension. + * + * NB. YOU DON'T NEED this mechanism for the most popular case of "one or several default vs. + * zero or one non-default". Just use [DefaultImplementation], and default instances will be automatically + * discriminated (see respective KDoc). + * Use [PlatformExtensionsClashResolver] only for cases when you need more invloved logic. + * + * Example: [org.jetbrains.kotlin.resolve.IdentifierChecker]. It is used in platform-agnostic code, + * which resolves and checks identifiers for correctness. Each platform has it's own rules + * regarding identifier correctness. In MPP modules we can't choose only one IdentifierChecker; + * instead, we have to provide a "composite" IdentifierChecker which will launch checks of *each* + * platform. + * + */ +abstract class PlatformExtensionsClashResolver>(val applicableTo: Class) { + abstract fun resolveExtensionsClash(extensions: List): E + + class FallbackToDefault>( + private val defaultValue: E, + applicableTo: Class + ) : PlatformExtensionsClashResolver(applicableTo) { + + override fun resolveExtensionsClash(extensions: List): E = defaultValue + } +} +