Skip to content

Commit

Permalink
Expect/actual support in klibs under -Xklib-mpp
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-gorshenev committed Dec 18, 2019
1 parent 218d7c3 commit dc8240c
Show file tree
Hide file tree
Showing 49 changed files with 2,623 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,12 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
)
var useMixedNamedArguments: Boolean by FreezableVar(false)

@Argument(
value = "-Xklib-mpp",
description = "Enable experimental support for multi-platform klib libraries"
)
var klibBasedMpp: Boolean by FreezableVar(false)

@Argument(value = "-Xdisable-default-scripting-plugin", description = "Do not enable scripting plugin by default")
var disableDefaultScriptingPlugin: Boolean by FreezableVar(false)

Expand All @@ -341,6 +347,7 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
collector.report(CompilerMessageSeverity.WARNING, "'-Xexperimental' is deprecated and will be removed in a future release")
}
put(AnalysisFlags.useExperimental, useExperimental?.toList().orEmpty() + optIn?.toList().orEmpty())
put(AnalysisFlags.klibBasedMpp, klibBasedMpp)
put(AnalysisFlags.explicitApiVersion, apiVersion != null)
put(AnalysisFlags.allowResultReturnType, allowResultReturnType)
ExplicitApiMode.fromString(explicitApi)?.also { put(AnalysisFlags.explicitApiMode, it) } ?: collector.report(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ fun <A : CommonCompilerArguments> CompilerConfiguration.setupCommonArguments(
) {
put(CommonConfigurationKeys.DISABLE_INLINE, arguments.noInline)
put(CommonConfigurationKeys.USE_FIR, arguments.useFir)
put(CommonConfigurationKeys.KLIB_MPP, arguments.klibBasedMpp)
putIfNotNull(CLIConfigurationKeys.INTELLIJ_PLUGIN_ROOT, arguments.intellijPluginRoot)
put(CommonConfigurationKeys.REPORT_OUTPUT_FILES, arguments.reportOutputFiles)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ object AnalysisFlags {
@JvmStatic
val multiPlatformDoNotCheckActual by AnalysisFlag.Delegates.Boolean

@JvmStatic
val klibBasedMpp by AnalysisFlag.Delegates.Boolean

@JvmStatic
val experimental by AnalysisFlag.Delegates.ListOfStrings

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ object CommonConfigurationKeys {

@JvmField
val USE_FIR = CompilerConfigurationKey.create<Boolean>("front-end IR")

@JvmField
val KLIB_MPP = CompilerConfigurationKey.create<Boolean>("Klib based MPP")
}

var CompilerConfiguration.languageVersionSettings: LanguageVersionSettings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class ExpectedActualDeclarationChecker(

override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects)) return
// TODO: we need a klib based MPP aware ModuleStructureOracle. Just disable the checks for now.
if (context.languageVersionSettings.getFlag(AnalysisFlags.klibBasedMpp)) return

// Note that this check is necessary, because for default accessors KtProperty is passed for KtDeclaration, so this
// case won't be covered by the next check (also, it accidentally fixes KT-28385)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@

package org.jetbrains.kotlin.backend.common.ir

import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.*

val IrDeclaration.isExpect
get() = this is IrClass && isExpect ||
this is IrFunction && isExpect ||
this is IrProperty && isExpect
this is IrProperty && isExpect

// The original isExpect represents what user has written.
// This predicate means "there can possibly exist an 'actual' for the given declaration".
// Shouldn't it be incorporated to descriptor -> ir declaration psi2ir translation phase?
val IrDeclaration.isProperExpect
get() = this is IrClass && isExpect ||
this is IrFunction && isExpect ||
this is IrProperty && isExpect ||
(this is IrClass || this is IrFunction || this is IrProperty || this is IrConstructor || this is IrEnumEntry)
&& (this.parent as? IrDeclaration)?.isExpect ?: false

Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ fun compile(
ExternalDependenciesGenerator(symbolTable, irProvidersWithoutDeserializer).generateUnboundSymbolsAsDependencies()
moduleFragment.patchDeclarationParents()

deserializer.finalizeExpectActualLinker()

moveBodilessDeclarationsToSeparatePlace(context, moduleFragment)

jsPhases.invokeToplevel(phaseConfig, context, moduleFragment)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ object JvmBackendFacade {
psi2irContext.moduleDescriptor, psi2irContext.irBuiltIns, psi2irContext.symbolTable,
extensions = extensions
)
val irModuleFragment = psi2ir.generateModuleFragment(psi2irContext, files, irProviders = irProviders)
val irModuleFragment = psi2ir.generateModuleFragment(psi2irContext, files, irProviders = irProviders, expectDescriptorToSymbol = null)
doGenerateFilesInternal(
state, irModuleFragment, psi2irContext.symbolTable, psi2irContext.sourceManager, phaseConfig, irProviders, extensions
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ fun compileWasm(
val irProviders = generateTypicalIrProviderList(moduleDescriptor, irBuiltIns, symbolTable, deserializer)
ExternalDependenciesGenerator(symbolTable, irProviders).generateUnboundSymbolsAsDependencies()
moduleFragment.patchDeclarationParents()
deserializer.finalizeExpectActualLinker()

wasmPhases.invokeToplevel(phaseConfig, context, moduleFragment)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,15 @@
package org.jetbrains.kotlin.psi2ir

import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.generators.AnnotationGenerator
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator
import org.jetbrains.kotlin.psi2ir.generators.*
import org.jetbrains.kotlin.psi2ir.transformations.insertImplicitCasts
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.utils.SmartList
Expand Down Expand Up @@ -68,11 +67,13 @@ class Psi2IrTranslator(
fun generateModuleFragment(
context: GeneratorContext,
ktFiles: Collection<KtFile>,
irProviders: List<IrProvider>
irProviders: List<IrProvider>,
expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>? = null
): IrModuleFragment {
val moduleGenerator = ModuleGenerator(context)
val irModule = moduleGenerator.generateModuleFragmentWithoutDependencies(ktFiles)

expectDescriptorToSymbol ?. let { referenceExpectsForUsedActuals(it, context.symbolTable, irModule) }
irModule.patchDeclarationParents()
postprocess(context, irModule)
// do not generate unbound symbols before postprocessing,
Expand All @@ -81,6 +82,7 @@ class Psi2IrTranslator(
irModule.computeUniqIdForDeclarations(context.symbolTable)

moduleGenerator.generateUnboundSymbolsAsDependencies(irProviders)

return irModule
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package org.jetbrains.kotlin.psi2ir.generators

import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.resolve.multiplatform.findExpects

// Need to create unbound symbols for expects corresponding to actuals of the currently compiled module.
// This is neccessary because there is no explicit links between expects and actuals
// neither in descriptors nor in IR.
fun referenceExpectsForUsedActuals(expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>, symbolTable: SymbolTable, irModule: IrModuleFragment) {
irModule.acceptVoid(object : IrElementVisitorVoid {

private fun <T> T.forEachExpect(body: (DeclarationDescriptor) -> Unit) where T: IrDeclaration {
this.descriptor.findExpects().forEach {
body(it)
}
}

override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
declaration.forEachExpect {
val symbol = symbolTable.referenceClass(it as ClassDescriptor)
expectDescriptorToSymbol.put(it, symbol);
it.constructors.forEach {
expectDescriptorToSymbol.put(it, symbolTable.referenceConstructor(it as ClassConstructorDescriptor))
}
}
super.visitDeclaration(declaration)
}
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
declaration.forEachExpect {
val symbol = symbolTable.referenceSimpleFunction(it as FunctionDescriptor);
expectDescriptorToSymbol.put(it, symbol);
}
super.visitDeclaration(declaration)
}
override fun visitConstructor(declaration: IrConstructor) {
declaration.forEachExpect {
val symbol = symbolTable.referenceConstructor(it as ClassConstructorDescriptor)
expectDescriptorToSymbol.put(it, symbol);

}
super.visitDeclaration(declaration)
}
override fun visitProperty(declaration: IrProperty) {
declaration.forEachExpect {
val symbol = symbolTable.referenceProperty(it as PropertyDescriptor)
expectDescriptorToSymbol.put(it, symbol);
}
super.visitDeclaration(declaration)
}
override fun visitEnumEntry(declaration: IrEnumEntry) {
declaration.forEachExpect {
val symbol = symbolTable.referenceEnumEntry(it as ClassDescriptor)
expectDescriptorToSymbol.put(it, symbol);

}
super.visitDeclaration(declaration)
}
override fun visitTypeAlias(declaration: IrTypeAlias) {
// Force actual type alias right hand side deserialization.
if (declaration.isActual) {
declaration.expandedType.classOrNull?.descriptor?.let { symbolTable.referenceClass(it) }
}

declaration.forEachExpect {
val symbol = when (it) {
is ClassDescriptor -> symbolTable.referenceClass(it)
else -> error("Unexpected expect for actual type alias: $it")
}
expectDescriptorToSymbol.put(it, symbol);

}
super.visitDeclaration(declaration)
}
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.MetadataSource
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.IrDeserializer
import org.jetbrains.kotlin.ir.util.IrProvider
import org.jetbrains.kotlin.ir.util.StubGeneratorExtensions
import org.jetbrains.kotlin.ir.util.generateTypicalIrProviderList
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.descriptors.findPackageFragmentForFile
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.jetbrains.kotlin.ir.symbols

import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.UniqId

abstract class IrDelegatingSymbol<S: IrBindableSymbol<D, B>, B: IrSymbolOwner, D: DeclarationDescriptor>(var delegate: S)
: IrBindableSymbol<D, B> {
override val owner: B get() = delegate.owner
override val descriptor: D get() = delegate.descriptor
override val isBound: Boolean get() = delegate.isBound
override var uniqId: UniqId
get() = delegate.uniqId
set(value: UniqId) { delegate.uniqId = value }

override fun bind(owner: B) = delegate.bind(owner)
override fun hashCode() = delegate.hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (this === delegate) return true
return false
}
}

class IrDelegatingClassSymbolImpl(delegate: IrClassSymbol) :
IrClassSymbol, IrDelegatingSymbol<IrClassSymbol, IrClass, ClassDescriptor>(delegate)

class IrDelegatingEnumEntrySymbolImpl(delegate: IrEnumEntrySymbol) :
IrEnumEntrySymbol, IrDelegatingSymbol<IrEnumEntrySymbol, IrEnumEntry, ClassDescriptor>(delegate)

class IrDelegatingSimpleFunctionSymbolImpl(delegate: IrSimpleFunctionSymbol) :
IrSimpleFunctionSymbol, IrDelegatingSymbol<IrSimpleFunctionSymbol, IrSimpleFunction, FunctionDescriptor>(delegate)

class IrDelegatingConstructorSymbolImpl(delegate: IrConstructorSymbol) :
IrConstructorSymbol, IrDelegatingSymbol<IrConstructorSymbol, IrConstructor, ClassConstructorDescriptor>(delegate)

class IrDelegatingPropertySymbolImpl(delegate: IrPropertySymbol) :
IrPropertySymbol, IrDelegatingSymbol<IrPropertySymbol, IrProperty, PropertyDescriptor>(delegate)

class IrDelegatingTypeAliasSymbolImpl(delegate: IrTypeAliasSymbol) :
IrTypeAliasSymbol, IrDelegatingSymbol<IrTypeAliasSymbol, IrTypeAlias, TypeAliasDescriptor>(delegate)

fun wrapInDelegatedSymbol(delegate: IrSymbol) = when(delegate) {
is IrClassSymbol -> IrDelegatingClassSymbolImpl(delegate)
is IrEnumEntrySymbol -> IrDelegatingEnumEntrySymbolImpl(delegate)
is IrSimpleFunctionSymbol -> IrDelegatingSimpleFunctionSymbolImpl(delegate)
is IrConstructorSymbol -> IrDelegatingConstructorSymbolImpl(delegate)
is IrPropertySymbol -> IrDelegatingPropertySymbolImpl(delegate)
is IrTypeAliasSymbol -> IrDelegatingTypeAliasSymbolImpl(delegate)
else -> error("Unexpected symbol to delegate to: $delegate")
}
6 changes: 6 additions & 0 deletions compiler/ir/serialization.common/src/KotlinIr.proto
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ message IrFile {
repeated int32 fq_name = 3;
repeated IrConstructorCall annotation = 4;
repeated int32 explicitly_exported_to_compiler = 5;
repeated Actual actuals = 6;
}

/* ------ IrSymbols --------------------------------------------- */
Expand Down Expand Up @@ -113,6 +114,11 @@ message IrSymbolData {
optional DescriptorReference descriptor_reference = 7;
}

message Actual {
required int32 actual_symbol = 1;
required int32 expect_symbol = 2;
}

/* ------ IrTypes --------------------------------------------- */

enum IrTypeVariance { // Should we import metadata variance, or better stay separate?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ enum class DescriptorReferenceFlags {
IS_DEFAULT_CONSTRUCTOR,
IS_ENUM_ENTRY,
IS_ENUM_SPECIAL,
IS_TYPE_PARAMETER;
IS_TYPE_PARAMETER,
IS_EXPECT;

fun encode(isSet: Boolean): Int = if (isSet) 1 shl ordinal else 0
fun decode(flags: Int): Boolean = (flags and (1 shl ordinal) != 0)
Expand Down
Loading

0 comments on commit dc8240c

Please sign in to comment.