Skip to content

Unable to use self reference in refinement within quotes #17409

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
steinybot opened this issue May 4, 2023 · 6 comments · Fixed by #17415
Closed

Unable to use self reference in refinement within quotes #17409

steinybot opened this issue May 4, 2023 · 6 comments · Fixed by #17415
Labels
area:metaprogramming:quotes Issues related to quotes and splices itype:bug

Comments

@steinybot
Copy link
Contributor

Compiler version

3.3.0-RC3

Minimized code

import scala.quoted.*

transparent inline def thing =
  ${ thingImpl }

def thingImpl(using Quotes): Expr[Any] =
  '{
    def makeThing: { def me: this.type } = ???
    makeThing
  }

Output

access to <refinement>.this from wrong staging level:
 - the definition is at level 0,
 - but the access is at level 1.

Expectation

I think this should compile.

I know that self reference in refinement is deprecated but I haven't figured out another way to refer to the same type, especially since cyclic type references are illegal which is annoying.

I can't figure out how to do this with the Quotes API either. Refinement doesn't take a symbol so there is nothing to give to This.

@steinybot steinybot added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels May 4, 2023
@nicolasstucki nicolasstucki added area:metaprogramming:quotes Issues related to quotes and splices and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels May 4, 2023
@nicolasstucki
Copy link
Contributor

This should work.

We are probably not recording the level of the refinement or the this definition, in which case the default level of 0 is used.

@sjrd
Copy link
Member

sjrd commented May 4, 2023

I can't figure out how to do this with the Quotes API either. Refinement doesn't take a symbol so there is nothing to give to This.

You need to wrap your refinement with a RecursiveType and use a RecursiveThis inside to get access to this.

@steinybot
Copy link
Contributor Author

I can't figure out how to do this with the Quotes API either. Refinement doesn't take a symbol so there is nothing to give to This.

You need to wrap your refinement with a RecursiveType and use a RecursiveThis inside to get access to this.

I tried that but the mistake I made was that I was literally recursing it as in:

  val refinement = labels.foldLeft(RecursiveType(_ => TypeRepr.of[Object])) {
    case (result, label) =>
      val methodType = MethodType(Nil)(_ => Nil, _ => result.recThis)
      val resultWithMethod = Refinement(result, label, methodType)
      RecursiveType(_ => resultWithMethod)
  }

Which blows up the pickler with an AssertionError.

This does indeed work:

  val recursiveType = RecursiveType(_ => TypeRepr.of[Object])
  val refinement = labels.foldLeft[TypeRepr](recursiveType) {
    case (result, label) =>
      val methodType = MethodType(Nil)(_ => Nil, _ => recursiveType.recThis)
      Refinement(result, label, methodType)
  }

Thank you! I really appreciate the quick response and providing the answer I was actually looking for all along.

@steinybot
Copy link
Contributor Author

Woops that's still not right. It is coming out as me: this instead of me: this.type.

Here is a more complete example:

import scala.quoted.*

class SelectMe extends Selectable {
  def selectDynamic(name: String): Any = name match
    case "name" => "Bob"
    case "me" => this
}

transparent inline def thing =
  ${ thingImpl }

def thingImpl(using Quotes): Expr[Any] =
  import quotes.reflect._

  val recursiveType = RecursiveType(_ => TypeRepr.of[Object])
  val refinementType =
    Refinement(
      Refinement(
        recursiveType,
        "name",
        TypeRepr.of[String]
      ),
      "me",
      recursiveType.recThis
    ).asType

  val result = refinementType match
    case '[t] => '{
      (new SelectMe).asInstanceOf[t]
    }
  println(result.show)
  result

def thing2 = new SelectMe().asInstanceOf[java.lang.Object {
  val name: java.lang.String
  val me: this.type
}]

This:

thing.me.name

Fails with:

[info] compiling 1 Scala source to /Users/jason/src/bug-reports/app/target/scala-3.3.0-RC4/classes ...
new SelectMe().asInstanceOf[java.lang.Object {
  val name: java.lang.String
  val me: this
}]
[error] -- [E008] Not Found Error: /Users/jason/src/bug-reports/app/src/main/scala/Main.scala:51:11
[error] 51 |  thing.me.name
[error]    |  ^^^^^^^^^^^^^
[error]    |value name is not a member of {z1 => Object} - did you mean {z1 => Object}.wait?

But thing2.me.name works.

@sjrd
Copy link
Member

sjrd commented May 4, 2023

You can only use recThis "lexically" within the recursive type:

  val recursiveType = RecursiveType({ recType =>
    Refinement(
      Refinement(
        TypeRepr.of[Object],
        "name",
        TypeRepr.of[String]
      ),
      "me",
      recType.recThis
    )
  }).asType

@nicolasstucki
Copy link
Contributor

This is already fixed.

nicolasstucki added a commit to dotty-staging/dotty that referenced this issue May 4, 2023
anatoliykmetyuk added a commit that referenced this issue May 5, 2023
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

Successfully merging a pull request may close this issue.

3 participants