Add basic support for multi-platform projects to the compiler (#1254)

Add compiler pass removing `expect` declarations

Thus support multi-platform projects in the compiler.

Also 

* Add some trivial checks to ensure that `expect` declarations
are not generated to bitcode.
* Add minor improvement to exported symbol name clash detection.
* Don't create LLVM declarations for Obj-C-interop fake functions.
This commit is contained in:
SvyatoslavScherbina
2018-01-25 12:55:06 +03:00
committed by GitHub
parent 155618c7dd
commit 7567c96bc9
10 changed files with 87 additions and 9 deletions
@@ -41,6 +41,10 @@ internal class KonanLower(val context: Context) {
fun lowerModule(irModule: IrModuleFragment) {
val phaser = PhaseManager(context)
phaser.phase(KonanPhase.REMOVE_EXPECT_DECLARATIONS) {
irModule.files.forEach(ExpectDeclarationsRemoving(context)::lower)
}
phaser.phase(KonanPhase.TEST_PROCESSOR) {
TestProcessor(context).process(irModule)
}
@@ -29,6 +29,7 @@ enum class KonanPhase(val description: String,
/* */ SERIALIZER("Serialize descriptor tree and inline IR bodies"),
/* */ BACKEND("All backend"),
/* ... */ LOWER("IR Lowering"),
/* ... ... */ REMOVE_EXPECT_DECLARATIONS("Expect declarations removing"),
/* ... ... */ TEST_PROCESSOR("Unit test processor"),
/* ... ... */ LOWER_BEFORE_INLINE("Special operations processing before inlining"),
/* ... ... */ LOWER_INLINE_CONSTRUCTORS("Inline constructors transformation", LOWER_BEFORE_INLINE),
@@ -344,3 +344,6 @@ val ClassDescriptor.enumEntries: List<ClassDescriptor>
.filterIsInstance<ClassDescriptor>()
.filter { it.kind == ClassKind.ENUM_ENTRY }
}
internal val DeclarationDescriptor.isExpectMember: Boolean
get() = this is MemberDescriptor && this.isExpect
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMTypeRef
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
import org.jetbrains.kotlin.backend.konan.descriptors.isExpectMember
import org.jetbrains.kotlin.backend.konan.descriptors.isUnit
import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
import org.jetbrains.kotlin.backend.konan.isExternalObjCClass
@@ -47,6 +48,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
* and so should be computable from the descriptor itself without checking a backend state.
*/
internal tailrec fun DeclarationDescriptor.isExported(): Boolean {
assert(!this.isExpectMember) { this }
if (this.annotations.findAnnotation(symbolNameAnnotation) != null) {
// Treat any `@SymbolName` declaration as exported.
return true
@@ -17,10 +17,7 @@
package org.jetbrains.kotlin.backend.konan.llvm
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.LlvmSymbolOrigin
import org.jetbrains.kotlin.backend.konan.descriptors.SyntheticModules
import org.jetbrains.kotlin.backend.konan.descriptors.origin
import org.jetbrains.kotlin.backend.konan.descriptors.stdlibModule
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.module
@@ -30,6 +27,8 @@ internal interface LlvmImports {
internal val DeclarationDescriptor.llvmSymbolOrigin: LlvmSymbolOrigin
get() {
assert(!this.isExpectMember) { this }
val module = this.module
val moduleOrigin = module.origin
when (moduleOrigin) {
@@ -444,6 +444,11 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
return
}
if (declaration.descriptor.getObjCInitMethod() != null) {
// Do not generate any ctors for external Objective-C classes.
return
}
val constructorDescriptor = declaration.descriptor
val classDescriptor = constructorDescriptor.constructedClass
if (constructorDescriptor.isPrimary) {
@@ -18,11 +18,10 @@ package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.*
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.isKotlinObjCClass
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.serialization.deserialization.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
@@ -384,8 +383,12 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
val descriptor = declaration.descriptor
val llvmFunctionType = getLlvmFunctionType(descriptor)
if ((descriptor is ConstructorDescriptor && descriptor.getObjCInitMethod() != null)) {
return
}
val llvmFunction = if (descriptor.isExternal) {
if (descriptor.isIntrinsic) {
if (descriptor.isIntrinsic || descriptor.getExternalObjCMethodInfo() != null) {
return
}
@@ -395,7 +398,14 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
)
} else {
val symbolName = if (descriptor.isExported()) {
descriptor.symbolName
descriptor.symbolName.also {
if (!MainFunctionDetector.isMain(descriptor)) {
assert(LLVMGetNamedFunction(context.llvm.llvmModule, it) == null) { it }
} else {
// As a workaround, allow `main` functions to clash because frontend accepts this.
// See [OverloadResolver.isTopLevelMainInDifferentFiles] usage.
}
}
} else {
"kfun:" + qualifyInternalName(descriptor)
}
@@ -0,0 +1,17 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.isExpectMember
import org.jetbrains.kotlin.ir.declarations.IrFile
/**
* This pass removes all declarations with `isExpect == true`.
*/
internal class ExpectDeclarationsRemoving(val context: Context) : FileLoweringPass {
override fun lower(irFile: IrFile) {
// All declarations with `isExpect == true` are nested into a top-level declaration with `isExpect == true`.
irFile.declarations.removeAll { it.descriptor.isExpectMember }
}
}
+5
View File
@@ -1918,6 +1918,11 @@ task memory_escape2(type: RunKonanTest) {
source = "runtime/memory/escape2.kt"
}
task mpp1(type: RunStandaloneKonanTest) {
source = "codegen/mpp/mpp1.kt"
flags = ['-tr', '-Xmulti-platform']
}
task unit1(type: RunKonanTest) {
goldValue = "First\nkotlin.Unit\n"
source = "codegen/basics/unit1.kt"
+31
View File
@@ -0,0 +1,31 @@
package codegen.mpp.mpp1
import kotlin.test.*
fun box() {
assertEquals(A().B().fourtyTwo(), 42)
assertEquals(seventeen(), 17)
}
expect class A {
constructor()
inner class B {
fun fourtyTwo(): Int
constructor()
}
}
actual class A {
actual inner class B actual constructor() {
actual fun fourtyTwo() = 42
}
}
actual fun seventeen() = 17
expect fun seventeen(): Int
@Test fun runTest() {
box()
}