diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt index db18cbc85dd..0a8b3b64877 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt @@ -19,9 +19,7 @@ import org.jetbrains.kotlin.fir.builder.buildPackageDirective import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.builder.buildFile import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty -import org.jetbrains.kotlin.fir.declarations.utils.isInline -import org.jetbrains.kotlin.fir.declarations.utils.isJava -import org.jetbrains.kotlin.fir.declarations.utils.isStatic +import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.extensions.FirExtensionApiInternals import org.jetbrains.kotlin.fir.extensions.declarationGenerators @@ -162,7 +160,7 @@ fun FirClassifierSymbol<*>.toSymbol( } context(Fir2IrComponents) -private fun FirBasedSymbol<*>.toSymbolForCall( +fun FirBasedSymbol<*>.toSymbolForCall( dispatchReceiver: FirExpression?, preferGetter: Boolean, explicitReceiver: FirExpression? = null, @@ -181,6 +179,19 @@ private fun FirBasedSymbol<*>.toSymbolForCall( else -> error("Unknown symbol: $this") } +fun FirReference.extractSymbolForCall(): FirBasedSymbol<*>? { + if (this !is FirResolvedNamedReference) { + return null + } + var symbol = resolvedSymbol + + if (symbol is FirCallableSymbol<*> && symbol.origin == FirDeclarationOrigin.SubstitutionOverride.CallSite) { + symbol = symbol.fir.unwrapUseSiteSubstitutionOverrides().symbol + } + + return symbol +} + context(Fir2IrComponents) @OptIn(ExperimentalContracts::class) fun FirReference.toSymbolForCall( @@ -193,16 +204,8 @@ fun FirReference.toSymbolForCall( contract { returnsNotNull() implies (this@toSymbolForCall is FirResolvedNamedReference) } - if (this !is FirResolvedNamedReference) { - return null - } - var symbol = resolvedSymbol - if (symbol is FirCallableSymbol<*> && symbol.origin == FirDeclarationOrigin.SubstitutionOverride.CallSite) { - symbol = symbol.fir.unwrapUseSiteSubstitutionOverrides().symbol - } - - return symbol.toSymbolForCall( + return extractSymbolForCall()?.toSymbolForCall( dispatchReceiver, preferGetter, explicitReceiver, @@ -219,9 +222,9 @@ private fun FirResolvedQualifier.toLookupTag(session: FirSession): ConeClassLike } context(Fir2IrComponents) -private fun FirCallableSymbol<*>.toSymbolForCall( +fun FirCallableSymbol<*>.toSymbolForCall( dispatchReceiver: FirExpression?, - preferGetter: Boolean, + preferGetter: Boolean = true, // Note: in fact LHS for callable references and explicit receiver for normal qualified accesses explicitReceiver: FirExpression? = null, isDelegate: Boolean = false, diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index 34922de03d0..de12513a2db 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCall import org.jetbrains.kotlin.fir.references.* import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference import org.jetbrains.kotlin.fir.resolve.* +import org.jetbrains.kotlin.fir.resolve.calls.FirSimpleSyntheticPropertySymbol import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol import org.jetbrains.kotlin.fir.resolve.calls.getExpectedType import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor @@ -71,24 +72,18 @@ class CallAndReferenceGenerator( ): IrExpression { val type = approximateFunctionReferenceType(callableReferenceAccess.resolvedType).toIrType() - val callableSymbol = callableReferenceAccess.calleeReference.toResolvedCallableSymbol() - if (callableSymbol?.origin == FirDeclarationOrigin.SamConstructor) { + val firSymbol = callableReferenceAccess.calleeReference.extractSymbolForCall() + if (firSymbol?.origin == FirDeclarationOrigin.SamConstructor) { assert(explicitReceiverExpression == null) { "Fun interface constructor reference should be unbound: ${explicitReceiverExpression?.dump()}" } return adapterGenerator.generateFunInterfaceConstructorReference( callableReferenceAccess, - callableSymbol as FirSyntheticFunctionSymbol, + firSymbol as FirSyntheticFunctionSymbol, type ) } - val symbol = callableReferenceAccess.calleeReference.toSymbolForCall( - callableReferenceAccess.dispatchReceiver, - explicitReceiver = callableReferenceAccess.explicitReceiver, - isDelegate = isDelegate, - isReference = true - ) // val x by y -> // val `x$delegate` = y // val x get() = `x$delegate`.getValue(this, ::x) @@ -96,90 +91,135 @@ class CallAndReferenceGenerator( val isForDelegate = callableReferenceAccess.source?.kind == KtFakeSourceElementKind.DelegatedPropertyAccessor val origin = if (isForDelegate) IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE else null return callableReferenceAccess.convertWithOffsets { startOffset, endOffset -> - when (symbol) { - is IrPropertySymbol -> { - val referencedPropertyGetterSymbol = declarationStorage.findGetterOfProperty(symbol) - val referencedPropertySetterSymbol = runIf(callableReferenceAccess.resolvedType.isKMutableProperty(session)) { - declarationStorage.findSetterOfProperty(symbol) - } - val backingFieldSymbol = when { - referencedPropertyGetterSymbol != null -> null - else -> declarationStorage.findBackingFieldOfProperty(symbol) - } - IrPropertyReferenceImpl( - startOffset, endOffset, type, symbol, - typeArgumentsCount = callableReferenceAccess.toResolvedCallableSymbol()?.fir?.typeParameters?.size ?: 0, - field = backingFieldSymbol, - getter = referencedPropertyGetterSymbol, - setter = referencedPropertySetterSymbol, - origin = origin - ).applyTypeArguments(callableReferenceAccess).applyReceivers(callableReferenceAccess, explicitReceiverExpression) - } - is IrLocalDelegatedPropertySymbol -> { - IrLocalDelegatedPropertyReferenceImpl( - startOffset, endOffset, type, symbol, - delegate = declarationStorage.findDelegateVariableOfProperty(symbol), - getter = declarationStorage.findGetterOfProperty(symbol), - setter = declarationStorage.findSetterOfProperty(symbol), - origin = origin - ) - } + fun FirCallableSymbol<*>.toSymbolForCall(): IrSymbol? { + return toSymbolForCall( + callableReferenceAccess.dispatchReceiver, + explicitReceiver = callableReferenceAccess.explicitReceiver, + isDelegate = isDelegate, + isReference = true + ) + } - is IrFieldSymbol -> { - val field = (callableSymbol as FirFieldSymbol).fir - val propertySymbol = declarationStorage.findPropertyForBackingField(symbol) - ?: run { - // In case of [IrField] without the corresponding property, we've created it directly from [FirField]. - // Since it's used as a field reference, we need a bogus property as a placeholder. - val firSymbol = - (callableReferenceAccess.calleeReference as FirResolvedNamedReference).resolvedSymbol as FirFieldSymbol - @OptIn(IrSymbolInternals::class) - declarationStorage.getOrCreateIrPropertyByPureField(firSymbol.fir, symbol.owner.parent).symbol - } - IrPropertyReferenceImpl( - startOffset, endOffset, type, - propertySymbol, - typeArgumentsCount = 0, - field = symbol, - getter = runIf(!field.isStatic) { declarationStorage.findGetterOfProperty(propertySymbol) }, - setter = runIf(!field.isStatic) { declarationStorage.findSetterOfProperty(propertySymbol) }, - origin - ).applyReceivers(callableReferenceAccess, explicitReceiverExpression) + fun convertReferenceToRegularProperty(propertySymbol: FirPropertySymbol): IrExpression? { + val irPropertySymbol = propertySymbol.toSymbolForCall() as? IrPropertySymbol ?: return null + val referencedPropertyGetterSymbol = declarationStorage.findGetterOfProperty(irPropertySymbol) + val referencedPropertySetterSymbol = runIf(callableReferenceAccess.resolvedType.isKMutableProperty(session)) { + declarationStorage.findSetterOfProperty(irPropertySymbol) } + val backingFieldSymbol = when { + referencedPropertyGetterSymbol != null -> null + else -> declarationStorage.findBackingFieldOfProperty(irPropertySymbol) + } + return IrPropertyReferenceImpl( + startOffset, endOffset, type, irPropertySymbol, + typeArgumentsCount = callableReferenceAccess.toResolvedCallableSymbol()?.fir?.typeParameters?.size ?: 0, + field = backingFieldSymbol, + getter = referencedPropertyGetterSymbol, + setter = referencedPropertySetterSymbol, + origin = origin + ).applyTypeArguments(callableReferenceAccess).applyReceivers(callableReferenceAccess, explicitReceiverExpression) + } - is IrFunctionSymbol -> { - require(type is IrSimpleType) - var function = callableReferenceAccess.calleeReference.toResolvedFunctionSymbol()!!.fir - if (function is FirConstructor) { - // The number of type parameters of typealias constructor may mismatch with that number in the original constructor. - // And for IR, we need to use the original constructor as a source of truth - function = function.originalConstructorIfTypeAlias ?: function - } - if (adapterGenerator.needToGenerateAdaptedCallableReference(callableReferenceAccess, type, function)) { - // Receivers are being applied inside - with(adapterGenerator) { - // TODO: Figure out why `adaptedType` is different from the `type`? - val adaptedType = callableReferenceAccess.resolvedType.toIrType() as IrSimpleType - generateAdaptedCallableReference(callableReferenceAccess, explicitReceiverExpression, symbol, adaptedType) - } - } else { - IrFunctionReferenceImpl( - startOffset, endOffset, type, symbol, - typeArgumentsCount = function.typeParameters.size, - valueArgumentsCount = function.valueParameters.size + function.contextReceivers.size, - reflectionTarget = symbol - ).applyTypeArguments(callableReferenceAccess) - .applyReceivers(callableReferenceAccess, explicitReceiverExpression) + fun convertReferenceToSyntheticProperty(propertySymbol: FirSimpleSyntheticPropertySymbol): IrExpression? { + val irPropertySymbol = callablesGenerator.generateIrPropertyForSyntheticPropertyReference( + propertySymbol, + conversionScope.parentFromStack() + ).symbol + val property = propertySymbol.syntheticProperty + val referencedPropertyGetterSymbol = declarationStorage.getIrFunctionSymbol(property.getter.delegate.unwrapUseSiteSubstitutionOverrides().symbol) as? IrSimpleFunctionSymbol ?: return null + val referencedPropertySetterSymbol = runIf(callableReferenceAccess.resolvedType.isKMutableProperty(session)) { + property.setter?.delegate?.unwrapUseSiteSubstitutionOverrides()?.symbol?.let { + declarationStorage.getIrFunctionSymbol(it) as? IrSimpleFunctionSymbol? ?: return null } } + return IrPropertyReferenceImpl( + startOffset, endOffset, type, irPropertySymbol, + typeArgumentsCount = callableReferenceAccess.toResolvedCallableSymbol()?.fir?.typeParameters?.size ?: 0, + field = null, + getter = referencedPropertyGetterSymbol, + setter = referencedPropertySetterSymbol, + origin = origin + ).applyTypeArguments(callableReferenceAccess).applyReceivers(callableReferenceAccess, explicitReceiverExpression) + } - else -> { - IrErrorCallExpressionImpl( - startOffset, endOffset, type, "Unsupported callable reference: ${callableReferenceAccess.render()}" - ) + fun convertReferenceToLocalDelegatedProperty(propertySymbol: FirPropertySymbol): IrExpression? { + val irPropertySymbol = propertySymbol.toSymbolForCall() as? IrLocalDelegatedPropertySymbol ?: return null + + return IrLocalDelegatedPropertyReferenceImpl( + startOffset, endOffset, type, irPropertySymbol, + delegate = declarationStorage.findDelegateVariableOfProperty(irPropertySymbol), + getter = declarationStorage.findGetterOfProperty(irPropertySymbol), + setter = declarationStorage.findSetterOfProperty(irPropertySymbol), + origin = origin + ) + } + + fun convertReferenceToField(fieldSymbol: FirFieldSymbol): IrExpression? { + val irFieldSymbol = fieldSymbol.toSymbolForCall() as? IrFieldSymbol ?: return null + + val field = fieldSymbol.fir + val propertySymbol = declarationStorage.findPropertyForBackingField(irFieldSymbol) + ?: run { + // In case of [IrField] without the corresponding property, we've created it directly from [FirField]. + // Since it's used as a field reference, we need a bogus property as a placeholder. + @OptIn(IrSymbolInternals::class) + declarationStorage.getOrCreateIrPropertyByPureField(fieldSymbol.fir, irFieldSymbol.owner.parent).symbol + } + return IrPropertyReferenceImpl( + startOffset, endOffset, type, + propertySymbol, + typeArgumentsCount = 0, + field = irFieldSymbol, + getter = runIf(!field.isStatic) { declarationStorage.findGetterOfProperty(propertySymbol) }, + setter = runIf(!field.isStatic) { declarationStorage.findSetterOfProperty(propertySymbol) }, + origin + ).applyReceivers(callableReferenceAccess, explicitReceiverExpression) + } + + fun convertReferenceToFunction(functionSymbol: FirFunctionSymbol<*>): IrExpression? { + val irFunctionSymbol = functionSymbol.toSymbolForCall() as? IrFunctionSymbol ?: return null + + require(type is IrSimpleType) + var function = callableReferenceAccess.calleeReference.toResolvedFunctionSymbol()!!.fir + if (function is FirConstructor) { + // The number of type parameters of typealias constructor may mismatch with that number in the original constructor. + // And for IR, we need to use the original constructor as a source of truth + function = function.originalConstructorIfTypeAlias ?: function + } + return if (adapterGenerator.needToGenerateAdaptedCallableReference(callableReferenceAccess, type, function)) { + // Receivers are being applied inside + with(adapterGenerator) { + // TODO: Figure out why `adaptedType` is different from the `type`? + val adaptedType = callableReferenceAccess.resolvedType.toIrType() as IrSimpleType + generateAdaptedCallableReference(callableReferenceAccess, explicitReceiverExpression, irFunctionSymbol, adaptedType) + } + } else { + IrFunctionReferenceImpl( + startOffset, endOffset, type, irFunctionSymbol, + typeArgumentsCount = function.typeParameters.size, + valueArgumentsCount = function.valueParameters.size + function.contextReceivers.size, + reflectionTarget = irFunctionSymbol + ).applyTypeArguments(callableReferenceAccess) + .applyReceivers(callableReferenceAccess, explicitReceiverExpression) } } + + when (firSymbol) { + is FirSimpleSyntheticPropertySymbol -> convertReferenceToSyntheticProperty(firSymbol) + is FirPropertySymbol -> when { + firSymbol.isLocal -> when { + firSymbol.hasDelegate -> convertReferenceToLocalDelegatedProperty(firSymbol) + else -> null + } + else -> convertReferenceToRegularProperty(firSymbol) + } + is FirFunctionSymbol<*> -> convertReferenceToFunction(firSymbol) + is FirFieldSymbol -> convertReferenceToField(firSymbol) + else -> null + } ?: IrErrorCallExpressionImpl( + startOffset, endOffset, type, "Unsupported callable reference: ${callableReferenceAccess.render()}" + ) } } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt index fd1a5a6d80b..18b4a528173 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt @@ -84,21 +84,6 @@ class FakeOverrideGenerator( // This parameter is only needed for data-class methods that is irrelevant for lazy library classes realDeclarationSymbols = emptySet() ) - // Only add synthetic properties if no real properties were found. This can happen in a mixed hierarchy when a Java class - // inherits an @JvmField property. When a Kotlin class extends that Java class, CONFLICTING_INHERITED_JVM_DECLARATIONS will be - // reported otherwise. See KT-56538. - if (none { it is IrProperty }) { - FirSyntheticPropertiesScope.createIfSyntheticNamesProviderIsDefined(session, firClass.defaultType(), useSiteMemberScope)?.let { - generateFakeOverridesForName( - irClass, it, name, firClass, - // We pass result = null so that the IR declaration is not added to the class' list of declarations - // but is still cached in the declaration storage. - result = null, - // This parameter is only needed for data-class methods that is irrelevant for lazy library classes - realDeclarationSymbols = emptySet() - ) - } - } val staticScope = firClass.scopeProvider.getStaticMemberScopeForCallables(firClass, session, scopeSession) if (staticScope != null) { generateFakeOverridesForName( diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrCallableDeclarationsGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrCallableDeclarationsGenerator.kt index 07a75edad2a..fb4a40cf12c 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrCallableDeclarationsGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrCallableDeclarationsGenerator.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyClass import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyProperty import org.jetbrains.kotlin.fir.references.toResolvedBaseSymbol +import org.jetbrains.kotlin.fir.resolve.calls.FirSimpleSyntheticPropertySymbol import org.jetbrains.kotlin.fir.resolve.dfa.cfg.isLocalClassOrAnonymousObject import org.jetbrains.kotlin.fir.resolve.isKFunctionInvoke import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag @@ -386,6 +387,30 @@ class Fir2IrCallableDeclarationsGenerator(val components: Fir2IrComponents) : Fi return initializer } + fun generateIrPropertyForSyntheticPropertyReference( + propertySymbol: FirSimpleSyntheticPropertySymbol, + parent: IrDeclarationParent, + ): IrProperty { + val property = propertySymbol.fir + return irFactory.createProperty( + startOffset = UNDEFINED_OFFSET, + endOffset = UNDEFINED_OFFSET, + origin = IrDeclarationOrigin.SYNTHETIC_JAVA_PROPERTY_DELEGATE, + name = property.name, + visibility = visibilityConverter.convertToDescriptorVisibility(property.visibility), + modality = property.modality ?: Modality.FINAL, + symbol = IrPropertySymbolImpl(), + isVar = property.isVar, + isConst = false, + isLateinit = property.isLateInit, + isDelegated = property.delegate != null, + isExternal = property.isExternal, + isExpect = property.isExpect + ).also { + it.parent = parent + } + } + // ------------------------------------ property accessors ------------------------------------ private fun createIrPropertyAccessor( 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 5f04d88c879..77d86c50ee6 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 @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.symbols.impl import org.jetbrains.kotlin.fir.FirLabel import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.expressions.FirDelegatedConstructorCall import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference import org.jetbrains.kotlin.fir.references.toResolvedConstructorSymbol @@ -81,6 +82,9 @@ class FirConstructorSymbol(callableId: CallableId) : FirFunctionSymbol visibility:private [final,static] EXPRESSION_BODY - PROPERTY_REFERENCE 'public open foo: @[FlexibleNullability] kotlin.String?' field=null getter='public open fun (): @[FlexibleNullability] kotlin.String? declared in .J' setter='public open fun (s: @[FlexibleNullability] kotlin.String?): kotlin.Unit declared in .J' type=kotlin.reflect.KMutableProperty0<@[FlexibleNullability] kotlin.String?> origin=null + PROPERTY_REFERENCE 'public open foo' field=null getter='public open fun getFoo (): @[FlexibleNullability] kotlin.String? declared in .J' setter='public open fun setFoo (s: @[FlexibleNullability] kotlin.String?): kotlin.Unit declared in .J' type=kotlin.reflect.KMutableProperty0<@[FlexibleNullability] kotlin.String?> origin=null $this: CONSTRUCTOR_CALL 'public constructor () declared in .J' type=.J origin=null FUN DELEGATED_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> () returnType:@[FlexibleNullability] kotlin.String? correspondingProperty: PROPERTY name:p1 visibility:public modality:FINAL [delegated,var] @@ -27,7 +27,7 @@ FILE fqName:k fileName:/box.kt PROPERTY name:p2 visibility:public modality:FINAL [delegated,var] FIELD PROPERTY_DELEGATE name:p2$delegate type:kotlin.reflect.KMutableProperty0<@[FlexibleNullability] kotlin.String?> visibility:private [final,static] EXPRESSION_BODY - PROPERTY_REFERENCE 'public open foo: @[FlexibleNullability] kotlin.String?' field=null getter='public open fun (): @[FlexibleNullability] kotlin.String? declared in .J' setter='public open fun (s: @[FlexibleNullability] kotlin.String?): kotlin.Unit declared in .J' type=kotlin.reflect.KMutableProperty0<@[FlexibleNullability] kotlin.String?> origin=null + PROPERTY_REFERENCE 'public open foo' field=null getter='public open fun getFoo (): @[FlexibleNullability] kotlin.String? declared in .J' setter='public open fun setFoo (s: @[FlexibleNullability] kotlin.String?): kotlin.Unit declared in .J' type=kotlin.reflect.KMutableProperty0<@[FlexibleNullability] kotlin.String?> origin=null $this: CONSTRUCTOR_CALL 'public constructor () declared in .J' type=.J origin=null FUN DELEGATED_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> () returnType:@[FlexibleNullability] kotlin.String? correspondingProperty: PROPERTY name:p2 visibility:public modality:FINAL [delegated,var]