Skip to content

Add regression test #18300

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

Merged
merged 1 commit into from
Jul 28, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions tests/pos-macros/quoted-with-precise-types.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import scala.quoted.*

def withType[T <: AnyKind, U](tpe: Type[T])(body: [X <: T] => Type[X] ?=> U)(using Quotes): U =
type X <: T
val tpeX: Type[X] = tpe.asInstanceOf[Type[X]]
body[X](using tpeX)

def test1(t1: Type[?], t2: Type[? <: Any])(using Quotes) =
withType(t1) { [T <: AnyKind] => _ ?=> // TODO remove _ ?=> // Implementation restriction: polymorphic function literals must have a value parameter
Type.of[T]
Type.show[T]
}
withType(t2) { [T] => _ ?=> // TODO remove _ ?=>
'{ val a: T = ??? }
Type.of[T]
Type.show[T]
}
withType(t2):
[T] => _ ?=> '{ val a: T = ??? } // TODO remove _ ?=>

def exprWithPreciseType[T, U](expr: Expr[T])(body: [X <: T] => Type[X] ?=> Expr[X] => U)(using Quotes): U =
import quotes.reflect.*
type X <: T
val exprX = expr.asInstanceOf[Expr[X]]
val tpeX = expr.asTerm.tpe.asType.asInstanceOf[Type[X]]
body[X](using tpeX)(exprX)
Copy link
Member

Choose a reason for hiding this comment

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

Why not pass T to body and avoid the casts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is to be able to refine the type of T to the precise type of the expression. For example if T is of type Any but the expression is of type Int, we can use that type in the generated code in a type safe way. Note that the cast would only be required in the stdlib method that provides this functionality.

Copy link
Member

Choose a reason for hiding this comment

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

but body is not inlined so I don't understand how this would change anything to the code we generate

Copy link
Contributor Author

@nicolasstucki nicolasstucki Jul 28, 2023

Choose a reason for hiding this comment

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

It is about the code generated inbody. For example

def foo[T: Type](expr: Expr[T])(using Quotes) =  '{ val y: T = $x }
def bar[T: Type](expr: Expr[T])(using Quotes) = 
  exprWithPreciseType(a) { [X] => _ ?=> x => 
    '{ val y: X = $x }
  }
val a: Expr[Int] = '{ 1 }
foo(a) // generates '{ val y: Int = 1 }
foo[Any](a) // generates '{ val y: Any = 1 } // boxing
foo(b) // generates '{ val y: Any = 1 } // boxing

val b: Expr[Any] = '{ 2 }
bar(a) // generates '{ val y: Int = 2 }
bar[Any](a) // generates '{ val y: Int = 2 }
bar(b) // generates '{ val y: Int = 2 }

Other uses involve getting the precise type to inspect it.

In general it is equivalent but will always be more efficient than

def baz[T: Type](expr: Expr[T])(using Quotes) = 
  expr match
    case '{ $x: t } =>  '{ val y: t = $x }

Though the main use case will probably be when we get a term or type from reflection and want to recover/name its precise type.

....
exprWithPreciseType(term.asExpr/*: Expr[Any]*/) { [T] => _ ?=> x =>
   '{ val y: T= $x; ... }
}

withType(tpe.asType/*: Type[? <: AnyKind]*/) { [T] => _ ?=> 
  someFunction[T]//(using Type.of[T])
}

Copy link
Member

@smarter smarter Aug 1, 2023

Choose a reason for hiding this comment

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

I'm still confused, if I make the following change:

diff --git tests/pos-macros/quoted-with-precise-types.scala tests/pos-macros/quoted-with-precise-types.scala
index e9620810377..ddfc5dfefa7 100644
--- tests/pos-macros/quoted-with-precise-types.scala
+++ tests/pos-macros/quoted-with-precise-types.scala
@@ -20,10 +20,8 @@ def test1(t1: Type[?], t2: Type[? <: Any])(using Quotes) =

 def exprWithPreciseType[T, U](expr: Expr[T])(body: [X <: T] => Type[X] ?=> Expr[X] => U)(using Quotes): U =
   import quotes.reflect.*
-  type X <: T
-  val exprX = expr.asInstanceOf[Expr[X]]
-  val tpeX = expr.asTerm.tpe.asType.asInstanceOf[Type[X]]
-  body[X](using tpeX)(exprX)
+  val tpe = expr.asTerm.tpe.asType.asInstanceOf[Type[T]]
+  body[T](using tpe)(expr)

 def test2(x: Expr[Any])(using Quotes) =
   // exprWithPreciseType(x) { [T] => x => // Inference limitation: x is assumed to be the Type[T] instead of the Expr[T]

Then the test still compiles, will it generate different code?


def test2(x: Expr[Any])(using Quotes) =
// exprWithPreciseType(x) { [T] => x => // Inference limitation: x is assumed to be the Type[T] instead of the Expr[T]
exprWithPreciseType(x) { [T] => _ ?=> x => // TODO remove _ ?=>
Type.of[T]
'{ val a: T = $x }
}
exprWithPreciseType('{1}) { [T <: Int] => _ ?=> x => // TODO remove _ ?=>
Type.of[T]
'{ val a: Int = $x }
'{ val a: T = $x }
'{ val a: T = i($x) }
}

def i[T <: Int](x: T): T = x