compose-multiplatform: Failing to use Compose runtime in common code targeting native

Failing to compile simple Compose function targeting native code (iOS iosX64 or iosArm64).

Kotlin 1.7.10 Jetbrains Compose 1.2.0-beta01 Compose compiler 1.3.0

Repo code is in this branch: https://github.com/kirillzh/kmm-production-sample/tree/common-compose.

// shared/src/commonMain
@Composable
fun Foo() {
}

Error:

> Task :shared:linkDebugFrameworkIosSimulatorArm64 FAILED
w: Mimalloc allocator isn't supported on target ios_simulator_arm64. Used standard mode.
e: Compilation failed: No file for com.github.jetbrains.rssreader/Foo|4071909078632269291[0] (@ com.github.jetbrains.rssreader/Foo|4071909078632269291[0]) in module `<RssReader:shared>`[ModuleDescriptorImpl@57d43629]

 * Source files: 
 * Compiler version info: Konan: 1.7.10 / Kotlin: 1.7.20
 * Output kind: FRAMEWORK

e: java.lang.IllegalStateException: No file for com.github.jetbrains.rssreader/Foo|4071909078632269291[0] (@ com.github.jetbrains.rssreader/Foo|4071909078632269291[0]) in module `<RssReader:shared>`[ModuleDescriptorImpl@57d43629]
	at org.jetbrains.kotlin.backend.common.serialization.BasicIrModuleDeserializer.deserializeIrSymbol(BasicIrModuleDeserializer.kt:95)
Full Error
> Task :shared:linkDebugFrameworkIosSimulatorArm64 FAILED
w: Mimalloc allocator isn't supported on target ios_simulator_arm64. Used standard mode.
e: Compilation failed: No file for com.github.jetbrains.rssreader/Foo|4071909078632269291[0] (@ com.github.jetbrains.rssreader/Foo|4071909078632269291[0]) in module `<RssReader:shared>`[ModuleDescriptorImpl@57d43629]

 * Source files: 
 * Compiler version info: Konan: 1.7.10 / Kotlin: 1.7.20
 * Output kind: FRAMEWORK

e: java.lang.IllegalStateException: No file for com.github.jetbrains.rssreader/Foo|4071909078632269291[0] (@ com.github.jetbrains.rssreader/Foo|4071909078632269291[0]) in module `<RssReader:shared>`[ModuleDescriptorImpl@57d43629]
	at org.jetbrains.kotlin.backend.common.serialization.BasicIrModuleDeserializer.deserializeIrSymbol(BasicIrModuleDeserializer.kt:95)
	at org.jetbrains.kotlin.backend.common.serialization.IrModuleDeserializer.declareIrSymbol(IrModuleDeserializer.kt:74)
	at org.jetbrains.kotlin.backend.common.serialization.KotlinIrLinker.findDeserializedDeclarationForSymbol(KotlinIrLinker.kt:127)
	at org.jetbrains.kotlin.backend.common.serialization.KotlinIrLinker.deserializeOrResolveDeclaration(KotlinIrLinker.kt:169)
	at org.jetbrains.kotlin.backend.common.serialization.KotlinIrLinker.getDeclaration(KotlinIrLinker.kt:158)
	at org.jetbrains.kotlin.ir.util.ExternalDependenciesGeneratorKt.getDeclaration(ExternalDependenciesGenerator.kt:57)
	at org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator.generateUnboundSymbolsAsDependencies(ExternalDependenciesGenerator.kt:44)
	at org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator.generateUnboundSymbolsAsDependencies(ModuleGenerator.kt:52)
	at org.jetbrains.kotlin.psi2ir.Psi2IrTranslator.generateModuleFragment(Psi2IrTranslator.kt:92)
	at org.jetbrains.kotlin.psi2ir.Psi2IrTranslator.generateModuleFragment$default(Psi2IrTranslator.kt:75)
	at org.jetbrains.kotlin.backend.konan.PsiToIrKt.psiToIr(PsiToIr.kt:187)
	at org.jetbrains.kotlin.backend.konan.ToplevelPhasesKt$psiToIrPhase$1.invoke(ToplevelPhases.kt:120)
	at org.jetbrains.kotlin.backend.konan.ToplevelPhasesKt$psiToIrPhase$1.invoke(ToplevelPhases.kt:118)
	at org.jetbrains.kotlin.backend.common.phaser.PhaseBuildersKt$namedOpUnitPhase$1.invoke(PhaseBuilders.kt:96)
	at org.jetbrains.kotlin.backend.common.phaser.PhaseBuildersKt$namedOpUnitPhase$1.invoke(PhaseBuilders.kt:94)
	at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.invoke(CompilerPhase.kt:96)
	at org.jetbrains.kotlin.backend.common.phaser.CompositePhase.invoke(PhaseBuilders.kt:29)
	at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.invoke(CompilerPhase.kt:96)
	at org.jetbrains.kotlin.backend.common.phaser.CompilerPhaseKt.invokeToplevel(CompilerPhase.kt:43)
	at org.jetbrains.kotlin.backend.konan.KonanDriverKt.runTopLevelPhases(KonanDriver.kt:40)
	at org.jetbrains.kotlin.cli.bc.K2Native.doExecute(K2Native.kt:94)

Moving Composable function under a class/interface, gives a bit different error:

// shared/src/commonMain
interface Example {
  @Composable
  fun Foo() {
  }
}

Error:

e: java.lang.AssertionError: Unbound symbols not allowed

Unbound public symbol IrSimpleFunctionPublicSymbolImpl: com.github.jetbrains.rssreader/Example.Foo|4071909078632269291[0]
	at org.jetbrains.kotlin.ir.util.SymbolTableKt.noUnboundLeft(SymbolTable.kt:1141)
	at org.jetbrains.kotlin.psi2ir.Psi2IrTranslator.generateModuleFragment(Psi2IrTranslator.kt:96)
	at org.jetbrains.kotlin.psi2ir.Psi2IrTranslator.generateModuleFragment$default(Psi2IrTranslator.kt:75)
	at org.jetbrains.kotlin.backend.konan.PsiToIrKt.psiToIr(PsiToIr.kt:187)
	at org.jetbrains.kotlin.backend.konan.ToplevelPhasesKt$psiToIrPhase$1.invoke(ToplevelPhases.kt:120)
	at org.jetbrains.kotlin.backend.konan.ToplevelPhasesKt$psiToIrPhase$1.invoke(ToplevelPhases.kt:118)
	at org.jetbrains.kotlin.backend.common.phaser.PhaseBuildersKt$namedOpUnitPhase$1.invoke(PhaseBuilders.kt:96)
	at org.jetbrains.kotlin.backend.common.phaser.PhaseBuildersKt$namedOpUnitPhase$1.invoke(PhaseBuilders.kt:94)
	at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.invoke(CompilerPhase.kt:96)

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 7
  • Comments: 16 (6 by maintainers)

Commits related to this issue

Most upvoted comments

I’m also getting this issue, which was also fixed by marking my composable internal!

I’m getting the exact same issue. Just adding @Composable is bringing this error

I fixed my issue by making the @Composable function internal 🤦‍♂️

@pablichjenkov

It is impossible to use composable functions directly on iOS target. Therefore, all the composable functions in the module exported to the iOS target must be specified as internal.

However, the composable functions in other modules may be public. Using this point, it seems possible to modularize composable function.

https://github.com/TaehoonLeee/multi-module-clean-architecture/tree/multi-platform

In above project the iOS app uses presentation module only. So the composable functions written in the presentation module should be internal. And the others in the other modules are can be public.

My English is limited. I ask for your understanding.

@kirillzh Except for the composable function of the module finally used in iOS, it is okay to use the public composable function.

For example, I use feature-wise modular like below

graph TD;
A[feature1] --> |Public Composable function| C[presentation]
B[feature2] --> |Public Composable function| C
C --> |Internal Composable function| D[iosApp]

In this case, public composable functions in feature1 and feature2 are available.

Fixed in Compose 1.4.0

@TaehoonLeee , Don’t worry about the English mine is probably worst. I believe now I understand better what you explained before, and you are right, the only module affected is the last one used in the iOS interoperability. Saying so, the issue can really be categorized as minor. Thank you so much for your help. It could be really beneficial to document this behavior somewhere. When you get the error the first time, it makes you think that Composables cannot be public in libraries and that is not correct at all. It is only the last library consumed by the iosApp the one enforcing the internal constraint and only for ios target.