From 2a825f8df18359682249ff5cd7bb4943a69c978d Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 9 Nov 2022 17:55:19 +0100 Subject: [PATCH] K2: implement FirClassAnySynthesizedMemberScope #KT-54844 Fixed --- ...ethodOfAnyImplementedInInterfaceChecker.kt | 2 +- .../FirValueClassDeclarationChecker.kt | 2 +- .../fir/session/ComponentsContainers.kt | 1 + .../fir/scopes/FirKotlinScopeProvider.kt | 21 +- .../impl/FirClassAnySynthesizedMemberScope.kt | 199 ++++++++++++++++++ .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 5 +- .../kotlin/fir/resolve/DeclarationUtils.kt | 9 - .../fir/symbols/impl/FirCallableSymbol.kt | 10 + .../codegen/box/dataClasses/kt49715.kt | 2 - .../box/dataClasses/kt49715_behaviorChange.kt | 3 - 10 files changed, 230 insertions(+), 24 deletions(-) create mode 100644 compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassAnySynthesizedMemberScope.kt diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMethodOfAnyImplementedInInterfaceChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMethodOfAnyImplementedInInterfaceChecker.kt index 673f077c9fc..e93b9c9ab0b 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMethodOfAnyImplementedInInterfaceChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMethodOfAnyImplementedInInterfaceChecker.kt @@ -16,7 +16,7 @@ import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.utils.hasBody import org.jetbrains.kotlin.fir.declarations.utils.isInterface import org.jetbrains.kotlin.fir.declarations.utils.isOverride -import org.jetbrains.kotlin.fir.resolve.isEquals +import org.jetbrains.kotlin.fir.symbols.impl.isEquals import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.util.OperatorNameConventions.TO_STRING diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirValueClassDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirValueClassDeclarationChecker.kt index 06d9774ae7f..337fd28be92 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirValueClassDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirValueClassDeclarationChecker.kt @@ -22,9 +22,9 @@ import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType -import org.jetbrains.kotlin.fir.resolve.isEquals import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol +import org.jetbrains.kotlin.fir.symbols.impl.isEquals import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef import org.jetbrains.kotlin.lexer.KtTokens 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 652e5ee8a11..c7bce3d24cd 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 @@ -73,6 +73,7 @@ fun FirSession.registerCommonComponents(languageVersionSettings: LanguageVersion register(FirSubstitutionOverrideStorage::class, FirSubstitutionOverrideStorage(this)) register(FirIntersectionOverrideStorage::class, FirIntersectionOverrideStorage(this)) + register(FirSynthesizedStorage::class, FirSynthesizedStorage(this)) register(FirGeneratedMemberDeclarationsStorage::class, FirGeneratedMemberDeclarationsStorage(this)) register(FirSamConstructorStorage::class, FirSamConstructorStorage(this)) register(FirOverrideService::class, FirOverrideService(this)) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirKotlinScopeProvider.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirKotlinScopeProvider.kt index ea402fc77d0..6288c773897 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirKotlinScopeProvider.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirKotlinScopeProvider.kt @@ -12,8 +12,7 @@ import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirResolvePhase -import org.jetbrains.kotlin.fir.declarations.utils.delegateFields -import org.jetbrains.kotlin.fir.declarations.utils.isExpect +import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.substitution.ConeRawScopeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor @@ -65,13 +64,23 @@ class FirKotlinScopeProvider( ).mapNotNull { useSiteSuperType -> useSiteSuperType.scopeForSupertype(useSiteSession, scopeSession, klass, memberRequiredPhase = memberRequiredPhase) } - - FirClassUseSiteMemberScope( + val useSiteMemberScope = FirClassUseSiteMemberScope( klass, useSiteSession, scopes, decoratedDeclaredMemberScope, ) + if (klass is FirRegularClass && !klass.isExpect && (klass.isData || klass.isInline)) { + val lookupTag = klass.symbol.toLookupTag() + scopeSession.getOrBuild(lookupTag, AnySynthesizedScopeKey(lookupTag)) { + FirClassAnySynthesizedMemberScope( + useSiteSession, useSiteMemberScope, lookupTag, + klass.moduleData, klass.defaultType(), klass.source + ) + } + } else { + useSiteMemberScope + } } } @@ -110,6 +119,10 @@ data class ConeSubstitutionScopeKey( val derivedClassLookupTag: ConeClassLikeLookupTag? ) : ScopeSessionKey() +data class AnySynthesizedScopeKey( + val lookupTag: ConeClassLikeLookupTag +) : ScopeSessionKey() + fun FirClass.unsubstitutedScope( useSiteSession: FirSession, scopeSession: ScopeSession, diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassAnySynthesizedMemberScope.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassAnySynthesizedMemberScope.kt new file mode 100644 index 00000000000..1767867ac08 --- /dev/null +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirClassAnySynthesizedMemberScope.kt @@ -0,0 +1,199 @@ +/* + * Copyright 2010-2023 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.KtFakeSourceElementKind +import org.jetbrains.kotlin.KtSourceElement +import org.jetbrains.kotlin.descriptors.EffectiveVisibility +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.fakeElement +import org.jetbrains.kotlin.fir.FirModuleData +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.FirCachesFactory +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.FirResolvePhase +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.declarations.builder.FirSimpleFunctionBuilder +import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction +import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter +import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.scopes.FirTypeScope +import org.jetbrains.kotlin.fir.scopes.ProcessorAction +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.impl.FirImplicitBooleanTypeRef +import org.jetbrains.kotlin.fir.types.impl.FirImplicitIntTypeRef +import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef +import org.jetbrains.kotlin.fir.types.impl.FirImplicitStringTypeRef +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled + +class FirClassAnySynthesizedMemberScope( + session: FirSession, + private val useSiteMemberScope: FirClassUseSiteMemberScope, + private val lookupTag: ConeClassLikeLookupTag, + private val baseModuleData: FirModuleData, + private val dispatchReceiverType: ConeClassLikeType, + classSource: KtSourceElement?, +) : FirTypeScope() { + private val synthesizedCache = session.synthesizedStorage.synthesizedCacheByScope.getValue(lookupTag, null) + + private val synthesizedOverrides = mutableMapOf() + + private val synthesizedSource = classSource?.fakeElement(KtFakeSourceElementKind.DataClassGeneratedMembers) + + override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) { + useSiteMemberScope.processClassifiersByNameWithSubstitution(name, processor) + } + + override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) { + useSiteMemberScope.processDeclaredConstructors(processor) + } + + override fun processDirectOverriddenPropertiesWithBaseScope( + propertySymbol: FirPropertySymbol, + processor: (FirPropertySymbol, FirTypeScope) -> ProcessorAction + ): ProcessorAction { + return useSiteMemberScope.processDirectOverriddenPropertiesWithBaseScope(propertySymbol, processor) + } + + override fun processDirectOverriddenFunctionsWithBaseScope( + functionSymbol: FirNamedFunctionSymbol, + processor: (FirNamedFunctionSymbol, FirTypeScope) -> ProcessorAction, + ): ProcessorAction { + val overridden = synthesizedOverrides[functionSymbol] + ?: return useSiteMemberScope.processDirectOverriddenFunctionsWithBaseScope(functionSymbol, processor) + return processor(overridden, useSiteMemberScope) + } + + override fun getCallableNames(): Set { + return useSiteMemberScope.getCallableNames() + } + + override fun getClassifierNames(): Set { + return useSiteMemberScope.getClassifierNames() + } + + override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) { + useSiteMemberScope.processPropertiesByName(name, processor) + } + + override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) { + if (name !in ANY_MEMBER_NAMES) { + useSiteMemberScope.processFunctionsByName(name, processor) + return + } + useSiteMemberScope.processFunctionsByName(name) { fromUseSiteScope -> + if (fromUseSiteScope.rawStatus.modality == Modality.FINAL) { + processor(fromUseSiteScope) + } else { + val matchedSomeAnyMember = when (name) { + OperatorNameConventions.HASH_CODE, OperatorNameConventions.TO_STRING -> { + fromUseSiteScope.valueParameterSymbols.isEmpty() && !fromUseSiteScope.isExtension && + fromUseSiteScope.fir.contextReceivers.isEmpty() + } + else -> { + fromUseSiteScope.lazyResolveToPhase(FirResolvePhase.TYPES) + fromUseSiteScope.fir.isEquals() + } + } + val hasSameReceiver = + dispatchReceiverType.lookupTag == (fromUseSiteScope.dispatchReceiverType as? ConeClassLikeType)?.lookupTag + if (!matchedSomeAnyMember || hasSameReceiver) { + processor(fromUseSiteScope) + } else { + val synthesized = synthesizedCache.synthesizedFunctionAndOverrides.getValue(name, this) + synthesizedOverrides[synthesized] = fromUseSiteScope + processor(synthesized) + } + } + } + } + + internal fun generateSyntheticFunctionByName(name: Name): FirNamedFunctionSymbol = + when (name) { + OperatorNameConventions.EQUALS -> generateEqualsFunction() + OperatorNameConventions.HASH_CODE -> generateHashCodeFunction() + OperatorNameConventions.TO_STRING -> generateToStringFunction() + else -> shouldNotBeCalled() + }.symbol + + private fun generateEqualsFunction(): FirSimpleFunction = + buildSimpleFunction { + generateSyntheticFunction(OperatorNameConventions.EQUALS, isOperator = true) + returnTypeRef = FirImplicitBooleanTypeRef(source) + this.valueParameters.add( + buildValueParameter { + this.name = Name.identifier("other") + origin = FirDeclarationOrigin.Synthetic + moduleData = baseModuleData + this.returnTypeRef = FirImplicitNullableAnyTypeRef(null) + this.symbol = FirValueParameterSymbol(this.name) + containingFunctionSymbol = this@buildSimpleFunction.symbol + isCrossinline = false + isNoinline = false + isVararg = false + } + ) + } + + private fun generateHashCodeFunction(): FirSimpleFunction = + buildSimpleFunction { + generateSyntheticFunction(OperatorNameConventions.HASH_CODE) + returnTypeRef = FirImplicitIntTypeRef(source) + } + + private fun generateToStringFunction(): FirSimpleFunction = + buildSimpleFunction { + generateSyntheticFunction(OperatorNameConventions.TO_STRING) + returnTypeRef = FirImplicitStringTypeRef(source) + } + + private fun FirSimpleFunctionBuilder.generateSyntheticFunction( + name: Name, + isOperator: Boolean = false, + ) { + this.source = synthesizedSource + moduleData = baseModuleData + origin = FirDeclarationOrigin.Synthetic + this.name = name + status = FirResolvedDeclarationStatusImpl(Visibilities.Public, Modality.OPEN, EffectiveVisibility.Public).apply { + this.isOperator = isOperator + } + symbol = FirNamedFunctionSymbol(CallableId(lookupTag.classId, name)) + dispatchReceiverType = this@FirClassAnySynthesizedMemberScope.dispatchReceiverType + } + + companion object { + private val ANY_MEMBER_NAMES = hashSetOf( + OperatorNameConventions.HASH_CODE, OperatorNameConventions.EQUALS, OperatorNameConventions.TO_STRING + ) + } +} + +class FirSynthesizedStorage(val session: FirSession) : FirSessionComponent { + private val cachesFactory = session.firCachesFactory + + val synthesizedCacheByScope: FirCache = + cachesFactory.createCache { _ -> SynthesizedCache(session.firCachesFactory) } + + class SynthesizedCache(cachesFactory: FirCachesFactory) { + val synthesizedFunctionAndOverrides: FirCache = + cachesFactory.createCache { name, scope -> scope.generateSyntheticFunctionByName(name) } + } +} + +private val FirSession.synthesizedStorage: FirSynthesizedStorage by FirSession.sessionComponentAccessor() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index 5c296835d34..8f734501280 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -30,10 +30,7 @@ import org.jetbrains.kotlin.fir.scopes.getFunctions import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope import org.jetbrains.kotlin.fir.scopes.impl.toConeType import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol +import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.types.ConstantValueKind diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/DeclarationUtils.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/DeclarationUtils.kt index 89fc5f6c9b6..7d9597df97d 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/DeclarationUtils.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/DeclarationUtils.kt @@ -87,12 +87,3 @@ var FirConstructor.originalConstructorIfTypeAlias: FirConstructor? by FirDeclara val FirConstructorSymbol.isTypeAliasedConstructor: Boolean get() = fir.originalConstructorIfTypeAlias != null - -fun FirSimpleFunction.isEquals(): Boolean { - if (name != OperatorNameConventions.EQUALS) return false - if (valueParameters.size != 1) return false - if (contextReceivers.isNotEmpty()) return false - if (receiverParameter != null) return false - val parameter = valueParameters.first() - return parameter.returnTypeRef.isNullableAny -} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirCallableSymbol.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirCallableSymbol.kt index eff8345987b..6d590ace061 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirCallableSymbol.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirCallableSymbol.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.mpp.CallableSymbolMarker import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.util.OperatorNameConventions abstract class FirCallableSymbol : FirBasedSymbol(), CallableSymbolMarker { abstract val callableId: CallableId @@ -95,3 +96,12 @@ val FirCallableSymbol<*>.isExtension: Boolean is FirProperty -> fir.receiverParameter != null is FirVariable -> false } + +fun FirSimpleFunction.isEquals(): Boolean { + if (name != OperatorNameConventions.EQUALS) return false + if (valueParameters.size != 1) return false + if (contextReceivers.isNotEmpty()) return false + if (receiverParameter != null) return false + val parameter = valueParameters.first() + return parameter.returnTypeRef.isNullableAny +} diff --git a/compiler/testData/codegen/box/dataClasses/kt49715.kt b/compiler/testData/codegen/box/dataClasses/kt49715.kt index 28fde705b8e..04001fec0d1 100644 --- a/compiler/testData/codegen/box/dataClasses/kt49715.kt +++ b/compiler/testData/codegen/box/dataClasses/kt49715.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND_K2: JVM_IR, JS_IR, NATIVE -// FIR status: [IR VALIDATION] Duplicate IR node: FUN GENERATED_DATA_CLASS_MEMBER name:toString // CHECK_BYTECODE_LISTING // FIR_IDENTICAL // WITH_STDLIB diff --git a/compiler/testData/codegen/box/dataClasses/kt49715_behaviorChange.kt b/compiler/testData/codegen/box/dataClasses/kt49715_behaviorChange.kt index 9807d38b4d9..95702c1d85a 100644 --- a/compiler/testData/codegen/box/dataClasses/kt49715_behaviorChange.kt +++ b/compiler/testData/codegen/box/dataClasses/kt49715_behaviorChange.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND_K2: JVM_IR, JS_IR, NATIVE -// FIR status: [IR VALIDATION] Duplicate IR node: FUN GENERATED_DATA_CLASS_MEMBER name:equals - interface A { fun Any.equals(other: Any?): Boolean = false fun Any.hashCode(): Int = 0