sealed-enum icon indicating copy to clipboard operation
sealed-enum copied to clipboard

Sealed interface with a bounded type parameter generates code with errors

Open eygraber opened this issue 2 years ago • 4 comments

If I have the following code:

sealed interface Foo {
  object Bar : Foo
  object Baz : Foo
}

sealed interface FooProvider<T : Foo> {
  object BarProvider : FooProvider<Foo.Bar>
  object BazProvider : FooProvider<Foo.Baz>

  @GenSealedEnum
  companion object
}

then the resulting code is generated, but with errors (I added the errors as inline comments):

/**
 * An implementation of [SealedEnum] for the sealed class [FooProvider]
 */
public object FooProviderSealedEnum : SealedEnum<FooProvider<Foo>> {
    public override val values: List<FooProvider<Foo>> by lazy(mode =
            LazyThreadSafetyMode.PUBLICATION) {
        listOf(
            FooProvider.BarProvider,
            FooProvider.BazProvider
        )
    }

  /*
  *Property delegate must have a 'getValue(FooProviderSealedEnum, KProperty*>)' method. None of the following functions are suitable.
  *Lazy<List<FooProvider<out Foo>>>.getValue(Any?, KProperty<*>)   where T = List<FooProvider<out Foo>> for    inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T defined in kotlin
  */


    public override fun ordinalOf(obj: FooProvider<Foo>): Int = when (obj) {
        is FooProvider.BarProvider -> 0 // Incompatible types: FooProvider.BarProvider and FooProvider<Foo>
        is FooProvider.BazProvider -> 1 // Incompatible types: FooProvider.BazProvider and FooProvider<Foo>
    }

    public override fun nameOf(obj: FooProvider<Foo>): String = when (obj) {
        is FooProvider.BarProvider -> "FooProvider_BarProvider" // Incompatible types: FooProvider.BarProvider and FooProvider<Foo>
        is FooProvider.BazProvider -> "FooProvider_BazProvider" // Incompatible types: FooProvider.BazProvider and FooProvider<Foo>
    }

    public override fun valueOf(name: String): FooProvider<Foo> = when (name) {
        "FooProvider_BarProvider" -> FooProvider.BarProvider // Type mismatch. Required: FooProvider<Foo> Found: FooProvider.BarProvider
        "FooProvider_BazProvider" -> FooProvider.BazProvider // Type mismatch. Required: FooProvider<Foo> Found: FooProvider.BazProvider
        else -> throw IllegalArgumentException("""No sealed enum constant $name""")
    }
}

eygraber avatar Jun 07 '23 04:06 eygraber

The processor is trying to match the type specified as close as possible, and the following won't compile either:

val values: List<FooProvider<Foo>> = listOf(
    FooProvider.BarProvider,
    FooProvider.BazProvider
)

Does it work for your case to have your generic type be defined as out T : Foo?

sealed interface FooProvider<out T : Foo> {
  object BarProvider : FooProvider<Foo.Bar>
  object BazProvider : FooProvider<Foo.Baz>

  @GenSealedEnum
  companion object
}

alexvanyo avatar Jun 07 '23 05:06 alexvanyo

No in my actual use case it needs to be invariant.

eygraber avatar Jun 07 '23 05:06 eygraber

Which type X would you expect SealedEnum<X> to be in your case?

alexvanyo avatar Jun 07 '23 15:06 alexvanyo

That's a good question :smile:

eygraber avatar Jun 07 '23 21:06 eygraber