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).
This commit is contained in:
Mikhail Glukhikh
2019-11-29 15:56:23 +03:00
parent ae62ccb76d
commit d15dae6ac6
13 changed files with 76 additions and 84 deletions
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.impl.FirClassSubstitutionScope 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.symbols.impl.*
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
@@ -670,7 +671,7 @@ class Fir2IrVisitor(
return when (this) { return when (this) {
is FirPropertyFromParameterResolvedNamedReference -> IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER is FirPropertyFromParameterResolvedNamedReference -> IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
is FirResolvedNamedReference -> when (resolvedSymbol) { is FirResolvedNamedReference -> when (resolvedSymbol) {
is FirAccessorSymbol, is SyntheticPropertySymbol -> IrStatementOrigin.GET_PROPERTY is AccessorSymbol, is SyntheticPropertySymbol -> IrStatementOrigin.GET_PROPERTY
else -> null else -> null
} }
else -> null else -> null
@@ -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.FirJavaClass
import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod
import org.jetbrains.kotlin.fir.java.declarations.FirJavaValueParameter 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.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.NEXT 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.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef 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.name.Name
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeFirstWord
class JavaClassUseSiteMemberScope( class JavaClassUseSiteMemberScope(
klass: FirRegularClass, klass: FirRegularClass,
@@ -150,28 +148,16 @@ class JavaClassUseSiteMemberScope(
} }
override fun processPropertiesByName(name: Name, processor: (FirCallableSymbol<*>) -> ProcessorAction): ProcessorAction { 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) return processAccessorFunctionsAndPropertiesByName(name, emptyList(), null, processor)
} }
val identifier = name.identifier val getterNames = possibleGetterNamesByPropertyName(name)
val capitalizedAsciiName = identifier.capitalizeAsciiOnly() val setterName = Name.identifier(SETTER_PREFIX + name.identifier.capitalize())
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())
return processAccessorFunctionsAndPropertiesByName(name, getterNames, setterName, processor) return processAccessorFunctionsAndPropertiesByName(name, getterNames, setterName, processor)
} }
companion object { companion object {
private const val GETTER_PREFIX = "get"
private const val SETTER_PREFIX = "set" private const val SETTER_PREFIX = "set"
private const val IS_PREFIX = "is"
} }
} }
@@ -7,20 +7,24 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction 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.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.* import org.jetbrains.kotlin.fir.symbols.AccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.* 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.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( class FirSyntheticFunctionSymbol(
callableId: CallableId callableId: CallableId
@@ -28,13 +32,11 @@ class FirSyntheticFunctionSymbol(
class FirSyntheticPropertiesScope( class FirSyntheticPropertiesScope(
val session: FirSession, val session: FirSession,
private val baseScope: FirScope, private val baseScope: FirScope
private val typeCalculator: ReturnTypeCalculator
) : FirScope() { ) : FirScope() {
val synthetic: MutableMap<FirCallableSymbol<*>, FirVariableSymbol<*>> = mutableMapOf() val synthetic: MutableMap<FirCallableSymbol<*>, FirVariableSymbol<*>> = mutableMapOf()
private fun checkGetAndCreateSynthetic( private fun checkGetAndCreateSynthetic(
name: Name, name: Name,
symbol: FirFunctionSymbol<*>, symbol: FirFunctionSymbol<*>,
@@ -45,45 +47,41 @@ class FirSyntheticPropertiesScope(
if (fir.typeParameters.isNotEmpty()) return ProcessorAction.NEXT if (fir.typeParameters.isNotEmpty()) return ProcessorAction.NEXT
if (fir.valueParameters.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) return processor(synthetic)
} }
override fun processPropertiesByName(name: Name, processor: (FirCallableSymbol<*>) -> ProcessorAction): ProcessorAction { override fun processPropertiesByName(name: Name, processor: (FirCallableSymbol<*>) -> ProcessorAction): ProcessorAction {
if (name.isSpecial) return ProcessorAction.NEXT val getterNames = possibleGetterNamesByPropertyName(name)
if (baseScope.processFunctionsByName(Name.guessByFirstCharacter("get${name.identifier.capitalize()}")) { for (getterName in getterNames) {
checkGetAndCreateSynthetic(name, it, processor) if (baseScope.processFunctionsByName(getterName) {
}.stop()) return ProcessorAction.STOP checkGetAndCreateSynthetic(name, it, processor)
}.stop()
if (name.asString().startsWith("is") && baseScope.processFunctionsByName(name) { ) return ProcessorAction.STOP
checkGetAndCreateSynthetic(name, it, processor) }
}.stop()) return ProcessorAction.STOP return ProcessorAction.NEXT
return super.processPropertiesByName(name, processor)
} }
} }
fun possibleGetterNamesByPropertyName(name: Name): List<Name> {
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"
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap 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.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractImportingScope import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractImportingScope
@@ -114,7 +113,10 @@ class MemberScopeTowerLevel(
} }
}.stop() }.stop()
) return ProcessorAction.STOP ) return ProcessorAction.STOP
return ProcessorAction.NEXT val withSynthetic = FirSyntheticPropertiesScope(session, scope)
return withSynthetic.processScopeMembers { symbol ->
output.consumeCandidate(symbol, symbol.dispatchReceiverValue(), implicitExtensionReceiver)
}
} }
override fun <T : AbstractFirBasedSymbol<*>> processElementsByName( override fun <T : AbstractFirBasedSymbol<*>> processElementsByName(
@@ -5,7 +5,7 @@ FILE: test.kt
} }
public final override fun getLookupString(): R|kotlin/String| { 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|
} }
} }
@@ -11,6 +11,6 @@ FILE: Derived.kt
} }
public final fun test(): R|kotlin/Unit| { public final fun test(): R|kotlin/Unit| {
lval d: R|Derived| = R|/Derived.Derived|() lval d: R|Derived| = R|/Derived.Derived|()
lval res1: R|ft<kotlin/String, kotlin/String?>!| = R|<local>/d|.R|/Base.something| lval res1: R|kotlin/String| = R|<local>/d|.R|/Derived.something|
lval res2: R|kotlin/String| = R|<local>/d|.R|/Derived.getSomething|() lval res2: R|kotlin/String| = R|<local>/d|.R|/Derived.getSomething|()
} }
@@ -74,7 +74,7 @@ class Foo {
} }
fun test_4(foo: Foo) { fun test_4(foo: Foo) {
foo.<!UNRESOLVED_REFERENCE!>x<!> // should be error foo.x // should be error
} }
fun test_5(x: D) { fun test_5(x: D) {
@@ -22,7 +22,7 @@ FILE: main.kt
} }
public final fun test_4(foo: R|Foo|): R|kotlin/Unit| { public final fun test_4(foo: R|Foo|): R|kotlin/Unit| {
R|<local>/foo|.<Unresolved name: x># R|<local>/foo|.R|/Foo.x|
} }
public final fun test_5(x: R|D|): R|kotlin/Unit| { public final fun test_5(x: R|D|): R|kotlin/Unit| {
R|<local>/x|.R|/D.isGood| R|<local>/x|.R|/D.isGood|
@@ -7,7 +7,7 @@ FILE: main.kt
} }
public final fun test(): R|kotlin/Unit| { public final fun test(): R|kotlin/Unit| {
lval b: R|MyMapEntry| = R|/MyMapEntry.MyMapEntry|() lval b: R|MyMapEntry| = R|/MyMapEntry.MyMapEntry|()
R|<local>/b|.R|/Test.MapEntryImpl.key| R|<local>/b|.R|FakeOverride<kotlin/collections/Map.Entry.key: R|kotlin/String|>|
R|<local>/b|.R|/Test.MapEntryImpl.value| R|<local>/b|.R|FakeOverride<kotlin/collections/Map.Entry.value: R|kotlin/String|>|
R|<local>/b|.R|/Test.MapEntryImpl.setValue|(Null(null)) R|<local>/b|.R|/Test.MapEntryImpl.setValue|(Null(null))
} }
@@ -1,11 +1,11 @@
FILE: test.kt FILE: test.kt
public final fun <D : R|kotlin/Any|> R|Call<D>|.testForEach(): R|kotlin/Unit| { public final fun <D : R|kotlin/Any|> R|Call<D>|.testForEach(): R|kotlin/Unit| {
R|/Call.arguments|.R|FakeOverride<java/util/Map.forEach: R|kotlin/Unit|>|(<L> = forEach@fun <anonymous>(key: R|kotlin/String!|, value: R|kotlin/String!|): R|kotlin/Unit| { this@R|/Call|.R|/Call.arguments|.R|FakeOverride<java/util/Map.forEach: R|kotlin/Unit|>|(<L> = forEach@fun <anonymous>(key: R|kotlin/String!|, value: R|kotlin/String!|): R|kotlin/Unit| {
R|<local>/key|.R|kotlin/String.length| R|<local>/key|.R|kotlin/String.length|
R|<local>/value|.R|kotlin/String.length| R|<local>/value|.R|kotlin/String.length|
} }
) )
R|/Call.arguments|.R|kotlin/collections/forEach|<R|ft<kotlin/String, kotlin/String?>!|, R|ft<kotlin/String, kotlin/String?>!|>(<L> = forEach@fun <anonymous>(it: R|kotlin/collections/Map.Entry<ft<kotlin/String, kotlin/String?>!, ft<kotlin/String, kotlin/String?>!>|): R|kotlin/Unit| <kind=UNKNOWN> { this@R|/Call|.R|/Call.arguments|.R|kotlin/collections/forEach|<R|ft<kotlin/String, kotlin/String?>!|, R|ft<kotlin/String, kotlin/String?>!|>(<L> = forEach@fun <anonymous>(it: R|kotlin/collections/Map.Entry<ft<kotlin/String, kotlin/String?>!, ft<kotlin/String, kotlin/String?>!>|): R|kotlin/Unit| <kind=UNKNOWN> {
R|<local>/it|.R|FakeOverride<kotlin/collections/Map.Entry.key: R|ft<kotlin/String, kotlin/String?>!|>|.R|kotlin/String.length| R|<local>/it|.R|FakeOverride<kotlin/collections/Map.Entry.key: R|ft<kotlin/String, kotlin/String?>!|>|.R|kotlin/String.length|
R|<local>/it|.R|FakeOverride<kotlin/collections/Map.Entry.value: R|ft<kotlin/String, kotlin/String?>!|>|.R|kotlin/String.length| R|<local>/it|.R|FakeOverride<kotlin/collections/Map.Entry.value: R|ft<kotlin/String, kotlin/String?>!|>|.R|kotlin/String.length|
} }
@@ -5,4 +5,9 @@
package org.jetbrains.kotlin.fir.symbols package org.jetbrains.kotlin.fir.symbols
interface SyntheticSymbol 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
}
@@ -6,8 +6,8 @@
package org.jetbrains.kotlin.fir.symbols.impl package org.jetbrains.kotlin.fir.symbols.impl
import org.jetbrains.kotlin.fir.declarations.* 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.CallableId
import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -35,8 +35,8 @@ class FirConstructorSymbol(
class FirAccessorSymbol( class FirAccessorSymbol(
callableId: CallableId, callableId: CallableId,
val accessorId: CallableId override val accessorId: CallableId
) : FirFunctionSymbol<FirSimpleFunction>(callableId), SyntheticSymbol ) : FirFunctionSymbol<FirSimpleFunction>(callableId), AccessorSymbol
// ------------------------ unnamed ------------------------ // ------------------------ unnamed ------------------------
@@ -5,7 +5,7 @@ FILE: Test.kt
} }
public final fun test(): R|kotlin/Int| { public final fun test(): R|kotlin/Int| {
^test R|/Inheritor.x| ^test this@R|/Base|.R|/Base.x|
} }
} }