Skip to content

Wrong type representation when using typeMember to select a type inherited from a base class #19825

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

Closed
OndrejSpanel opened this issue Feb 29, 2024 · 2 comments
Assignees
Labels
area:metaprogramming:quotes Issues related to quotes and splices itype:bug

Comments

@OndrejSpanel
Copy link
Member

When using typeMember function of TypeRepr.of[OuterType].typeSymbol, the resulting symbol is represented as Base.this.InnerType, not OuterType.InnerType. This is a problem when creating a macro which is expected to return an expression of OuterType.InnerType type.

This was found while working on Surface representation of types derived from Scala 2 Enumeration, see wvlet/airframe#3429.

Compiler version

3.3.2, 3.4.0, 3.4.1-RC1

Minimized code

trait Base {
  class InnerType
}

object OuterType extends Base {
  def create: InnerType = new InnerType
}

@main
def main(): Unit = {
  val typeDesc = TypeUtil.memberTypeName[OuterType.type, OuterType.InnerType]
  println(s"Type $typeDesc")
}
import scala.quoted.*

object TypeUtil {
  inline def memberTypeName[A, B] = ${ memberTypeNameImpl[A, B]}

  private def memberTypeNameImpl[A, B](using typeA: Type[A], typeB: Type[B], quotes: Quotes): Expr[String] = {
    import quotes.reflect.*
    val repr = TypeRepr.of(using typeA)
    val memberName = TypeRepr.of(using typeB).typeSymbol.name
    val result = repr.show + "." + memberName +": " + repr.typeSymbol.typeMember(memberName).typeRef.show
    Expr(result)
  }
}

Output

Type OuterType.InnerType: Base.this.InnerType

Expectation

The output should be OuterType.InnerType: OuterType.InnerType.

@jchyb
Copy link
Contributor

jchyb commented Nov 28, 2024

Apologies for the very late reply, but this is actually correct behavior. in the quotes reflect API, Symbols do not store type prefix information (whereas TypeRepr do have those). They are moreso a representation of a part of the code that defines them and their attributes, so for TypeRepr.of[OuterType].typeMember("InnerType") we get a symbol pointing to Base.InnerType:

trait Base {
  class InnerType // <- with a Symbol we reference exactly that, no more, no less
}

We lose that type information about the type prefix there, so when calling typeRef on that symbol, we are not able to regain it.
This is why the scaladoc for typeRef explains:

Type reference to the symbol usable in the scope of its owner
To get a reference to a symbol from a specific prefix `tp`, use `tp.select(symbol)` instead.
*  @see TypeReprMethods.select

("usable in the scope of an owner" means that if we were calling that macro from inside of OuterType, the compiler would know what Base.this.InnerType references).

Regardless, the rewritten macro with TypeRepr.select would look like this:

object TypeUtil {
  inline def memberTypeName[A, B] = ${ memberTypeNameImpl[A, B]}

  private def memberTypeNameImpl[A, B](using typeA: Type[A], typeB: Type[B], quotes: Quotes): Expr[String] = {
    import quotes.reflect.*
    val repr = TypeRepr.of(using typeA)
    val memberName = TypeRepr.of(using typeB).typeSymbol.name
    val result = repr.show + "." + memberName +": " + repr.select(repr.typeSymbol.typeMember(memberName)).show
    Expr(result)
  }
}

@jchyb jchyb closed this as not planned Won't fix, can't repro, duplicate, stale Dec 16, 2024
@OndrejSpanel
Copy link
Member Author

Thanks for looking into this. I more or less gave up in trying to get Surface working, I created a light fork which does not need stuff like this, therefore I am unable to check if the rewritten macro would suit the purpose, I already lost context meanwhile and it would be non practical to try to return to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:metaprogramming:quotes Issues related to quotes and splices itype:bug
Projects
None yet
Development

No branches or pull requests

3 participants