Skip to content
This repository has been archived by the owner on Jun 5, 2023. It is now read-only.

Cannot instantiate traits with Scala 3 macros nor inline methods #155

Open
xerial opened this issue Dec 28, 2020 · 0 comments
Open

Cannot instantiate traits with Scala 3 macros nor inline methods #155

xerial opened this issue Dec 28, 2020 · 0 comments

Comments

@xerial
Copy link

xerial commented Dec 28, 2020

Use Case

Creating an instance of a given Type[A] enriched with helper traits.

This functionality is necessary for porting Airframe DI, a dependency injection library (https://wvlet.org/airframe/docs/airframe), to Scala 3. Scala 2 has an untyped quasiquotes, so we can generate new A with ... { ... }. Scala 3 macros has strict type checking, so if A might not be able to instantiate, it reject any code generation for new A { ... } even when A is just a trait.

Minimized code

scala> import scala.quoted._

scala> trait LogSupport
// defined trait LogSupport

scala> inline def mk[A] = { new A with LogSupport {} }
1 |inline def mk[A] = { new A with LogSupport {} }
  |                         ^
  |                         A is not a class type

Using Scala 3 macro show the same error:

def newInstanceImpl[A](using quotes:Quotes, t:Type[A]): Expr[A] = {
  '{ 
      new A {}  // Compile error: A is not a class type
  }
}

Output

The compiler internally generates an anonymous class if A is a trait:

val e = '{
  trait A; new A {}
}

pritnln(e.show)
{
  final class $anon() extends A

  (new $anon(): A)
}

But if type A is given as Type[A] to Scala 3 macros or inline methods, even though Type[A] is referencing a trait, we cannot generate code equivalent to new A {}.

I've added a code to dotty to show the stack trace around this compile error. It seems Type.underlyingClassRef called at checkClassType failed to resolve the anonymous class generated by Dotty (3.0.0-M3):

[error]    |A is not a class type:
[error]    |java.lang.Throwable
[error]    |    at dotty.tools.dotc.typer.Checking.checkClassType(Checking.scala:762)
[error]    |    at dotty.tools.dotc.typer.Checking.checkClassType$(Checking.scala:653)
[error]    |    at dotty.tools.dotc.typer.Typer.checkClassType(Typer.scala:101)
[error]    |    at dotty.tools.dotc.typer.Namer$ClassCompleter.checkedParentType$2(Namer.scala:1180)
[error]    |    at dotty.tools.dotc.typer.Namer$ClassCompleter.$anonfun$1(Namer.scala:1212)
[error]    |    at scala.collection.immutable.List.map(List.scala:246)
[error]    |    at dotty.tools.dotc.typer.Namer$ClassCompleter.completeInCreationContext(Namer.scala:1212)
[error]    |    at dotty.tools.dotc.typer.Namer$Completer.complete(Namer.scala:732)
[error]    |    at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeFrom(SymDenotations.scala:166)
[error]    |    at dotty.tools.dotc.core.Denotations$Denotation.completeInfo$1(Denotations.scala:188)
[error]    |    at dotty.tools.dotc.core.Denotations$Denotation.info(Denotations.scala:190)
[error]    |    at dotty.tools.dotc.core.SymDenotations$SymDenotation.ensureCompleted(SymDenotations.scala:370)
[error]    |    at dotty.tools.dotc.typer.Typer.retrieveSym(Typer.scala:2473)
[error]    |    at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:2498)
[error]    |    at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:2592)
[error]    |    at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2662)
[error]    |    at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2666)
[error]    |    at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:2688)
[error]    |    at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:2738)
[error]    |    at dotty.tools.dotc.typer.Typer.typedBlockStats(Typer.scala:937)

Expectation

  • There should be a way to instantiate a Trait with inline methods and Scala 3 macros.
  • If A is not a trait, this compilation error is valid. We need a workaround if A is safe to instantiate when an anonymous class that extends A will be generated.
  • As a workaround, I've also tried to rewriting the AST using TreeMap by creating a concrete trait (e.g., trait X; new X{}, then rewrite X to A), but new X {} code will generate Template node, which is not yet exposed to Reflect API TreeMap doesn't traverse Template class scala/scala3#10931, so this approach also didn't work
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant