Skip to content

Conversation

@DavidGregory084
Copy link

@DavidGregory084 DavidGregory084 commented Jun 25, 2025

Pull Request Checklist

  • Have you read through the contributor guidelines?
  • Have you squashed your commits?
  • Have you added copyright headers to new files?
  • Have you updated the documentation?
  • Have you added tests for any changed functionality?

Fixes

Fixes #1017.

However, it does so using Scala 3's Mirror.Sum. This forbids any subclasses which are not products or sums themselves.

This is why the test for knownSubclasses is failing - TestUnion.UT has a subclass UC which is a normal class.

Purpose

This PR adds support for Scala 3 enums.

Background Context

The problem with the current implementation is that it assumes that the .children of a TypeRepr will be subclasses of that TypeRepr.

Scala 3 enum cases are encoded as vals which are bound to an anonymous class. They are ascribed with the type of the parent definition.

For example:

enum Color {
  case Red, Green, Blue
}

// Color.Red is encoded as ===>

val Red: Color = $new(0, "Red")

This causes the subclasses function of the knownSubclasses helper to loop indefinitely trying to find the subclasses of Color because the tpt.tpe of the ValDef children is the same TypeRepr for Color that we started with. This is the cause for #1019.

I do not know how to get the singleton TypeRepr of the enum case from the ValDef, but I think that would be the way to solve this problem while preserving the existing behaviour of the macro.

References

Scala 3 Enumerations Implementation

if types.isEmpty then None else Some(types)
tpr.asType match {
case '[t] =>
Expr.summon[Mirror.Of[t]].collect {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that a less WIP implementation of this should probably use Implicits.search so that it could surface any implicit search error messages to the user


val tpeCaseName: Expr[String] = '{
${ config }.typeNaming(${ Expr(typeName(tpr.typeSymbol)) })
${ config }.typeNaming(${ Expr(typeName(if isCase then termSym else typeSym)) })
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By using the termSymbol we get the fully qualified name of the enum case rather than the name of the enum as our type discriminator.

tpd.tpt.tpe

case vd: ValDef =>
vd.tpt.tpe
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what to do here to get the singleton Type that the compiler-generated Mirror gives us

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Scala 3 Enums

1 participant