From d15dae6ac6f605059d6f6285641aa3a03fddb8e4 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 29 Nov 2019 15:56:23 +0300 Subject: [PATCH] FIR: use SyntheticPropertySymbol & synthetic scope instead of FirAccessorSymbol Before this commit, we used FirAccessorSymbol to emulate synthetic properties. These symbols were generated in Java use-site scope. Now, we use synthetic scope instead which is above MemberScopeTowerLevel. This is more performance-friendly and does not require override matching. However, accessor symbols should be used in situation when Java accessor overrides Kotlin property which is broken in this commit (that's why MapEntry test is corrupted here). Also, we should not create synthetics for pure Kotlin accessors (that's why javaAccessorConversion test is corrupted here). --- .../kotlin/fir/backend/Fir2IrVisitor.kt | 3 +- .../scopes/JavaClassUseSiteMemberScope.kt | 24 +---- .../kotlin/fir/resolve/calls/Synthetics.kt | 96 +++++++++---------- .../kotlin/fir/resolve/calls/TowerLevels.kt | 6 +- .../resolve/expresssions/genericDecorator.txt | 2 +- .../expresssions/overriddenJavaGetter.txt | 2 +- .../problems/javaAccessorConversion.kt | 2 +- .../problems/javaAccessorConversion.txt | 2 +- .../testData/resolve/stdlib/j+k/MapEntry.txt | 4 +- .../stdlib/j+k/SyntheticWithForEach.txt | 4 +- .../kotlin/fir/symbols/SyntheticSymbol.kt | 7 +- .../fir/symbols/impl/FirFunctionSymbol.kt | 6 +- .../javaInheritsKotlinProperty/jvm/Test.txt | 2 +- 13 files changed, 76 insertions(+), 84 deletions(-) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt index a326f077986..0d1d73b41c5 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.fir.resolve.firSymbolProvider import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.impl.FirClassSubstitutionScope +import org.jetbrains.kotlin.fir.symbols.AccessorSymbol import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor @@ -670,7 +671,7 @@ class Fir2IrVisitor( return when (this) { is FirPropertyFromParameterResolvedNamedReference -> IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER is FirResolvedNamedReference -> when (resolvedSymbol) { - is FirAccessorSymbol, is SyntheticPropertySymbol -> IrStatementOrigin.GET_PROPERTY + is AccessorSymbol, is SyntheticPropertySymbol -> IrStatementOrigin.GET_PROPERTY else -> null } else -> null diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt index b4c8a6fb456..c869ba96647 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod import org.jetbrains.kotlin.fir.java.declarations.FirJavaValueParameter +import org.jetbrains.kotlin.fir.resolve.calls.possibleGetterNamesByPropertyName import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.ProcessorAction.NEXT @@ -23,10 +24,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirSuperTypeScope import org.jetbrains.kotlin.fir.symbols.CallableId import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef -import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly -import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeFirstWord class JavaClassUseSiteMemberScope( klass: FirRegularClass, @@ -150,28 +148,16 @@ class JavaClassUseSiteMemberScope( } override fun processPropertiesByName(name: Name, processor: (FirCallableSymbol<*>) -> ProcessorAction): ProcessorAction { - if (name.isSpecial) { + // Do not generate accessors at all? + if (true) { return processAccessorFunctionsAndPropertiesByName(name, emptyList(), null, processor) } - val identifier = name.identifier - val capitalizedAsciiName = identifier.capitalizeAsciiOnly() - val capitalizedFirstWordName = identifier.capitalizeFirstWord(asciiOnly = true) - val getterNames = listOfNotNull( - Name.identifier(GETTER_PREFIX + capitalizedAsciiName), - if (capitalizedFirstWordName == capitalizedAsciiName) null else Name.identifier(GETTER_PREFIX + capitalizedFirstWordName), - name.takeIf { identifier.startsWith(IS_PREFIX) } - ).filter { - propertyNameByGetMethodName(it) == name - } - val setterName = Name.identifier(SETTER_PREFIX + identifier.capitalize()) + val getterNames = possibleGetterNamesByPropertyName(name) + val setterName = Name.identifier(SETTER_PREFIX + name.identifier.capitalize()) return processAccessorFunctionsAndPropertiesByName(name, getterNames, setterName, processor) } companion object { - private const val GETTER_PREFIX = "get" - private const val SETTER_PREFIX = "set" - - private const val IS_PREFIX = "is" } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt index 22a8a4da209..a234b43f4ae 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt @@ -7,20 +7,24 @@ package org.jetbrains.kotlin.fir.resolve.calls import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction -import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl -import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter -import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter -import org.jetbrains.kotlin.fir.declarations.impl.FirPropertyImpl -import org.jetbrains.kotlin.fir.declarations.modality -import org.jetbrains.kotlin.fir.declarations.visibility -import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.ProcessorAction -import org.jetbrains.kotlin.fir.symbols.* -import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.symbols.AccessorSymbol +import org.jetbrains.kotlin.fir.symbols.CallableId +import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol +import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly +import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeFirstWord -class SyntheticPropertySymbol(callableId: CallableId) : FirPropertySymbol(callableId), SyntheticSymbol +class SyntheticPropertySymbol( + callableId: CallableId, + override val accessorId: CallableId +) : FirNamedFunctionSymbol(callableId), AccessorSymbol class FirSyntheticFunctionSymbol( callableId: CallableId @@ -28,13 +32,11 @@ class FirSyntheticFunctionSymbol( class FirSyntheticPropertiesScope( val session: FirSession, - private val baseScope: FirScope, - private val typeCalculator: ReturnTypeCalculator + private val baseScope: FirScope ) : FirScope() { val synthetic: MutableMap, FirVariableSymbol<*>> = mutableMapOf() - private fun checkGetAndCreateSynthetic( name: Name, symbol: FirFunctionSymbol<*>, @@ -45,45 +47,41 @@ class FirSyntheticPropertiesScope( if (fir.typeParameters.isNotEmpty()) return ProcessorAction.NEXT if (fir.valueParameters.isNotEmpty()) return ProcessorAction.NEXT - val synthetic = SyntheticPropertySymbol(CallableId(symbol.callableId.packageName, symbol.callableId.className, name)) + val synthetic = SyntheticPropertySymbol( + accessorId = symbol.callableId, + callableId = CallableId(symbol.callableId.packageName, symbol.callableId.className, name) + ) + synthetic.bind(fir) - val returnTypeRef = typeCalculator.tryCalculateReturnType(fir) - val status = FirDeclarationStatusImpl(fir.visibility, fir.modality).apply { - isExpect = false - isActual = false - isOverride = false - isConst = false - isLateInit = false - } - FirPropertyImpl( - null, - session, - returnTypeRef, - null, - name, - null, - null, - true, - synthetic, - false, - status - ).apply { - resolvePhase = fir.resolvePhase - getter = FirDefaultPropertyGetter(null, this@FirSyntheticPropertiesScope.session, returnTypeRef, fir.visibility) - setter = FirDefaultPropertySetter(null, this@FirSyntheticPropertiesScope.session, returnTypeRef, fir.visibility) - } return processor(synthetic) } override fun processPropertiesByName(name: Name, processor: (FirCallableSymbol<*>) -> ProcessorAction): ProcessorAction { - if (name.isSpecial) return ProcessorAction.NEXT - if (baseScope.processFunctionsByName(Name.guessByFirstCharacter("get${name.identifier.capitalize()}")) { - checkGetAndCreateSynthetic(name, it, processor) - }.stop()) return ProcessorAction.STOP - - if (name.asString().startsWith("is") && baseScope.processFunctionsByName(name) { - checkGetAndCreateSynthetic(name, it, processor) - }.stop()) return ProcessorAction.STOP - return super.processPropertiesByName(name, processor) + val getterNames = possibleGetterNamesByPropertyName(name) + for (getterName in getterNames) { + if (baseScope.processFunctionsByName(getterName) { + checkGetAndCreateSynthetic(name, it, processor) + }.stop() + ) return ProcessorAction.STOP + } + return ProcessorAction.NEXT } -} \ No newline at end of file +} + +fun possibleGetterNamesByPropertyName(name: Name): List { + if (name.isSpecial) return emptyList() + val identifier = name.identifier + val capitalizedAsciiName = identifier.capitalizeAsciiOnly() + val capitalizedFirstWordName = identifier.capitalizeFirstWord(asciiOnly = true) + return listOfNotNull( + Name.identifier(GETTER_PREFIX + capitalizedAsciiName), + if (capitalizedFirstWordName == capitalizedAsciiName) null else Name.identifier(GETTER_PREFIX + capitalizedFirstWordName), + name.takeIf { identifier.startsWith(IS_PREFIX) } + ).filter { + propertyNameByGetMethodName(it) == name + } +} + +private const val GETTER_PREFIX = "get" + +private const val IS_PREFIX = "is" diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt index c847c85015d..180d548d19c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt @@ -16,7 +16,6 @@ import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap -import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorWithJump import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractImportingScope @@ -114,7 +113,10 @@ class MemberScopeTowerLevel( } }.stop() ) return ProcessorAction.STOP - return ProcessorAction.NEXT + val withSynthetic = FirSyntheticPropertiesScope(session, scope) + return withSynthetic.processScopeMembers { symbol -> + output.consumeCandidate(symbol, symbol.dispatchReceiverValue(), implicitExtensionReceiver) + } } override fun > processElementsByName( diff --git a/compiler/fir/resolve/testData/resolve/expresssions/genericDecorator.txt b/compiler/fir/resolve/testData/resolve/expresssions/genericDecorator.txt index 23394588f1f..acb550e6810 100644 --- a/compiler/fir/resolve/testData/resolve/expresssions/genericDecorator.txt +++ b/compiler/fir/resolve/testData/resolve/expresssions/genericDecorator.txt @@ -5,7 +5,7 @@ FILE: test.kt } public final override fun getLookupString(): R|kotlin/String| { - ^getLookupString R|/Decorator.delegate|.R|/LookupElement.lookupString| + ^getLookupString this@R|/Decorator|.R|/Decorator.delegate|.R|/LookupElement.lookupString| } } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/overriddenJavaGetter.txt b/compiler/fir/resolve/testData/resolve/expresssions/overriddenJavaGetter.txt index 78fc2c5ca70..4ddeea411b6 100644 --- a/compiler/fir/resolve/testData/resolve/expresssions/overriddenJavaGetter.txt +++ b/compiler/fir/resolve/testData/resolve/expresssions/overriddenJavaGetter.txt @@ -11,6 +11,6 @@ FILE: Derived.kt } public final fun test(): R|kotlin/Unit| { lval d: R|Derived| = R|/Derived.Derived|() - lval res1: R|ft!| = R|/d|.R|/Base.something| + lval res1: R|kotlin/String| = R|/d|.R|/Derived.something| lval res2: R|kotlin/String| = R|/d|.R|/Derived.getSomething|() } diff --git a/compiler/fir/resolve/testData/resolve/problems/javaAccessorConversion.kt b/compiler/fir/resolve/testData/resolve/problems/javaAccessorConversion.kt index 7a2258723c5..3b9feb647a6 100644 --- a/compiler/fir/resolve/testData/resolve/problems/javaAccessorConversion.kt +++ b/compiler/fir/resolve/testData/resolve/problems/javaAccessorConversion.kt @@ -74,7 +74,7 @@ class Foo { } fun test_4(foo: Foo) { - foo.x // should be error + foo.x // should be error } fun test_5(x: D) { diff --git a/compiler/fir/resolve/testData/resolve/problems/javaAccessorConversion.txt b/compiler/fir/resolve/testData/resolve/problems/javaAccessorConversion.txt index 79d58c33d0e..51482dbde15 100644 --- a/compiler/fir/resolve/testData/resolve/problems/javaAccessorConversion.txt +++ b/compiler/fir/resolve/testData/resolve/problems/javaAccessorConversion.txt @@ -22,7 +22,7 @@ FILE: main.kt } public final fun test_4(foo: R|Foo|): R|kotlin/Unit| { - R|/foo|.# + R|/foo|.R|/Foo.x| } public final fun test_5(x: R|D|): R|kotlin/Unit| { R|/x|.R|/D.isGood| diff --git a/compiler/fir/resolve/testData/resolve/stdlib/j+k/MapEntry.txt b/compiler/fir/resolve/testData/resolve/stdlib/j+k/MapEntry.txt index 61f54feae89..e8e6310b102 100644 --- a/compiler/fir/resolve/testData/resolve/stdlib/j+k/MapEntry.txt +++ b/compiler/fir/resolve/testData/resolve/stdlib/j+k/MapEntry.txt @@ -7,7 +7,7 @@ FILE: main.kt } public final fun test(): R|kotlin/Unit| { lval b: R|MyMapEntry| = R|/MyMapEntry.MyMapEntry|() - R|/b|.R|/Test.MapEntryImpl.key| - R|/b|.R|/Test.MapEntryImpl.value| + R|/b|.R|FakeOverride| + R|/b|.R|FakeOverride| R|/b|.R|/Test.MapEntryImpl.setValue|(Null(null)) } diff --git a/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.txt b/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.txt index f087124cb7d..e98dad788f9 100644 --- a/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.txt +++ b/compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.txt @@ -1,11 +1,11 @@ FILE: test.kt public final fun R|Call|.testForEach(): R|kotlin/Unit| { - R|/Call.arguments|.R|FakeOverride|( = forEach@fun (key: R|kotlin/String!|, value: R|kotlin/String!|): R|kotlin/Unit| { + this@R|/Call|.R|/Call.arguments|.R|FakeOverride|( = forEach@fun (key: R|kotlin/String!|, value: R|kotlin/String!|): R|kotlin/Unit| { R|/key|.R|kotlin/String.length| R|/value|.R|kotlin/String.length| } ) - R|/Call.arguments|.R|kotlin/collections/forEach|!|, R|ft!|>( = forEach@fun (it: R|kotlin/collections/Map.Entry!, ft!>|): R|kotlin/Unit| { + this@R|/Call|.R|/Call.arguments|.R|kotlin/collections/forEach|!|, R|ft!|>( = forEach@fun (it: R|kotlin/collections/Map.Entry!, ft!>|): R|kotlin/Unit| { R|/it|.R|FakeOverride!|>|.R|kotlin/String.length| R|/it|.R|FakeOverride!|>|.R|kotlin/String.length| } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/SyntheticSymbol.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/SyntheticSymbol.kt index b6c2f596d13..d29c788f60a 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/SyntheticSymbol.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/SyntheticSymbol.kt @@ -5,4 +5,9 @@ package org.jetbrains.kotlin.fir.symbols -interface SyntheticSymbol \ No newline at end of file +interface SyntheticSymbol + +interface AccessorSymbol : SyntheticSymbol { + val callableId: CallableId // it's an id of related property (synthetic or not) + val accessorId: CallableId // it's an id of accessor function +} \ No newline at end of file diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirFunctionSymbol.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirFunctionSymbol.kt index 89e02fd23cd..eaa5a3a74fd 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirFunctionSymbol.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirFunctionSymbol.kt @@ -6,8 +6,8 @@ package org.jetbrains.kotlin.fir.symbols.impl import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.symbols.AccessorSymbol import org.jetbrains.kotlin.fir.symbols.CallableId -import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -35,8 +35,8 @@ class FirConstructorSymbol( class FirAccessorSymbol( callableId: CallableId, - val accessorId: CallableId -) : FirFunctionSymbol(callableId), SyntheticSymbol + override val accessorId: CallableId +) : FirFunctionSymbol(callableId), AccessorSymbol // ------------------------ unnamed ------------------------ diff --git a/idea/testData/fir/multiModule/javaInheritsKotlinProperty/jvm/Test.txt b/idea/testData/fir/multiModule/javaInheritsKotlinProperty/jvm/Test.txt index a64cae97643..40592906772 100644 --- a/idea/testData/fir/multiModule/javaInheritsKotlinProperty/jvm/Test.txt +++ b/idea/testData/fir/multiModule/javaInheritsKotlinProperty/jvm/Test.txt @@ -5,7 +5,7 @@ FILE: Test.kt } public final fun test(): R|kotlin/Int| { - ^test R|/Inheritor.x| + ^test this@R|/Base|.R|/Base.x| } }