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.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
@@ -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"
}
}
@@ -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<FirCallableSymbol<*>, 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
}
}
}
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.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 <T : AbstractFirBasedSymbol<*>> processElementsByName(
@@ -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|
}
}
@@ -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<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|()
}
@@ -74,7 +74,7 @@ class Foo {
}
fun test_4(foo: Foo) {
foo.<!UNRESOLVED_REFERENCE!>x<!> // should be error
foo.x // should be error
}
fun test_5(x: D) {
@@ -22,7 +22,7 @@ FILE: main.kt
}
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| {
R|<local>/x|.R|/D.isGood|
@@ -7,7 +7,7 @@ FILE: main.kt
}
public final fun test(): R|kotlin/Unit| {
lval b: R|MyMapEntry| = R|/MyMapEntry.MyMapEntry|()
R|<local>/b|.R|/Test.MapEntryImpl.key|
R|<local>/b|.R|/Test.MapEntryImpl.value|
R|<local>/b|.R|FakeOverride<kotlin/collections/Map.Entry.key: R|kotlin/String|>|
R|<local>/b|.R|FakeOverride<kotlin/collections/Map.Entry.value: R|kotlin/String|>|
R|<local>/b|.R|/Test.MapEntryImpl.setValue|(Null(null))
}
@@ -1,11 +1,11 @@
FILE: test.kt
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>/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.value: R|ft<kotlin/String, kotlin/String?>!|>|.R|kotlin/String.length|
}
@@ -5,4 +5,9 @@
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
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<FirSimpleFunction>(callableId), SyntheticSymbol
override val accessorId: CallableId
) : FirFunctionSymbol<FirSimpleFunction>(callableId), AccessorSymbol
// ------------------------ unnamed ------------------------
@@ -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|
}
}