diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt index d4657c348f9..91eea9c61bc 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.fir.resolve.transformers.plugin.GeneratedClassIndex import org.jetbrains.kotlin.fir.scopes.FirOverrideService import org.jetbrains.kotlin.fir.scopes.FirPlatformClassMapper import org.jetbrains.kotlin.fir.scopes.impl.FirDeclaredMemberScopeProvider +import org.jetbrains.kotlin.fir.scopes.impl.FirDynamicMembersStorage import org.jetbrains.kotlin.fir.scopes.impl.FirIntersectionOverrideStorage import org.jetbrains.kotlin.fir.scopes.impl.FirSubstitutionOverrideStorage import org.jetbrains.kotlin.fir.symbols.FirPhaseManager @@ -66,6 +67,7 @@ fun FirSession.registerCommonComponents(languageVersionSettings: LanguageVersion register(FirIntersectionOverrideStorage::class, FirIntersectionOverrideStorage(this)) register(FirSamConstructorStorage::class, FirSamConstructorStorage(this)) register(FirOverrideService::class, FirOverrideService(this)) + register(FirDynamicMembersStorage::class, FirDynamicMembersStorage(this)) } @OptIn(SessionConfiguration::class) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt index 5f87afed2df..48cc3beaca0 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt @@ -14,10 +14,7 @@ import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator import org.jetbrains.kotlin.fir.scopes.FirTypeScope import org.jetbrains.kotlin.fir.scopes.FirUnstableSmartcastTypeScope -import org.jetbrains.kotlin.fir.scopes.impl.FirScopeWithFakeOverrideTypeCalculator -import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker -import org.jetbrains.kotlin.fir.scopes.impl.FirTypeIntersectionScope -import org.jetbrains.kotlin.fir.scopes.impl.getOrBuildScopeForIntegerConstantOperatorType +import org.jetbrains.kotlin.fir.scopes.impl.* import org.jetbrains.kotlin.fir.scopes.scopeForClass import org.jetbrains.kotlin.fir.symbols.ensureResolved import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl @@ -81,6 +78,7 @@ private fun ConeKotlinType.scope(useSiteSession: FirSession, scopeSession: Scope } } is ConeRawType -> lowerBound.scope(useSiteSession, scopeSession, requiredPhase) + is ConeDynamicType -> useSiteSession.dynamicMembersStorage.getDynamicScopeFor(scopeSession) is ConeFlexibleType -> lowerBound.scope(useSiteSession, scopeSession, requiredPhase) is ConeIntersectionType -> FirTypeIntersectionScope.prepareIntersectionScope( useSiteSession, diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDynamicScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDynamicScope.kt new file mode 100644 index 00000000000..edb996211ad --- /dev/null +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDynamicScope.kt @@ -0,0 +1,189 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.fir.scopes.impl + +import org.jetbrains.kotlin.builtins.StandardNames.DYNAMIC_FQ_NAME +import org.jetbrains.kotlin.descriptors.EffectiveVisibility +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.FirSessionComponent +import org.jetbrains.kotlin.fir.caches.FirCache +import org.jetbrains.kotlin.fir.caches.createCache +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.declarations.builder.* +import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl +import org.jetbrains.kotlin.fir.expressions.FirOperationNameConventions +import org.jetbrains.kotlin.fir.moduleData +import org.jetbrains.kotlin.fir.resolve.ScopeSession +import org.jetbrains.kotlin.fir.resolve.scope +import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator +import org.jetbrains.kotlin.fir.scopes.FirTypeScope +import org.jetbrains.kotlin.fir.scopes.ProcessorAction +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.types.ConeDynamicType +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.create +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.StandardClassIds + +@RequiresOptIn(message = "Please, don't create FirDynamicScope-s manually") +annotation class FirDynamicScopeConstructor + +class FirDynamicScope @FirDynamicScopeConstructor constructor( + private val session: FirSession, + private val scopeSession: ScopeSession, +) : FirTypeScope() { + override fun processDirectOverriddenFunctionsWithBaseScope( + functionSymbol: FirNamedFunctionSymbol, + processor: (FirNamedFunctionSymbol, FirTypeScope) -> ProcessorAction + ): ProcessorAction = ProcessorAction.NEXT + + override fun processDirectOverriddenPropertiesWithBaseScope( + propertySymbol: FirPropertySymbol, + processor: (FirPropertySymbol, FirTypeScope) -> ProcessorAction + ): ProcessorAction = ProcessorAction.NEXT + + override fun getCallableNames(): Set = emptySet() + + override fun getClassifierNames(): Set = emptySet() + + private val anyTypeScope by lazy { + session.builtinTypes.anyType.type.scope(session, scopeSession, FakeOverrideTypeCalculator.DoNothing) + } + + override fun processFunctionsByName( + name: Name, + processor: (FirNamedFunctionSymbol) -> Unit + ) { + var foundMemberInAny = false + + anyTypeScope?.processFunctionsByName(name) { + foundMemberInAny = true + processor(it) + } + + if (foundMemberInAny) { + return + } + + session.dynamicMembersStorage.functionsCacheByName.getValue(name, null).also { + processor(it.symbol) + } + } + + override fun processPropertiesByName( + name: Name, + processor: (FirVariableSymbol<*>) -> Unit + ) { + var foundMemberInAny = false + + anyTypeScope?.processPropertiesByName(name) { + foundMemberInAny = true + processor(it) + } + + if (foundMemberInAny) { + return + } + + session.dynamicMembersStorage.propertiesCacheByName.getValue(name, null).also { + processor(it.symbol) + } + } +} + +class FirDynamicMembersStorage(val session: FirSession) : FirSessionComponent { + private val cachesFactory = session.firCachesFactory + + @OptIn(FirDynamicScopeConstructor::class) + private val dynamicScopeCacheByScope: FirCache = + cachesFactory.createCache { it -> FirDynamicScope(session, it) } + + fun getDynamicScopeFor(scopeSession: ScopeSession) = dynamicScopeCacheByScope.getValue(scopeSession, null) + + val functionsCacheByName: FirCache = + cachesFactory.createCache { name -> buildPseudoFunctionByName(name) } + + val propertiesCacheByName: FirCache = + cachesFactory.createCache { name -> buildPseudoPropertyByName(name) } + + private val dynamicTypeRef = buildResolvedTypeRef { + type = ConeDynamicType.create(session) + } + + private val anyArrayTypeRef = buildResolvedTypeRef { + type = ConeClassLikeTypeImpl( + ConeClassLikeLookupTagImpl(StandardClassIds.Array), + arrayOf(session.builtinTypes.nullableAnyType.coneType), + isNullable = false + ) + } + + private fun buildPseudoFunctionByName(name: Name) = buildSimpleFunction { + status = FirResolvedDeclarationStatusImpl( + Visibilities.Public, + Modality.FINAL, + EffectiveVisibility.Public, + ).apply { + isInfix = true + isOperator = true + } + + this.name = name + this.symbol = FirNamedFunctionSymbol(CallableId(DYNAMIC_FQ_NAME, this.name)) + + moduleData = session.moduleData + origin = FirDeclarationOrigin.DynamicScope + resolvePhase = FirResolvePhase.BODY_RESOLVE + + returnTypeRef = if (name in FirOperationNameConventions.ASSIGNMENT_NAMES) { + session.builtinTypes.unitType + } else { + dynamicTypeRef + } + + val parameter = buildValueParameter { + moduleData = session.moduleData + origin = FirDeclarationOrigin.DynamicScope + returnTypeRef = anyArrayTypeRef + this.name = Name.identifier("args") + this.symbol = FirValueParameterSymbol(this.name) + isCrossinline = false + isNoinline = false + isVararg = true + } + + valueParameters.add(parameter) + } + + private fun buildPseudoPropertyByName(name: Name) = buildProperty { + this.name = name + this.symbol = FirPropertySymbol(CallableId(DYNAMIC_FQ_NAME, this.name)) + + status = FirResolvedDeclarationStatusImpl( + Visibilities.Public, + Modality.FINAL, + EffectiveVisibility.Public, + ) + + moduleData = session.moduleData + origin = FirDeclarationOrigin.DynamicScope + resolvePhase = FirResolvePhase.BODY_RESOLVE + returnTypeRef = dynamicTypeRef + isVar = true + isLocal = false + } +} + +val FirSession.dynamicMembersStorage: FirDynamicMembersStorage by FirSession.sessionComponentAccessor() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index 1f995dc5a8d..964b0a65ffb 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -522,6 +522,7 @@ fun FirFunction.getAsForbiddenNamedArgumentsTarget(session: FirSession): Forbidd } } FirDeclarationOrigin.Synthetic -> null + FirDeclarationOrigin.DynamicScope -> null FirDeclarationOrigin.RenamedForOverride -> null FirDeclarationOrigin.WrappedIntegerOperator -> null is FirDeclarationOrigin.Plugin -> null // TODO: figure out what to do with plugin generated functions diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt index 3d8b4a6c9b7..4130ffe8933 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt @@ -20,6 +20,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) { CheckDslScopeViolation, CheckLowPriorityInOverloadResolution, PostponedVariablesInitializerResolutionStage, + LowerPriorityIfDynamic, ConstraintSystemForks, CheckIncompatibleTypeVariableUpperBounds, ) @@ -53,6 +54,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) { EagerResolveOfCallableReferences, CheckLowPriorityInOverloadResolution, PostponedVariablesInitializerResolutionStage, + LowerPriorityIfDynamic, ConstraintSystemForks, CheckIncompatibleTypeVariableUpperBounds, ) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirArgumentsToParametersMapper.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirArgumentsToParametersMapper.kt index 54cc7d7413a..9a7adbeb30b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirArgumentsToParametersMapper.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirArgumentsToParametersMapper.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.resolve.calls import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirValueParameter @@ -79,7 +80,11 @@ fun BodyResolveComponents.mapArguments( // If this is an indexed access set operator, it could have default values or a vararg parameter in the middle. // For proper argument mapping, wrap the last one, which is supposed to be the updated value, as a named argument. - val isIndexedSetOperator = function is FirSimpleFunction && function.isOperator && function.name == OperatorNameConventions.SET + val isIndexedSetOperator = function is FirSimpleFunction + && function.isOperator + && function.name == OperatorNameConventions.SET + && function.origin !is FirDeclarationOrigin.DynamicScope + if (isIndexedSetOperator && function.valueParameters.any { it.defaultValue != null || it.isVararg } ) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt index c8f7f18629a..4b619042077 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt @@ -644,6 +644,14 @@ internal object CheckDeprecatedSinceKotlin : ResolutionStage() { } } +internal object LowerPriorityIfDynamic : ResolutionStage() { + override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) { + if (candidate.symbol.origin is FirDeclarationOrigin.DynamicScope) { + candidate.addDiagnostic(LowerPriorityForDynamic) + } + } +} + internal object ConstraintSystemForks : ResolutionStage() { override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) { if (candidate.system.hasContradiction) return diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TypeArgumentMapping.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TypeArgumentMapping.kt index c411aa4e58b..3eb0c6594c9 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TypeArgumentMapping.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TypeArgumentMapping.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.fir.resolve.calls +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner import org.jetbrains.kotlin.fir.types.FirTypeProjection import org.jetbrains.kotlin.fir.types.builder.buildPlaceholderProjection @@ -33,7 +35,11 @@ internal object MapTypeArguments : ResolutionStage() { val owner = candidate.symbol.fir as FirTypeParameterRefsOwner - if (typeArguments.size == owner.typeParameters.size || callInfo.callKind == CallKind.DelegatingConstructorCall) { + if ( + typeArguments.size == owner.typeParameters.size || + callInfo.callKind == CallKind.DelegatingConstructorCall || + (owner as? FirDeclaration)?.origin is FirDeclarationOrigin.DynamicScope + ) { candidate.typeArgumentMapping = TypeArgumentMapping.Mapped(typeArguments) } else { candidate.typeArgumentMapping = TypeArgumentMapping.Mapped(emptyList()) diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt index 47a3fef3ebf..c074029d8bc 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt @@ -89,6 +89,8 @@ class UnsafeCall(val actualType: ConeKotlinType) : ResolutionDiagnostic(UNSAFE_C object LowerPriorityToPreserveCompatibilityDiagnostic : ResolutionDiagnostic(RESOLVED_NEED_PRESERVE_COMPATIBILITY) +object LowerPriorityForDynamic : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY) + object CandidateChosenUsingOverloadResolutionByLambdaAnnotation : ResolutionDiagnostic(RESOLVED) class UnstableSmartCast(val argument: FirExpressionWithSmartcast, val targetType: ConeKotlinType, val isCastToNotNull: Boolean) : diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt index 37e3189c889..5d3b1867e37 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin.kt @@ -12,6 +12,7 @@ sealed class FirDeclarationOrigin(private val displayName: String? = null, val f object BuiltIns : FirDeclarationOrigin() object Java : FirDeclarationOrigin() object Synthetic : FirDeclarationOrigin() + object DynamicScope : FirDeclarationOrigin() object SamConstructor : FirDeclarationOrigin() object Enhancement : FirDeclarationOrigin() object ImportedFromObject : FirDeclarationOrigin() diff --git a/core/compiler.common/src/org/jetbrains/kotlin/builtins/StandardNames.kt b/core/compiler.common/src/org/jetbrains/kotlin/builtins/StandardNames.kt index 8a5a7a83eaa..c15e6a11fc6 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/builtins/StandardNames.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/builtins/StandardNames.kt @@ -32,6 +32,8 @@ object StandardNames { @JvmField val CONTEXT_FUNCTION_TYPE_PARAMETER_COUNT_NAME = Name.identifier("count") + @JvmField val DYNAMIC_FQ_NAME = FqName("") + @JvmField val COROUTINES_PACKAGE_FQ_NAME = FqName("kotlin.coroutines") @JvmField val COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME = FqName("kotlin.coroutines.jvm.internal")