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

Support enums #19

Merged
merged 5 commits into from
Feb 23, 2024
Merged

Support enums #19

merged 5 commits into from
Feb 23, 2024

Conversation

mrdziuban
Copy link

@mrdziuban mrdziuban commented Feb 23, 2024

enums caused two issues (1 and 2 below), the fix for one of which (2) led to another issue (3)

1. Singleton enum members are desugared to vals instead of objects

This breaks the way that we generate references to ADT members in TypeScript code. Calling .getClass.getName shows that it's an anonymous instance of the class, and .getClass.getSimpleName returns an empty string.

sealed trait Foo
object Foo {
  case object Bar extends Foo
}
Foo.Bar.getClass.getName       // path.of.package.Foo$Bar$
Foo.Bar.getClass.getSimpleName // Bar$

enum Foo {
  case Bar
}
Foo.Bar.getClass.getName       // path.of.package.Foo$$anon$1
Foo.Bar.getClass.getSimpleName // ""

The fix

871b32b

If the value we're referencing is a Product, we can use its productPrefix instead of getClass.getSimpleName. This produces the same result and works properly for enum members.

2. Getting the type of a val defined in an enum member didn’t work

We previously got the type of a val using its ValDef tree, and expected that tree to have a right-hand side (which is what Some(term) is in that code). This doesn't work with enums, the ValDef trees have None for the RHS.

sealed trait Foo { val id: Int }
object Foo {
  case object Bar extends Foo { val id = 1 } // the `ValDef` for `Bar#id` has a RHS
}

enum Foo(val id: Int) {
  case Bar extends Foo(1) // the `ValDef` for `Bar#id` does not have a RHS
}

The fix

We can get the type of the val using typeRepr.memberType(sym) where typeRepr is the TypeRepr that represents the enum member and sym is the Symbol that represents the the val.

This has one benefit: we no longer require the -Yretain-trees compiler option, as we're no longer using the ValDef tree.

It also has one major drawback: the result of typeRepr.memberType(sym) is a less specific type than what we get now (see below).

3. The type we can determine for enum members' vals is less specific

Our old approach (described in issue 2 above) would produce the most specific type possible, whereas the new approach produces the type defined in the member's parent.

sealed trait Foo { val id: Int }
object Foo {
  case object Bar extends Foo { val id = 1 } // the type of `Bar#id` is the singleton type `1`
}

enum Foo(val id: Int) {
  case Bar extends Foo(1) // the type of `Bar#id` is `Int`
}

This ultimately causes issues with ADTs that contain references to themselves, or to other types being generated in the same TypeScript file.

Our previous type sorting logic relies on an instance of ReferencedTypes to know which types reference others, but with the less specific types in place, these references weren't reliable. For example:

sealed trait Foo { val parent: Eval[Option[Foo]] }
object Foo {
  // With the more specific types, `ReferencedTypes` would properly show that `Bar` refers to `Baz`
  // so the output TS code would ensure that `Baz` is generated before `Bar`
  case object Bar extends Foo { val parent = Eval.later(Some(Baz)) }
  case object Baz extends Foo { val parent = Eval.now(None) }
}

enum Foo(val parent: Eval[Option[Foo]]) {
  // With the less specific types, `ReferencedTypes` just shows that `Bar` refers to `Foo`
  // so the output TS code is not ordered correctly
  case Bar extends Foo(Eval.later(Some(Baz)))
  case Baz extends Foo(Eval.now(None))
}

The fix

f53c463

Instead of producing a ReferencedTypes based on the TypeNames contained within the TsModels being generated, we now sort definitions by inspecting the generated code. We first find the names of all defined consts, functions, and types. We then look for references to those names in other definitions, and sort the definitions accordingly.

@mrdziuban mrdziuban merged commit 08cf2c3 into master Feb 23, 2024
5 checks passed
@mrdziuban mrdziuban deleted the enum-support branch February 23, 2024 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
1 participant