Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RealmObject interface/class extension with generics crashes #999

Open
Burtan opened this issue Sep 11, 2022 · 4 comments
Open

RealmObject interface/class extension with generics crashes #999

Burtan opened this issue Sep 11, 2022 · 4 comments

Comments

@Burtan
Copy link

Burtan commented Sep 11, 2022

Hi,

when using generics on an interface/class that extends RealmObject you get an error. Not sure if it is kotlin or realm though.

class BaseEntity<T> : RealmObject

org.jetbrains.kotlin.backend.common.CompilationException: Back-end: Please report this problem https://kotl.in/issue
...BaseEntity.kt:-1:-1
Details: Internal error in file lowering: java.lang.IndexOutOfBoundsException: Empty list doesn't contain element at index 0.
	at kotlin.collections.EmptyList.get(Collections.kt:36)
	at kotlin.collections.EmptyList.get(Collections.kt:24)
	at org.jetbrains.kotlin.backend.common.ir.IrUtilsKt.remapTypeParameters(IrUtils.kt:329)
	at org.jetbrains.kotlin.backend.common.ir.IrUtilsKt.remapTypeParameters(IrUtils.kt:342)
	at org.jetbrains.kotlin.backend.common.ir.IrUtilsKt.copyTo$default(IrUtils.kt:121)
	at io.realm.kotlin.compiler.RealmModelSyntheticPropertiesGeneration.addVariableProperty(RealmModelSyntheticPropertiesGeneration.kt:613)
	at io.realm.kotlin.compiler.RealmModelSyntheticPropertiesGeneration.addRealmObjectInternalProperties(RealmModelSyntheticPropertiesGeneration.kt:171)
	at io.realm.kotlin.compiler.RealmModelLowering.lower(RealmModelLoweringExtension.kt:89)
	at org.jetbrains.kotlin.backend.common.ClassLoweringVisitor.visitClass(Lower.kt:101)
	at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid$DefaultImpls.visitClass(IrElementVisitorVoid.kt:44)
	at org.jetbrains.kotlin.backend.common.ClassLoweringVisitor.visitClass(Lower.kt:92)
	at org.jetbrains.kotlin.backend.common.ClassLoweringVisitor.visitClass(Lower.kt:92)
	at org.jetbrains.kotlin.ir.declarations.IrClass.accept(IrClass.kt:46)
	at org.jetbrains.kotlin.ir.declarations.IrFile.acceptChildren(IrFile.kt:28)
	at org.jetbrains.kotlin.ir.visitors.IrVisitorsKt.acceptChildrenVoid(IrVisitors.kt:15)
	at org.jetbrains.kotlin.backend.common.ClassLoweringVisitor.visitElement(Lower.kt:96)
	at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid$DefaultImpls.visitPackageFragment(IrElementVisitorVoid.kt:30)
	at org.jetbrains.kotlin.backend.common.ClassLoweringVisitor.visitPackageFragment(Lower.kt:92)
	at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid$DefaultImpls.visitFile(IrElementVisitorVoid.kt:37)
	at org.jetbrains.kotlin.backend.common.ClassLoweringVisitor.visitFile(Lower.kt:92)
	at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid$DefaultImpls.visitFile(IrElementVisitorVoid.kt:38)
	at org.jetbrains.kotlin.backend.common.ClassLoweringVisitor.visitFile(Lower.kt:92)
	at org.jetbrains.kotlin.backend.common.ClassLoweringVisitor.visitFile(Lower.kt:92)
	at org.jetbrains.kotlin.ir.declarations.IrFile.accept(IrFile.kt:22)
	at org.jetbrains.kotlin.ir.visitors.IrVisitorsKt.acceptVoid(IrVisitors.kt:11)
	at org.jetbrains.kotlin.backend.common.LowerKt.runOnFilePostfix(Lower.kt:89)
	at org.jetbrains.kotlin.backend.common.ClassLoweringPass$DefaultImpls.lower(Lower.kt:44)
	at io.realm.kotlin.compiler.RealmModelLowering.lower(RealmModelLoweringExtension.kt:47)
	at org.jetbrains.kotlin.backend.common.LowerKt.lower(Lower.kt:75)
	at io.realm.kotlin.compiler.RealmModelLoweringExtension.generate(RealmModelLoweringExtension.kt:43)
	at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.convertToIr$lambda-1(JvmIrCodegenFactory.kt:162)
	at org.jetbrains.kotlin.psi2ir.Psi2IrTranslator.generateModuleFragment(Psi2IrTranslator.kt:99)
	at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.convertToIr(JvmIrCodegenFactory.kt:194)
	at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.convertToIr(JvmIrCodegenFactory.kt:53)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.convertToIr(KotlinToJVMBytecodeCompiler.kt:232)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:115)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:60)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:157)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:52)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:94)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:43)
	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101)
	at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:477)
	at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:127)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:366)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally$default(IncrementalCompilerRunner.kt:311)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:175)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:75)
	at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:625)
	at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:101)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1739)
	at jdk.internal.reflect.GeneratedMethodAccessor101.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360)
	at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
	at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:712)
	at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:587)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:705)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:704)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)
@edualonso
Copy link
Contributor

Hi @Burtan. We will try to devise a fix for this problem since the code you wrote is syntactically correct and the error appears to be stemming from our compiler plugin.

Though I have an observation about the class definition you wrote. I assume you want to create an inheritance hierarchy by writing, say

open class NewBaseEntity<T> : RealmObject // I added the `open` modifier myself
class Foo : NewBaseEntity<String>()

While the class declaration you wrote and the one above are syntactically correct, inheritance/polymorphism is not supported by Realm yet and therefore you will not be able to use this in your schema - see realm/realm-java#761 for more info. This constraint from our SDK would render your problem moot (if this is in fact what you are trying to do) as you would not be able to use said hierarchies in Realm.

@realm realm deleted a comment from sync-by-unito bot Sep 11, 2022
@Burtan
Copy link
Author

Burtan commented Sep 12, 2022

I think my case is a little special. I'm using multiple interfaces on my entity classes

class A: RealmObject, Bable<T>, Cable<T>, Dable<T>

To use generics on a combination of these interfaces I need a common interface that I don't inherit from.

MetaInterface<T>: RealmObject, Bable<T>, Cable<T>

fun doSomething<A: MetaInterface<T>>()

@edualonso
Copy link
Contributor

Hi @Burtan.

Even though you are not using MetaInterface actively for inheritance purposes, our compiler plugin visits all model definitions implementing RealmObject and their fields to generate the code that allows us to create your database schema when you open a realm. This will ultimately fail as models implementing RealmObject need to have a zero-argument constructor for us to be able to instantiate them as managed Realm objects. Since interfaces do not have such a constructor we reject them as invalid - we do not support inline instantiation as we would not be able to handle compile time-defined properties in the schema. These constraints are established by Realm Core because they do not support polymorphic inheritance so we cannot lift this requirement yet.

Additionally:

// currently not working, requires a bug fix
class MyClass<T>: RealmObject, Bable<T>, Cable<T>, Dable<T> {
  @io.realm.kotlin.types.annotations.Ignore
  var myGenericField: T? = null // MUST BE IGNORED as this is a dynamic type and we cannot generate appropriate schema creation instructions in compile time
}

should be allowed as long as your Xable<T> interfaces do not inherit from RealmObject and you ignore any generic fields - see comment in the snippet.

Something along these lines should also be allowed, again, as long as you don't make your interface inherit from RealmObject:

interface MyInterface<T> {
  var genericField: T?
}

// currently not working, requires a bug fix
class MyGenericClass<T> : RealmObject, MyInterface<T> {
  @io.realm.kotlin.types.annotations.Ignore
  override var genericField: T? = null // MUST be ignored, same reason as above
}

class MyStringClass : RealmObject, MyInterface<String> {
  override var genericField: String? = null // OK
}

We will try to devise a bug fix for class MyClass<T>: RealmObject.

@Burtan
Copy link
Author

Burtan commented Sep 15, 2022

Thanks for the effort and extensive explanation already!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants