Skip to content

Commit 22c2f31

Browse files
committed
Decouple quoted Type interface from encoding
Before, `quoted.Type` was defined as ```scala // old class Type[X <: AnyKind]: type T = X ``` Where `T` is the proper type of the instance of a `Type`. This is the type used for all operations within the compiler. The only purpose of the type `X` was to be able to write context bounds as `[X: Type]` and staged types `Type[X]` instead of `Type { type T = X }`. Unfortunately, the type `X` makes the assumption that `X` is known, which implies that instances of `Type` with unknown types need to be encoded with a whildcard type. A `val t: Type[?] = ...` is unusable as we cannot heal staged wildcard types, even though we do have a `t.T` which can be handled. Now, we change the definition of `Type` to only have the type member and rename it to `QuotedType`. ```scala // new class QuotedType: type T <: AnyKind ``` This solves the aforementioned limitations with unkown staged types. Now we are also able to remove the wildcard from `QuoteContext.tasty.Type.seal` and the result can be used in quotes. To keep the context bound syntax and provide more descriptive name for the concept, we defined the `quoted.Type` type alias. ```scala type Type[X <: AnyKind] = QuotedType { type T = X } ```
1 parent 9ef5859 commit 22c2f31

File tree

26 files changed

+220
-53
lines changed

26 files changed

+220
-53
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,14 @@ class Definitions {
688688
@tu lazy val QuotedExprModule_nullExpr: Symbol = QuotedExprModule.requiredMethod(nme.nullExpr)
689689
@tu lazy val QuotedExprModule_unitExpr: Symbol = QuotedExprModule.requiredMethod(nme.unitExpr)
690690

691+
@tu lazy val QuotedTypeAliasClass: Symbol = requiredPackage("scala.quoted").typeRef.select("Type".toTypeName).typeSymbol
692+
693+
@tu lazy val QuotedTypeClass: ClassSymbol = requiredClass("scala.quoted.QuotedType")
694+
@tu lazy val QuotedType_splice: Symbol = QuotedTypeClass.requiredType(tpnme.spliceType)
695+
696+
@tu lazy val QuotedTypeModule: Symbol = QuotedTypeClass.companionModule
697+
@tu lazy val QuotedTypeModule_apply: Symbol = QuotedTypeModule.requiredMethod("apply")
698+
691699
@tu lazy val QuoteContextClass: ClassSymbol = requiredClass("scala.quoted.QuoteContext")
692700

693701
@tu lazy val LiftableModule: Symbol = requiredModule("scala.quoted.Liftable")
@@ -720,12 +728,6 @@ class Definitions {
720728
@tu lazy val InternalQuotedTypeModule: Symbol = requiredModule("scala.internal.quoted.Type")
721729
@tu lazy val InternalQuotedType_unapply: Symbol = InternalQuotedTypeModule.requiredMethod(nme.unapply)
722730

723-
@tu lazy val QuotedTypeClass: ClassSymbol = requiredClass("scala.quoted.Type")
724-
@tu lazy val QuotedType_splice: Symbol = QuotedTypeClass.requiredType(tpnme.spliceType)
725-
726-
@tu lazy val QuotedTypeModule: Symbol = QuotedTypeClass.companionModule
727-
@tu lazy val QuotedTypeModule_apply: Symbol = QuotedTypeModule.requiredMethod("apply")
728-
729731
@tu lazy val TastyReflectionClass: ClassSymbol = requiredClass("scala.tasty.Reflection")
730732

731733
@tu lazy val Unpickler_unpickleExpr: Symbol = requiredMethod("scala.internal.quoted.Unpickler.unpickleExpr")

compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ object PickledQuotes {
4444
}
4545

4646
/** Transform the expression into its fully spliced TypeTree */
47-
def quotedTypeToTree(tpe: quoted.Type[?])(using Context): Tree = {
47+
def quotedTypeToTree(tpe: quoted.QuotedType)(using Context): Tree = {
4848
val tpe1 = tpe.asInstanceOf[scala.internal.quoted.Type[Tree]]
4949
QuoteContext.checkScopeId(tpe1.scopeId)
5050
healOwner(tpe1.typeTree)
@@ -91,7 +91,7 @@ object PickledQuotes {
9191
else
9292
// Replaces type holes generated by ReifyQuotes (non-spliced types).
9393
// These are types defined in a quote and used at the same level in a nested quote.
94-
val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.Type[?]](reifiedArgs)
94+
val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.QuotedType](reifiedArgs)
9595
PickledQuotes.quotedTypeToTree(quotedType)
9696
case tree: Select =>
9797
// Retain selected members
@@ -134,7 +134,7 @@ object PickledQuotes {
134134
assert(tdef.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot))
135135
val tree = tdef.rhs match
136136
case TypeBoundsTree(_, Hole(_, idx, args), _) =>
137-
val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.Type[?]](args)
137+
val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.QuotedType](args)
138138
PickledQuotes.quotedTypeToTree(quotedType)
139139
case TypeBoundsTree(_, tpt, _) =>
140140
tpt

compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
222222
* Emits and error if `T` cannot be healed and returns `T`.
223223
*/
224224
protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SourcePosition)(using Context): TypeRef = {
225-
val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp)
225+
val reqType = defn.QuotedTypeAliasClass.typeRef.appliedTo(tp)
226226
val tag = ctx.typer.inferImplicitArg(reqType, pos.span)
227227
tag.tpe match
228228

compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ class ReifyQuotes extends MacroTransform {
265265
}
266266
assert(tpw.isInstanceOf[ValueType])
267267
val argTpe =
268-
if (tree.isType) defn.QuotedTypeClass.typeRef.appliedTo(tpw)
268+
if (tree.isType) defn.QuotedTypeAliasClass.typeRef.appliedTo(tpw)
269269
else defn.FunctionType(1, isContextual = true).appliedTo(defn.QuoteContextClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpw))
270270
val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).cast(argTpe)
271271
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.symbol.name.toTermName).toTermName, selectArg)
@@ -335,7 +335,9 @@ class ReifyQuotes extends MacroTransform {
335335
def getTypeHoleType(using Context) = new TypeMap() {
336336
override def apply(tp: Type): Type = tp match
337337
case tp: TypeRef if tp.typeSymbol.isTypeSplice =>
338-
apply(tp.dealias)
338+
val dealiased = tp.dealias
339+
if tp == dealiased then apply(tp.symbol.info.hiBound)
340+
else apply(tp.dealias)
339341
case tp @ TypeRef(pre, _) if pre == NoPrefix || pre.termSymbol.isLocal =>
340342
val hiBound = tp.typeSymbol.info match
341343
case info @ ClassInfo(_, _, classParents, _, _) => classParents.reduce(_ & _)

compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ trait QuotesAndSplices {
167167
case _ => TypeBounds.empty
168168
val typeSym = newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span)
169169
typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuotedMatcher_patternTypeAnnot.typeRef)).withSpan(tree.expr.span)))
170-
val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
170+
val pat = typedPattern(tree.expr, defn.QuotedTypeAliasClass.typeRef.appliedTo(typeSym.typeRef))(
171171
using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
172172
pat.select(tpnme.spliceType)
173173
else
@@ -298,7 +298,7 @@ trait QuotesAndSplices {
298298
if (variance == -1)
299299
tdef.symbol.addAnnotation(Annotation(New(ref(defn.InternalQuotedMatcher_fromAboveAnnot.typeRef)).withSpan(tdef.span)))
300300
val bindingType = getBinding(tdef.symbol).symbol.typeRef
301-
val bindingTypeTpe = AppliedType(defn.QuotedTypeClass.typeRef, bindingType :: Nil)
301+
val bindingTypeTpe = AppliedType(defn.QuotedTypeAliasClass.typeRef, bindingType :: Nil)
302302
val bindName = tdef.name.toString.stripPrefix("$").toTermName
303303
val sym = newPatternBoundSymbol(bindName, bindingTypeTpe, tdef.span, flags = ImplicitTerm)(using ctx0)
304304
buff += Bind(sym, untpd.Ident(nme.WILDCARD).withType(bindingTypeTpe)).withSpan(tdef.span)
@@ -436,7 +436,7 @@ trait QuotesAndSplices {
436436
else typed(untpd.Tuple(splices.map(x => untpd.TypedSplice(replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType)
437437

438438
val unapplySym = if (tree.quoted.isTerm) defn.InternalQuotedExpr_unapply else defn.InternalQuotedType_unapply
439-
val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass
439+
val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeAliasClass
440440
val quotedPattern =
441441
if (tree.quoted.isTerm) ref(defn.InternalQuoted_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx)
442442
else ref(defn.QuotedTypeModule_apply.termRef).appliedToTypeTree(shape).select(nme.apply).appliedTo(qctx)

library/src-bootstrapped/scala/quoted/Liftable.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,12 @@ object Liftable {
130130
'{ Set(${Expr(set.toSeq)}: _*) }
131131
}
132132

133-
given [T: Type: Liftable, U: Type: Liftable] as Liftable[Map[T, U]] = new Liftable[Map[T, U]] {
133+
given [T: Type : Liftable, U: Type: Liftable] as Liftable[Map[T, U]] = new Liftable[Map[T, U]] {
134134
def toExpr(map: Map[T, U]): QuoteContext ?=> Expr[Map[T, U]] =
135135
'{ Map(${Expr(map.toSeq)}: _*) }
136136
}
137137

138-
given [T: Type: Liftable] as Liftable[Option[T]] = new Liftable[Option[T]] {
138+
given [T: Type : Liftable] as Liftable[Option[T]] = new Liftable[Option[T]] {
139139
def toExpr(x: Option[T]): QuoteContext ?=> Expr[Option[T]] = x match {
140140
case x: Some[T] => Expr(x)
141141
case None => Expr(None)
@@ -173,7 +173,7 @@ object Liftable {
173173
'{ EmptyTuple }
174174
}
175175

176-
given [T1: Type: Liftable] as Liftable[Tuple1[T1]] = new {
176+
given [T1: Type : Liftable] as Liftable[Tuple1[T1]] = new {
177177
def toExpr(tup: Tuple1[T1]) =
178178
'{ Tuple1(${Expr(tup._1)}) }
179179
}

library/src-bootstrapped/scala/quoted/Type.scala renamed to library/src-bootstrapped/scala/quoted/QuotedType.scala

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package scala.quoted
33
import scala.annotation.compileTimeOnly
44
import scala.quoted.show.SyntaxHighlight
55

6-
/** Quoted type (or kind) `T` */
7-
abstract class Type[X <: AnyKind] private[scala] {
8-
type T = X
6+
/** Quoted type (or kind) `X` */
7+
type Type[X <: AnyKind] = QuotedType { type T = X }
8+
9+
/** A TypeTree with a known type (or kind) `T` */
10+
abstract class QuotedType private[scala] {
11+
type T <: AnyKind
912

1013
/** Show a source code like representation of this type without syntax highlight */
1114
def show(using qctx: QuoteContext): String =
@@ -21,12 +24,17 @@ abstract class Type[X <: AnyKind] private[scala] {
2124
}
2225

2326
/** Some basic type tags, currently incomplete */
24-
object Type {
27+
object QuotedType {
2528

2629
/** Return a quoted.Type with the given type */
27-
@compileTimeOnly("Reference to `scala.quoted.Type.apply` was not handled by ReifyQuotes")
30+
@compileTimeOnly("Reference to `scala.quoted.QuotedType.apply` was not handled by ReifyQuotes")
2831
given apply[T <: AnyKind] as (QuoteContext ?=> Type[T]) = ???
2932

33+
34+
// TODO: Move Tags
35+
// these are here for a compiler optimization not for the user. users should write '[Int] directly
36+
// they should be in sclaa.internal.quoted.Type
37+
3038
def UnitTag: QuoteContext ?=> Type[Unit] =
3139
qctx.tasty.defn.UnitType.seal.asInstanceOf[quoted.Type[Unit]]
3240

library/src-bootstrapped/scala/quoted/util/ExprMap.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,8 @@ trait ExprMap {
6565
// TODO improve code
6666
case AppliedType(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "<repeated>"), List(tp0: Type)) =>
6767
// TODO rewrite without using quotes
68-
type T
69-
val qtp: quoted.Type[T] = tp0.seal.asInstanceOf[quoted.Type[T]]
70-
given qtp.type = qtp
71-
'[Seq[T]].unseal.tpe
68+
val t = tp0.seal.asInstanceOf[quoted.QuotedType { type T <: Any }]
69+
'[Seq[$t]].unseal.tpe
7270
case tp => tp
7371
Typed.copy(tree)(transformTerm(expr, tp), transformTypeTree(tpt))
7472
case tree: NamedArg =>
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package scala
2+
3+
import scala.quoted._
4+
5+
package object compiletime {
6+
7+
/** Use this method when you have a type, do not have a value for it but want to
8+
* pattern match on it. For example, given a type `Tup <: Tuple`, one can
9+
* pattern-match on it as follows:
10+
* ```
11+
* erasedValue[Tup] match {
12+
* case _: EmptyTuple => ...
13+
* case _: h *: t => ...
14+
* }
15+
* ```
16+
*/
17+
erased def erasedValue[T]: T = ???
18+
19+
/** The error method is used to produce user-defined compile errors during inline expansion.
20+
* If an inline expansion results in a call error(msgStr) the compiler produces an error message containing the given msgStr.
21+
*
22+
* ```scala
23+
* error("My error message")
24+
* ```
25+
* or
26+
* ```scala
27+
* error(code"My error of this code: ${println("foo")}")
28+
* ```
29+
*/
30+
inline def error(inline msg: String): Nothing = ???
31+
32+
extension (inline self: StringContext):
33+
/** Returns the string representation of interpolated elaborated code:
34+
*
35+
* ```scala
36+
* inline def logged(p1: => Any) = {
37+
* val c = code"code: $p1"
38+
* val res = p1
39+
* (c, p1)
40+
* }
41+
* logged(identity("foo"))
42+
* // above is equivalent to:
43+
* // ("code: scala.Predef.identity("foo")", identity("foo"))
44+
* ```
45+
*
46+
* @note only by-name arguments will be displayed as "code".
47+
* Other values may display unintutively.
48+
*/
49+
transparent inline def code (inline args: Any*): String =
50+
throw new Error("Non-bootstrapped lib")
51+
end extension
52+
53+
/** Same as `constValue` but returns a `None` if a constant value
54+
* cannot be constructed from the provided type. Otherwise returns
55+
* that value wrapped in `Some`.
56+
*/
57+
inline def constValueOpt[T]: Option[T] = ???
58+
59+
/** Given a constant, singleton type `T`, convert it to a value
60+
* of the same singleton type. For example: `assert(constValue[1] == 1)`.
61+
*/
62+
inline def constValue[T]: T = ???
63+
64+
/** Given a tuple type `(X1, ..., Xn)`, returns a tuple value
65+
* `(constValue[X1], ..., constValue[Xn])`.
66+
*/
67+
inline def constValueTuple[T <: Tuple]: Tuple.Widen[T]=
68+
val res =
69+
inline erasedValue[T] match
70+
case _: EmptyTuple => EmptyTuple
71+
case _: (t *: ts) => constValue[t] *: constValueTuple[ts]
72+
end match
73+
res.asInstanceOf[Tuple.Widen[T]]
74+
end constValueTuple
75+
76+
/** Summons first given matching one of the listed cases. E.g. in
77+
*
78+
* given B { ... }
79+
*
80+
* summonFrom {
81+
* case given A => 1
82+
* case given B => 2
83+
* case given C => 3
84+
* case _ => 4
85+
* }
86+
*
87+
* the returned value would be `2`.
88+
*/
89+
transparent inline def summonFrom[T](f: Nothing => T): T = ???
90+
91+
92+
/** Summon a given value of type `T`. Usually, the argument is not passed explicitly.
93+
* The summoning is delayed until the call has been fully inlined.
94+
*
95+
* @tparam T the type of the value to be summoned
96+
* @return the given value typed as the provided type parameter
97+
*/
98+
transparent inline def summonInline[T]: T = summonFrom {
99+
case t: T => t
100+
}
101+
102+
/** Given a tuple T, summons each of its member types and returns them in
103+
* a Tuple.
104+
*
105+
* @tparam T the tuple containing the types of the values to be summoned
106+
* @return the given values typed as elements of the tuple
107+
*/
108+
inline def summonAll[T <: Tuple]: Tuple.Widen[T] =
109+
val res =
110+
inline erasedValue[T] match
111+
case _: EmptyTuple => EmptyTuple
112+
case _: (t *: ts) => summonInline[t] *: summonAll[ts]
113+
end match
114+
res.asInstanceOf[Tuple.Widen[T]]
115+
end summonAll
116+
117+
/** Succesor of a natural number where zero is the type 0 and successors are reduced as if the definition was
118+
*
119+
* type S[N <: Int] <: Int = N match {
120+
* case 0 => 1
121+
* case 1 => 2
122+
* case 2 => 3
123+
* ...
124+
* case 2147483646 => 2147483647
125+
* }
126+
*/
127+
type S[N <: Int] <: Int
128+
129+
/** Assertion that an argument is by-name. Used for nullability checking. */
130+
def byName[T](x: => T): T = x
131+
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package scala.quoted
22

3-
abstract class Type[T <: AnyKind] private[scala]:
4-
type `$splice` = T
3+
abstract class QuotedType private[scala]:
4+
type `$splice`
5+
type T <: AnyKind
56
def unseal(using qctx: QuoteContext): qctx.tasty.TypeTree
7+
8+
type Type[X <: AnyKind] = QuotedType { type T = X }

library/src/scala/internal/quoted/Type.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package scala.internal.quoted
33
import scala.quoted._
44

55
/** Quoted type (or kind) `T` backed by a tree */
6-
final class Type[Tree](val typeTree: Tree, val scopeId: Int) extends scala.quoted.Type[Any] {
6+
final class Type[Tree](val typeTree: Tree, val scopeId: Int) extends scala.quoted.QuotedType {
77
override def equals(that: Any): Boolean = that match {
88
case that: Type[_] => typeTree ==
99
// TastyTreeExpr are wrappers around trees, therfore they are equals if their trees are equal.
@@ -31,13 +31,13 @@ object Type {
3131
* - scala.internal.Quoted.patternHole[T]: hole that matches an expression `x` of type `Type[U]`
3232
* if `U <:< T` and returns `x` as part of the match.
3333
*
34-
* @param scrutineeType `Type[_]` on which we are pattern matching
35-
* @param patternType `Type[_]` containing the pattern tree
34+
* @param scrutineeType `QuotedType` on which we are pattern matching
35+
* @param patternType `QuotedType` containing the pattern tree
3636
* @param hasTypeSplices `Boolean` notify if the pattern has type splices (if so we use a GADT context)
3737
* @param qctx the current QuoteContext
3838
* @return None if it did not match, `Some(tup)` if it matched where `tup` contains `Type[Ti]``
3939
*/
40-
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutineeType: scala.quoted.Type[_])(using patternType: scala.quoted.Type[_],
40+
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutineeType: scala.quoted.QuotedType)(using patternType: scala.quoted.QuotedType,
4141
hasTypeSplices: Boolean, qctx: QuoteContext): Option[Tup] = {
4242
new Matcher.QuoteMatcher[qctx.type].typeTreeMatch(scrutineeType.unseal, patternType.unseal, hasTypeSplices).asInstanceOf[Option[Tup]]
4343
}

library/src/scala/tasty/Reflection.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,7 +1753,7 @@ class Reflection(private[scala] val internal: CompilerInterface) { self =>
17531753

17541754
/** Returns the type (Type) of T */
17551755
def typeOf[T](using qtype: scala.quoted.Type[T], ctx: Context): Type =
1756-
qtype.asInstanceOf[scala.internal.quoted.Type[T]].typeTree.asInstanceOf[TypeTree].tpe
1756+
qtype.asInstanceOf[scala.internal.quoted.Type[TypeTree]].typeTree.tpe
17571757

17581758
given TypeOrBoundsOps as AnyRef:
17591759
/** Members of `TypeOrBounds` */
@@ -1783,9 +1783,9 @@ class Reflection(private[scala] val internal: CompilerInterface) { self =>
17831783

17841784
extension (self: Type):
17851785

1786-
/** Convert `Type` to an `quoted.Type[_]` */
1787-
def seal(using ctx: Context): scala.quoted.Type[_] =
1788-
new scala.internal.quoted.Type(Inferred(self), internal.compilerId)
1786+
/** Wraps the `Type` in a `TypeTree` and convert it to a `quoted.QuotedType` */
1787+
def seal(using ctx: Context): scala.quoted.QuotedType =
1788+
new scala.internal.quoted.Type(Inferred(self), internal.compilerId).asInstanceOf[scala.quoted.QuotedType]
17891789

17901790
/** Is `self` type the same as `that` type?
17911791
* This is the case iff `self <:< that` and `that <:< self`.

staging/test-resources/repl-staging/i6263

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ scala> import quoted.staging._
33
scala> implicit def toolbox: Toolbox = Toolbox.make(getClass.getClassLoader)
44
def toolbox: quoted.staging.Toolbox
55
scala> def fn[T : Type](v : T) = println("ok")
6-
def fn[T](v: T)(implicit evidence$1: quoted.Type[T]): Unit
6+
def fn[T](v: T)(implicit evidence$1: scala.quoted.Type[T]): Unit
77
scala> withQuoteContext { fn("foo") }
88
ok
99
scala> withQuoteContext { fn((1,2)) }

0 commit comments

Comments
 (0)