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:
@@ -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
|
||||
|
||||
+5
-19
@@ -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|
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -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|
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user