diff --git a/build.gradle.kts b/build.gradle.kts index 9b2201802d0..9721874ffeb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -160,6 +160,7 @@ val firCompilerCoreModules = arrayOf( ":compiler:fir:resolve", ":compiler:fir:fir-serialization", ":compiler:fir:fir-deserialization", + ":compiler:fir:plugin-utils", ":compiler:fir:tree", ":compiler:fir:java", ":compiler:fir:raw-fir:raw-fir.common", @@ -358,6 +359,7 @@ val projectsWithEnabledContextReceivers by extra { ":core:descriptors.jvm", ":compiler:frontend.common", ":compiler:fir:resolve", + ":compiler:fir:plugin-utils", ":compiler:fir:fir2ir", ":kotlin-lombok-compiler-plugin.k1", ":kotlinx-serialization-compiler-plugin.k2", diff --git a/compiler/fir/plugin-utils/build.gradle.kts b/compiler/fir/plugin-utils/build.gradle.kts new file mode 100644 index 00000000000..6438c85a2d8 --- /dev/null +++ b/compiler/fir/plugin-utils/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + kotlin("jvm") + id("jps-compatible") +} + +kotlin { + explicitApiWarning() +} + +dependencies { + api(project(":core:compiler.common")) + api(project(":compiler:resolution.common")) + api(project(":compiler:fir:cones")) + api(project(":compiler:fir:tree")) + api(project(":compiler:fir:providers")) + api(project(":compiler:fir:semantics")) + implementation(project(":core:util.runtime")) + + compileOnly(commonDependency("com.google.guava:guava")) +} + +sourceSets { + "main" { projectDefault() } + "test" { none() } +} diff --git a/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/ClassBuildingContext.kt b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/ClassBuildingContext.kt new file mode 100644 index 00000000000..d6005b129ec --- /dev/null +++ b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/ClassBuildingContext.kt @@ -0,0 +1,156 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.plugin + +import org.jetbrains.kotlin.GeneratedDeclarationKey +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.containingClassForLocalAttr +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef +import org.jetbrains.kotlin.fir.declarations.builder.buildOuterClassTypeParameterRef +import org.jetbrains.kotlin.fir.declarations.builder.buildRegularClass +import org.jetbrains.kotlin.fir.declarations.origin +import org.jetbrains.kotlin.fir.declarations.utils.isLocal +import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension +import org.jetbrains.kotlin.fir.moduleData +import org.jetbrains.kotlin.fir.scopes.kotlinScopeProvider +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.SpecialNames + +public class ClassBuildingContext( + session: FirSession, + key: GeneratedDeclarationKey, + owner: FirClassSymbol<*>?, + private val classId: ClassId, + private val classKind: ClassKind, +) : DeclarationBuildingContext(session, key, owner) { + private val superTypeProviders = mutableListOf<(List) -> ConeKotlinType>() + + /** + * Adds [type] as supertype for constructed class + * + * If no supertypes are declared [kotlin.Any] supertype will be + * added automatically + */ + public fun superType(type: ConeKotlinType) { + superTypeProviders += { type } + } + + /** + * Adds type created by [typeProvider] as supertype for constructed class + * Use this overload when supertype uses type parameters of constructed class + * + * If no supertypes are declared [kotlin.Any] supertype will be + * added automatically + */ + public fun superType(typeProvider: (List) -> ConeKotlinType) { + superTypeProviders += typeProvider + } + + override fun build(): FirRegularClass { + return buildRegularClass { + resolvePhase = FirResolvePhase.BODY_RESOLVE + moduleData = session.moduleData + origin = key.origin + classKind = this@ClassBuildingContext.classKind + scopeProvider = session.kotlinScopeProvider + status = generateStatus() + name = classId.shortClassName + symbol = FirRegularClassSymbol(classId) + + if (status.isInner) { + requireNotNull(owner) { "Inner class must have owner" } + owner.typeParameterSymbols.mapTo(typeParameters) { buildOuterClassTypeParameterRef { symbol = it } } + } + val ownParameters = this@ClassBuildingContext.typeParameters.map { + generateTypeParameter(it, symbol) + } + typeParameters += ownParameters + initTypeParameterBounds(typeParameters, ownParameters) + + if (superTypeProviders.isEmpty()) { + superTypeRefs += session.builtinTypes.anyType + } else { + superTypeProviders.mapTo(this.superTypeRefs) { + buildResolvedTypeRef { type = it(this@buildRegularClass.typeParameters) } + } + } + }.apply { + if (owner?.isLocal == true) { + containingClassForLocalAttr = owner.toLookupTag() + } + } + } +} + +// --------------------------------------------------------------------------------------------------------------------- + +/** + * Creates top-level class with given [classId] + * All declarations in class should be generated using methods from [FirDeclarationGenerationExtension] + * + * If no supertypes added then [kotlin.Any] supertype will be added automatically + * + * Created class won't have a constructor; constructor can be added separately with [createConstructor] function + */ +public fun FirDeclarationGenerationExtension.createTopLevelClass( + classId: ClassId, + key: GeneratedDeclarationKey, + classKind: ClassKind = ClassKind.CLASS, + config: ClassBuildingContext.() -> Unit = {} +): FirRegularClass { + return ClassBuildingContext(session, key, owner = null, classId, classKind).apply(config).build() +} + +/** + * Creates nested class for [owner] class with name [name] + * All declarations in class should be generated using methods from [FirDeclarationGenerationExtension] + * + * If no supertypes added then [kotlin.Any] supertype will be added automatically + * + * Created class won't have a constructor; constructor can be added separately with [createConstructor] function + * + * By default, the class is only nested; to create an inner class, create nested class and add inner status via status() + */ +public fun FirDeclarationGenerationExtension.createNestedClass( + owner: FirClassSymbol<*>, + name: Name, + key: GeneratedDeclarationKey, + classKind: ClassKind = ClassKind.CLASS, + config: ClassBuildingContext.() -> Unit = {} +): FirRegularClass { + return ClassBuildingContext(session, key, owner, owner.classId.createNestedClassId(name), classKind).apply(config).build() +} + +/** + * Creates companion object for [owner] class + * All declarations in class should be generated using methods from [FirDeclarationGenerationExtension] + * + * If no supertypes added then [kotlin.Any] supertype will be added automatically + * + * Created class won't have a constructor; constructor can be added separately with [createDefaultPrivateConstructor] function + */ +public fun FirDeclarationGenerationExtension.createCompanionObject( + owner: FirClassSymbol<*>, + key: GeneratedDeclarationKey, + config: ClassBuildingContext.() -> Unit = {} +): FirRegularClass { + val classId = owner.classId.createNestedClassId(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) + return ClassBuildingContext(session, key, owner, classId, ClassKind.OBJECT).apply(config).apply { + modality = Modality.FINAL + status { + isCompanion = true + } + }.build() +} diff --git a/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/ConstructorBuildingContext.kt b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/ConstructorBuildingContext.kt new file mode 100644 index 00000000000..144e2254380 --- /dev/null +++ b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/ConstructorBuildingContext.kt @@ -0,0 +1,178 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.plugin + +import org.jetbrains.kotlin.GeneratedDeclarationKey +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.containingClassForStaticMemberAttr +import org.jetbrains.kotlin.fir.declarations.FirConstructor +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef +import org.jetbrains.kotlin.fir.declarations.builder.* +import org.jetbrains.kotlin.fir.declarations.origin +import org.jetbrains.kotlin.fir.declarations.utils.isInner +import org.jetbrains.kotlin.fir.expressions.buildResolvedArgumentList +import org.jetbrains.kotlin.fir.expressions.builder.buildDelegatedConstructorCall +import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension +import org.jetbrains.kotlin.fir.getContainingClassLookupTag +import org.jetbrains.kotlin.fir.moduleData +import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference +import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.scopes.getDeclaredConstructors +import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.callableIdForConstructor +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled + +public class ConstructorBuildingContext( + session: FirSession, + key: GeneratedDeclarationKey, + owner: FirClassSymbol<*>, + private val isPrimary: Boolean +) : FunctionBuildingContext(owner.classId.callableIdForConstructor(), session, key, owner) { + override fun build(): FirConstructor { + requireNotNull(owner) + val init: FirAbstractConstructorBuilder.() -> Unit = { + symbol = FirConstructorSymbol(owner.classId) + + resolvePhase = FirResolvePhase.BODY_RESOLVE + moduleData = session.moduleData + origin = key.origin + + owner.typeParameterSymbols.mapTo(typeParameters) { buildConstructedClassTypeParameterRef { symbol = it } } + returnTypeRef = owner.defaultType().toFirResolvedTypeRef() + status = generateStatus() + if (owner.isInner) { + val parentSymbol = owner.getContainingClassLookupTag()?.toSymbol(session) as? FirClassSymbol<*> + ?: error("Symbol for parent of $owner not found") + dispatchReceiverType = parentSymbol.defaultType() + } + this@ConstructorBuildingContext.valueParameters.mapTo(valueParameters) { generateValueParameter(it, symbol, typeParameters) } + if (owner is FirRegularClassSymbol) { + owner.resolvedContextReceivers.mapTo(contextReceivers) { + buildContextReceiver { typeRef = it.typeRef.coneType.toFirResolvedTypeRef() } + } + } + } + val constructor = if (isPrimary) { + buildPrimaryConstructor(init) + } else { + buildConstructor(init) + } + constructor.containingClassForStaticMemberAttr = owner.toLookupTag() + return constructor + } + + /** + * Type parameters of constructor are inherited from constructed class + */ + @Deprecated("This function does nothing and should not be called", level = DeprecationLevel.HIDDEN) + override fun typeParameter( + name: Name, + variance: Variance, + isReified: Boolean, + key: GeneratedDeclarationKey, + config: TypeParameterBuildingContext.() -> Unit + ) { + shouldNotBeCalled() + } + + /** + * Context receivers of constructor are inherited from constructed class + */ + @Deprecated("This function does nothing and should not be called", level = DeprecationLevel.HIDDEN) + override fun contextReceiver(type: ConeKotlinType) { + shouldNotBeCalled() + } + + /** + * Context receivers of constructor are inherited from constructed class + */ + @Deprecated("This function does nothing and should not be called", level = DeprecationLevel.HIDDEN) + override fun contextReceiver(typeProvider: (List) -> ConeKotlinType) { + shouldNotBeCalled() + } +} + +// --------------------------------------------------------------------------------------------------------------------- + +/** + * Creates constructor for [owner] class + * + * Setting [generateDelegatedNoArgConstructorCall] to true automatically generates delegating constructor call to superclass + * This generation works only if single superclass of the class contains constructor without arguments + * + * If you want to create custom delegated constructor call please do it in the [IrGenerationExtension] + */ +public fun FirDeclarationGenerationExtension.createConstructor( + owner: FirClassSymbol<*>, + key: GeneratedDeclarationKey, + isPrimary: Boolean = false, + generateDelegatedNoArgConstructorCall: Boolean = false, + config: ConstructorBuildingContext.() -> Unit = {} +): FirConstructor { + return ConstructorBuildingContext(session, key, owner, isPrimary).apply(config).build().also { + if (generateDelegatedNoArgConstructorCall) { + it.generateNoArgDelegatingConstructorCall() + } + } +} + +/** + * Creates constructor for [owner] object + * This constructor will be private primary constructor without parameters + * + * This is a shorthand for createConstructor() which is useful for creating constructors for companions and other objects, as they should be private + * + * Setting [generateDelegatedNoArgConstructorCall] to true automatically generates delegating constructor call to superclass + * This generation works only if single superclass of the class contains constructor without arguments + * + * If you want to create custom delegated constructor call please do it in the [IrGenerationExtension] + */ +public fun FirDeclarationGenerationExtension.createDefaultPrivateConstructor( + owner: FirClassSymbol<*>, + key: GeneratedDeclarationKey, + generateDelegatedNoArgConstructorCall: Boolean = true +): FirConstructor { + return createConstructor(owner, key, isPrimary = true, generateDelegatedNoArgConstructorCall) { + visibility = Visibilities.Private + } +} + +context(FirDeclarationGenerationExtension) +private fun FirConstructor.generateNoArgDelegatingConstructorCall() { + val owner = returnTypeRef.coneType.toSymbol(session) as? FirClassSymbol<*> + requireNotNull(owner) + val delegatingConstructorCall = buildDelegatedConstructorCall { + val superClasses = owner.resolvedSuperTypes.filter { it.toRegularClassSymbol(session)?.classKind == ClassKind.CLASS } + val singleSupertype = when (superClasses.size) { + 0 -> session.builtinTypes.anyType.type + 1 -> superClasses.first() + else -> error("Object $owner has more than one class supertypes: $superClasses") + } + constructedTypeRef = singleSupertype.toFirResolvedTypeRef() + val superSymbol = singleSupertype.toRegularClassSymbol(session) ?: error("Symbol for supertype $singleSupertype not found") + val superConstructorSymbol = superSymbol.declaredMemberScope(session) + .getDeclaredConstructors() + .firstOrNull { it.valueParameterSymbols.isEmpty() } + ?: error("No arguments constructor for class $singleSupertype not found") + calleeReference = buildResolvedNamedReference { + name = superConstructorSymbol.name + resolvedSymbol = superConstructorSymbol + } + argumentList = buildResolvedArgumentList(LinkedHashMap()) + isThis = false + } + replaceDelegatedConstructor(delegatingConstructorCall) +} diff --git a/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/DeclarationBuildingContext.kt b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/DeclarationBuildingContext.kt new file mode 100644 index 00000000000..1c3d26ff131 --- /dev/null +++ b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/DeclarationBuildingContext.kt @@ -0,0 +1,172 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.plugin + +import org.jetbrains.kotlin.GeneratedDeclarationKey +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.builder.buildContextReceiver +import org.jetbrains.kotlin.fir.declarations.builder.buildTypeParameter +import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl +import org.jetbrains.kotlin.fir.moduleData +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol +import org.jetbrains.kotlin.fir.toEffectiveVisibility +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.Variance + +public sealed class DeclarationBuildingContext( + protected val session: FirSession, + protected val key: GeneratedDeclarationKey, + protected val owner: FirClassSymbol<*>? +) { + /** + * Allows to set visibility of the declaration + */ + public var visibility: Visibility = Visibilities.Public + + /** + * Allows to set modality of the declaration + */ + public var modality: Modality = Modality.FINAL + + /** + * Allows to configure flags in status of declaration + * For full list of possible flags refer to [FirDeclarationStatus] class + * Note that not all flags are meaningful for each declaration + * E.g. there is no point to mark function as inner + */ + public fun status(statusConfig: FirResolvedDeclarationStatusImpl.() -> Unit) { + statusConfigs += statusConfig + } + + /** + * Adds type parameter with specified [name] and [variance] to declaration + * + * Upper bounds of type parameters can be configured in [config] lambda + * + * If no bounds passed then `kotlin.Any?` bound will be added automatically + */ + public open fun typeParameter( + name: Name, + variance: Variance = Variance.INVARIANT, + isReified: Boolean = false, + key: GeneratedDeclarationKey = this@DeclarationBuildingContext.key, + config: TypeParameterBuildingContext.() -> Unit = {} + ) { + typeParameters += TypeParameterData(name, variance, isReified, TypeParameterBuildingContext().apply(config).boundProviders, key) + } + + public class TypeParameterBuildingContext { + /** + * Declares [type] as upper bound of type parameter + */ + public fun bound(type: ConeKotlinType) { + bound { type } + } + + /** + * Type produced by [typeProvider] will be an upper bound of the type parameter + * + * Use this method when bounds of your type parameters depend on each other + * For example, in this case: + * ``` + * interface Out + * + * fun foo() where T : R, R : Out {} + * ``` + */ + public fun bound(typeProvider: (List) -> ConeKotlinType) { + boundProviders += typeProvider + } + + internal val boundProviders: MutableList<(List) -> ConeKotlinType> = mutableListOf() + } + + + private val contextReceiverTypeProviders: MutableList<(List) -> ConeKotlinType> = mutableListOf() + + /** + * Adds context receiver with [type] type to declaration + */ + public open fun contextReceiver(type: ConeKotlinType) { + contextReceiver { type } + } + + /** + * Adds context receiver with type provided by [typeProvider] to declaration + * Use this overload when context receiver type uses type parameters of constructed declaration + */ + public open fun contextReceiver(typeProvider: (List) -> ConeKotlinType) { + contextReceiverTypeProviders += typeProvider + } + + protected fun produceContextReceiversTo(destination: MutableList, typeParameters: List) { + contextReceiverTypeProviders.mapTo(destination) { + buildContextReceiver { typeRef = it.invoke(typeParameters).toFirResolvedTypeRef() } + } + } + + protected data class TypeParameterData( + val name: Name, + val variance: Variance, + val isReified: Boolean, + val boundProviders: List<(List) -> ConeKotlinType>, + val key: GeneratedDeclarationKey + ) + + protected val typeParameters: MutableList = mutableListOf() + + private val statusConfigs: MutableList Unit> = mutableListOf() + + public abstract fun build(): T + + protected fun generateStatus(): FirResolvedDeclarationStatusImpl { + return FirResolvedDeclarationStatusImpl( + visibility, + modality, + visibility.toEffectiveVisibility(owner, forClass = true) + ).also { + for (statusConfig in statusConfigs) { + it.apply(statusConfig) + } + } + } + + protected fun generateTypeParameter( + typeParameter: TypeParameterData, + containingDeclarationSymbol: FirBasedSymbol<*> + ): FirTypeParameter { + return buildTypeParameter { + resolvePhase = FirResolvePhase.BODY_RESOLVE + moduleData = session.moduleData + origin = typeParameter.key.origin + name = typeParameter.name + symbol = FirTypeParameterSymbol() + this.containingDeclarationSymbol = containingDeclarationSymbol + variance = typeParameter.variance + isReified = typeParameter.isReified + } + } + + protected fun initTypeParameterBounds(allParameters: List, ownTypeParameters: List) { + for ((typeParameter, data) in ownTypeParameters.zip(typeParameters)) { + val coneBounds = data.boundProviders.map { it.invoke(allParameters) } + val bounds = if (coneBounds.isEmpty()) { + listOf(session.builtinTypes.nullableAnyType) + } else { + coneBounds.map { it.toFirResolvedTypeRef() } + } + typeParameter.replaceBounds(bounds) + } + } +} diff --git a/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/FunctionBuildingContext.kt b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/FunctionBuildingContext.kt new file mode 100644 index 00000000000..c97398d8216 --- /dev/null +++ b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/FunctionBuildingContext.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.plugin + +import org.jetbrains.kotlin.GeneratedDeclarationKey +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter +import org.jetbrains.kotlin.fir.expressions.builder.buildExpressionStub +import org.jetbrains.kotlin.fir.moduleData +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.Name + +public sealed class FunctionBuildingContext( + protected val callableId: CallableId, + session: FirSession, + key: GeneratedDeclarationKey, + owner: FirClassSymbol<*>? +) : DeclarationBuildingContext(session, key, owner) { + protected data class ValueParameterData( + val name: Name, + val typeProvider: (List) -> ConeKotlinType, + val isCrossinline: Boolean, + val isNoinline: Boolean, + val isVararg: Boolean, + val hasDefaultValue: Boolean, + val key: GeneratedDeclarationKey + ) + + protected val valueParameters: MutableList = mutableListOf() + + /** + * Adds value parameter with [type] type to constructed function + * + * If you set [hasDefaultValue] to true then you need to generate actual default value + * in [IrGenerationExtension] + */ + public fun valueParameter( + name: Name, + type: ConeKotlinType, + isCrossinline: Boolean = false, + isNoinline: Boolean = false, + isVararg: Boolean = false, + hasDefaultValue: Boolean = false, + key: GeneratedDeclarationKey = this@FunctionBuildingContext.key + ) { + valueParameter(name, { type }, isCrossinline, isNoinline, isVararg, hasDefaultValue, key) + } + + /** + * Adds value parameter with type provided by [typeProvider] to constructed function + * Use this overload when parameter type uses type parameters of constructed declaration + * + * If you set [hasDefaultValue] to true then you need to generate actual default value + * in [IrGenerationExtension] + */ + public fun valueParameter( + name: Name, + typeProvider: (List) -> ConeKotlinType, + isCrossinline: Boolean = false, + isNoinline: Boolean = false, + isVararg: Boolean = false, + hasDefaultValue: Boolean = false, + key: GeneratedDeclarationKey = this@FunctionBuildingContext.key + ) { + valueParameters += ValueParameterData(name, typeProvider, isCrossinline, isNoinline, isVararg, hasDefaultValue, key) + } + + protected fun generateValueParameter( + valueParameter: ValueParameterData, + containingFunctionSymbol: FirFunctionSymbol<*>, + functionTypeParameters: List + ): FirValueParameter { + return buildValueParameter { + resolvePhase = FirResolvePhase.BODY_RESOLVE + moduleData = session.moduleData + origin = valueParameter.key.origin + returnTypeRef = valueParameter.typeProvider.invoke(functionTypeParameters).toFirResolvedTypeRef() + name = valueParameter.name + symbol = FirValueParameterSymbol(name) + if (valueParameter.hasDefaultValue) { + // TODO: check how it will actually work in fir2ir + defaultValue = buildExpressionStub { typeRef = session.builtinTypes.nothingType } + } + this.containingFunctionSymbol = containingFunctionSymbol + isCrossinline = valueParameter.isCrossinline + isNoinline = valueParameter.isNoinline + isVararg = valueParameter.isVararg + } + } +} diff --git a/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/PropertyBuildingContext.kt b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/PropertyBuildingContext.kt new file mode 100644 index 00000000000..a14089d0071 --- /dev/null +++ b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/PropertyBuildingContext.kt @@ -0,0 +1,184 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.plugin + +import org.jetbrains.kotlin.GeneratedDeclarationKey +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.builder.buildProperty +import org.jetbrains.kotlin.fir.declarations.builder.buildReceiverParameter +import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyBackingField +import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter +import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter +import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension +import org.jetbrains.kotlin.fir.moduleData +import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.Name + +public class PropertyBuildingContext( + session: FirSession, + key: GeneratedDeclarationKey, + owner: FirClassSymbol<*>?, + private val callableId: CallableId, + private val returnTypeProvider: (List) -> ConeKotlinType, + private val isVal: Boolean, + private val hasBackingField: Boolean, +) : DeclarationBuildingContext(session, key, owner) { + private var setterVisibility: Visibility? = null + private var extensionReceiverTypeProvider: ((List) -> ConeKotlinType)? = null + + /** + * Sets [type] as extension receiver type of constructed property + */ + public fun extensionReceiverType(type: ConeKotlinType) { + extensionReceiverType { type } + } + + /** + * Sets type, provided by [typeProvider], as extension receiver type of constructed property + * + * Use this overload when extension receiver type uses type parameters of constructed property + */ + public fun extensionReceiverType(typeProvider: (List) -> ConeKotlinType) { + extensionReceiverTypeProvider = typeProvider + } + + /** + * Declares [visibility] of property setter if property marked as var + * If this function is not called then setter will have same visibility + * as property itself + */ + public fun setter(visibility: Visibility) { + setterVisibility = visibility + } + + override fun build(): FirProperty { + return buildProperty { + resolvePhase = FirResolvePhase.BODY_RESOLVE + moduleData = session.moduleData + origin = key.origin + + symbol = FirPropertySymbol(callableId) + name = callableId.callableName + + val resolvedStatus = generateStatus() + status = resolvedStatus + + dispatchReceiverType = owner?.defaultType() + + this@PropertyBuildingContext.typeParameters.mapTo(typeParameters) { + generateTypeParameter(it, symbol) + } + initTypeParameterBounds(typeParameters, typeParameters) + + returnTypeRef = returnTypeProvider.invoke(typeParameters).toFirResolvedTypeRef() + extensionReceiverTypeProvider?.invoke(typeParameters)?.let { + receiverParameter = buildReceiverParameter { + typeRef = it.toFirResolvedTypeRef() + } + } + + produceContextReceiversTo(contextReceivers, typeParameters) + + isVar = !isVal + getter = FirDefaultPropertyGetter( + source = null, session.moduleData, key.origin, returnTypeRef, status.visibility, symbol, + Modality.FINAL, resolvedStatus.effectiveVisibility + ) + if (isVar) { + setter = FirDefaultPropertySetter( + source = null, session.moduleData, key.origin, returnTypeRef, setterVisibility ?: status.visibility, + symbol, Modality.FINAL, resolvedStatus.effectiveVisibility + ) + } else { + require(setterVisibility == null) { "isVar = false but setterVisibility is specified. Did you forget to set isVar = true?" } + } + if (hasBackingField) { + backingField = FirDefaultPropertyBackingField(session.moduleData, mutableListOf(), returnTypeRef, isVar, symbol, status) + } + isLocal = false + bodyResolveState = FirPropertyBodyResolveState.EVERYTHING_RESOLVED + } + } +} + +// --------------------------------------------------------------------------------------------------------------------- + +/** + * Creates a member property for [owner] class with [returnType] return type + */ +public fun FirDeclarationGenerationExtension.createMemberProperty( + owner: FirClassSymbol<*>, + key: GeneratedDeclarationKey, + name: Name, + returnType: ConeKotlinType, + isVal: Boolean = true, + hasBackingField: Boolean = true, + config: PropertyBuildingContext.() -> Unit = {} +): FirProperty { + return createMemberProperty(owner, key, name, { returnType }, isVal, hasBackingField, config) +} + +/** + * Creates a member property for [owner] class with return type provided by [returnTypeProvider] + * Use this overload when those types use type parameters of constructed property + */ +public fun FirDeclarationGenerationExtension.createMemberProperty( + owner: FirClassSymbol<*>, + key: GeneratedDeclarationKey, + name: Name, + returnTypeProvider: (List) -> ConeKotlinType, + isVal: Boolean = true, + hasBackingField: Boolean = true, + config: PropertyBuildingContext.() -> Unit = {} +): FirProperty { + val callableId = CallableId(owner.classId, name) + return PropertyBuildingContext(session, key, owner, callableId, returnTypeProvider, isVal, hasBackingField).apply(config).build() +} + +/** + * Creates a top-level property class with [returnType] return type + * + * If you create top-level extension property don't forget to set [hasBackingField] to false, + * since such properties never have backing fields + */ +public fun FirDeclarationGenerationExtension.createTopLevelProperty( + key: GeneratedDeclarationKey, + callableId: CallableId, + returnType: ConeKotlinType, + isVal: Boolean = true, + hasBackingField: Boolean = true, + config: PropertyBuildingContext.() -> Unit = {} +): FirProperty { + return createTopLevelProperty(key, callableId, { returnType }, isVal, hasBackingField, config) +} + +/** + * Creates a top-level property with return type provided by [returnTypeProvider] + * + * If you create top-level extension property don't forget to set [hasBackingField] to false, + * since such properties never have backing fields + * + * Use this overload when those types use type parameters of constructed property + */ +public fun FirDeclarationGenerationExtension.createTopLevelProperty( + key: GeneratedDeclarationKey, + callableId: CallableId, + returnTypeProvider: (List) -> ConeKotlinType, + isVal: Boolean = true, + hasBackingField: Boolean = true, + config: PropertyBuildingContext.() -> Unit = {} +): FirProperty { + require(callableId.classId == null) + return PropertyBuildingContext(session, key, owner = null, callableId, returnTypeProvider, isVal, hasBackingField).apply(config).build() +} diff --git a/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/SimpleFunctionBuildingContext.kt b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/SimpleFunctionBuildingContext.kt new file mode 100644 index 00000000000..37cd39a5fe2 --- /dev/null +++ b/compiler/fir/plugin-utils/src/org/jetbrains/kotlin/fir/plugin/SimpleFunctionBuildingContext.kt @@ -0,0 +1,147 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.plugin + +import org.jetbrains.kotlin.GeneratedDeclarationKey +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.declarations.FirTypeParameter +import org.jetbrains.kotlin.fir.declarations.builder.buildReceiverParameter +import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction +import org.jetbrains.kotlin.fir.declarations.origin +import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension +import org.jetbrains.kotlin.fir.moduleData +import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +public class SimpleFunctionBuildingContext( + session: FirSession, + key: GeneratedDeclarationKey, + owner: FirClassSymbol<*>?, + callableId: CallableId, + private val returnTypeProvider: (List) -> ConeKotlinType, +) : FunctionBuildingContext(callableId, session, key, owner) { + private var extensionReceiverTypeProvider: ((List) -> ConeKotlinType)? = null + + /** + * Sets [type] as extension receiver type of constructed function + */ + public fun extensionReceiverType(type: ConeKotlinType) { + extensionReceiverType { type } + } + + /** + * Sets type, provided by [typeProvider], as extension receiver type of constructed function + * + * Use this overload when extension receiver type uses type parameters of constructed function + */ + public fun extensionReceiverType(typeProvider: (List) -> ConeKotlinType) { + require(extensionReceiverTypeProvider == null) { "Extension receiver type is already initialized" } + extensionReceiverTypeProvider = typeProvider + } + + override fun build(): FirSimpleFunction { + return buildSimpleFunction { + resolvePhase = FirResolvePhase.BODY_RESOLVE + moduleData = session.moduleData + origin = key.origin + + symbol = FirNamedFunctionSymbol(callableId) + name = callableId.callableName + + status = generateStatus() + + dispatchReceiverType = owner?.defaultType() + + this@SimpleFunctionBuildingContext.typeParameters.mapTo(typeParameters) { + generateTypeParameter(it, symbol) + } + initTypeParameterBounds(typeParameters, typeParameters) + produceContextReceiversTo(contextReceivers, typeParameters) + + this@SimpleFunctionBuildingContext.valueParameters.mapTo(valueParameters) { + generateValueParameter(it, symbol, typeParameters) + } + returnTypeRef = returnTypeProvider(typeParameters).toFirResolvedTypeRef() + extensionReceiverTypeProvider?.invoke(typeParameters)?.let { + receiverParameter = buildReceiverParameter { + typeRef = it.toFirResolvedTypeRef() + } + } + } + } +} + +// --------------------------------------------------------------------------------------------------------------------- + +/** + * Creates a member function for [owner] class with [returnType] return type + * + * Type and value parameters can be configured with [config] builder lambda + */ +public fun FirDeclarationGenerationExtension.createMemberFunction( + owner: FirClassSymbol<*>, + key: GeneratedDeclarationKey, + name: Name, + returnType: ConeKotlinType, + config: SimpleFunctionBuildingContext.() -> Unit = {} +): FirSimpleFunction { + return createMemberFunction(owner, key, name, { returnType }, config) +} + +/** + * Creates a member function for [owner] class with return type provided by [returnTypeProvider] + * Use this overload when return type uses type parameters of constructed property + * + * Type and value parameters can be configured with [config] builder lambda + */ +public fun FirDeclarationGenerationExtension.createMemberFunction( + owner: FirClassSymbol<*>, + key: GeneratedDeclarationKey, + name: Name, + returnTypeProvider: (List) -> ConeKotlinType, + config: SimpleFunctionBuildingContext.() -> Unit = {} +): FirSimpleFunction { + val callableId = CallableId(owner.classId, name) + return SimpleFunctionBuildingContext(session, key, owner, callableId, returnTypeProvider).apply(config).build() +} + +/** + * Creates a top-level function with [returnType] return type + * + * Type and value parameters can be configured with [config] builder lambda + */ +public fun FirDeclarationGenerationExtension.createTopLevelFunction( + key: GeneratedDeclarationKey, + callableId: CallableId, + returnType: ConeKotlinType, + config: SimpleFunctionBuildingContext.() -> Unit = {} +): FirSimpleFunction { + return createTopLevelFunction(key, callableId, { returnType }, config) +} + +/** + * Creates a top-level function for [owner] class with return type provided by [returnTypeProvider] + * Use this overload when those types use type parameters of constructed property + * + * Type and value parameters can be configured with [config] builder lambda + */ +public fun FirDeclarationGenerationExtension.createTopLevelFunction( + key: GeneratedDeclarationKey, + callableId: CallableId, + returnTypeProvider: (List) -> ConeKotlinType, + config: SimpleFunctionBuildingContext.() -> Unit = {} +): FirSimpleFunction { + require(callableId.classId == null) + return SimpleFunctionBuildingContext(session, key, owner = null, callableId, returnTypeProvider).apply(config).build() +} diff --git a/settings.gradle b/settings.gradle index c048420b80b..119ef9bfbb0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -319,6 +319,7 @@ include ":compiler:fir", ":compiler:fir:providers", ":compiler:fir:semantics", ":compiler:fir:resolve", + ":compiler:fir:plugin-utils", ":compiler:fir:fir-serialization", ":compiler:fir:fir-deserialization", ":compiler:fir:java",