Skip to content

Symbol#overridingSymbol returns NoSymbol for correct arguments #17349

Open
@Iltotore

Description

@Iltotore

Compiler version

3.3.1-RC1-bin-20230425-c9ca7ce-NIGHTLY

Minimized code

//> using scala "3.3.1-RC1-bin-20230425-c9ca7ce-NIGHTLY"

import scala.quoted.*

abstract class Foo:

  def test: String

inline def showExpr(inline value: Any): String = ${showExprImpl('value)}

def showExprImpl(expr: Expr[Any])(using Quotes): Expr[String] =
  import quotes.reflect.*

  Expr(
    s"""${expr.asTerm.show}
       |
       |${expr.asTerm.show(using Printer.TreeStructure)}""".stripMargin)

inline def generateFooOverride: Foo = ${generateFooImpl}

def generateFooImpl(using Quotes): Expr[Foo] =
  import quotes.reflect.*

  val fooRepr = TypeRepr.of[Foo]
  val fooSymbol = fooRepr.dealias.classSymbol.get

  val overridingSymbol = Symbol.newClass(Symbol.spliceOwner, Symbol.freshName("Foo"), List(fooRepr), _ => Nil, None)
  def newInstance(clazz: Symbol, args: List[Term]): Term = Apply(Select(New(TypeTree.ref(clazz)), clazz.primaryConstructor), args)

  val fooTestSymbol = fooSymbol.declaredMethod("test").head
  val overridingTestSymbol = fooTestSymbol.overridingSymbol(overridingSymbol)

  println(fooTestSymbol.flags.is(Flags.Private).toString) //false as expected
  println(fooTestSymbol.isClassConstructor.toString) //false as expected
  println(fooTestSymbol.maybeOwner.isClassDef.toString) //true as expected
  println(overridingSymbol.isClassDef.toString) //true as expected
  println(overridingTestSymbol.isNoSymbol.toString) //true but should be false

  def overridingTestDef(term: Term): DefDef =
    DefDef( //java.lang.AssertionError: assertion failed: expected a term symbol but received val <none>
      overridingTestSymbol,
      _ => Some(term)
    )

  def overridingClassDef(term: Term): ClassDef =
    ClassDef(
      overridingSymbol,
      List(newInstance(fooSymbol, Nil)),
      List(overridingTestDef(term))
    )

  def instance(term: Term): Term =
    Block(
      List(overridingClassDef(term)),
      newInstance(overridingSymbol, Nil)
    )

  instance(Literal(StringConstant("hello"))).asExprOf[Foo]

In another file call generateFooOverride

Output

overridingTestSymbol is NoSymbol causing the DefDef in overridingTestDef to throw the following error:

[error] java.lang.AssertionError: assertion failed: expected a term symbol but received val <none>
[error]         at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
[error]         at scala.quoted.runtime.impl.QuotesImpl$reflect$DefDef$.apply(QuotesImpl.scala:279)
[error]         at scala.quoted.runtime.impl.QuotesImpl$reflect$DefDef$.apply(QuotesImpl.scala:278)
[error]         at ClassGen$package$.overridingTestDef$1(ClassGen.scala:41)
[error]         at ClassGen$package$.overridingClassDef$1(ClassGen.scala:48)
[error]         at ClassGen$package$.instance$1(ClassGen.scala:53)
[error]         at ClassGen$package$.generateFooImpl(ClassGen.scala:57)

Expectation

I'd expect overridingTestSymbol to not be NoSymbol and generate this code:

class Foo$macro$1 extends Foo:

  override def test: String = "hello"

new Foo$macro$1

mostly like:

new Foo:

  override def test: String = "hello"

Notes

  • I use the low-level term API because in my current use case I need to generate this code for a generic type (T instead of Foo). I'm trying to reproduce this macro.
  • After diving into the compiler codebase, I checked if my arguments satisfy the criterias needed by Symbol#overridingSymbol (see line 34). They all pass.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions