Skip to content

Decouple quoted Type interface from encoding V2 #9535

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
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion community-build/community-projects/utest
14 changes: 8 additions & 6 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,14 @@ class Definitions {
@tu lazy val QuotedExprModule_nullExpr: Symbol = QuotedExprModule.requiredMethod(nme.nullExpr)
@tu lazy val QuotedExprModule_unitExpr: Symbol = QuotedExprModule.requiredMethod(nme.unitExpr)

@tu lazy val QuotedTypeAliasClass: Symbol = requiredPackage("scala.quoted").typeRef.select("Type".toTypeName).typeSymbol

@tu lazy val QuotedTypeClass: ClassSymbol = requiredClass("scala.quoted.QuotedType")
@tu lazy val QuotedType_splice: Symbol = QuotedTypeClass.requiredType(tpnme.spliceType)

@tu lazy val QuotedTypeModule: Symbol = QuotedTypeClass.companionModule
@tu lazy val QuotedTypeModule_apply: Symbol = QuotedTypeModule.requiredMethod("apply")

@tu lazy val QuoteContextClass: ClassSymbol = requiredClass("scala.quoted.QuoteContext")

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

@tu lazy val QuotedTypeClass: ClassSymbol = requiredClass("scala.quoted.Type")
@tu lazy val QuotedType_splice: Symbol = QuotedTypeClass.requiredType(tpnme.spliceType)

@tu lazy val QuotedTypeModule: Symbol = QuotedTypeClass.companionModule
@tu lazy val QuotedTypeModule_apply: Symbol = QuotedTypeModule.requiredMethod("apply")

@tu lazy val TastyReflectionClass: ClassSymbol = requiredClass("scala.tasty.Reflection")

@tu lazy val Unpickler_unpickleExpr: Symbol = requiredMethod("scala.internal.quoted.Unpickler.unpickleExpr")
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ object PickledQuotes {
}

/** Transform the expression into its fully spliced TypeTree */
def quotedTypeToTree(tpe: quoted.Type[?])(using Context): Tree = {
def quotedTypeToTree(tpe: quoted.QuotedType)(using Context): Tree = {
val tpe1 = tpe.asInstanceOf[scala.internal.quoted.Type[Tree]]
QuoteContext.checkScopeId(tpe1.scopeId)
healOwner(tpe1.typeTree)
Expand Down Expand Up @@ -91,7 +91,7 @@ object PickledQuotes {
else
// Replaces type holes generated by ReifyQuotes (non-spliced types).
// These are types defined in a quote and used at the same level in a nested quote.
val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.Type[?]](reifiedArgs)
val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.QuotedType](reifiedArgs)
PickledQuotes.quotedTypeToTree(quotedType)
case tree: Select =>
// Retain selected members
Expand Down Expand Up @@ -134,7 +134,7 @@ object PickledQuotes {
assert(tdef.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot))
val tree = tdef.rhs match
case TypeBoundsTree(_, Hole(_, idx, args), _) =>
val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.Type[?]](args)
val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.QuotedType](args)
PickledQuotes.quotedTypeToTree(quotedType)
case TypeBoundsTree(_, tpt, _) =>
tpt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
* Emits and error if `T` cannot be healed and returns `T`.
*/
protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SourcePosition)(using Context): TypeRef = {
val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp)
val reqType = defn.QuotedTypeAliasClass.typeRef.appliedTo(tp)
val tag = ctx.typer.inferImplicitArg(reqType, pos.span)
tag.tpe match

Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ class ReifyQuotes extends MacroTransform {
}
assert(tpw.isInstanceOf[ValueType])
val argTpe =
if (tree.isType) defn.QuotedTypeClass.typeRef.appliedTo(tpw)
if (tree.isType) defn.QuotedTypeAliasClass.typeRef.appliedTo(tpw)
else defn.FunctionType(1, isContextual = true).appliedTo(defn.QuoteContextClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpw))
val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).cast(argTpe)
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.symbol.name.toTermName).toTermName, selectArg)
Expand Down Expand Up @@ -335,7 +335,9 @@ class ReifyQuotes extends MacroTransform {
def getTypeHoleType(using Context) = new TypeMap() {
override def apply(tp: Type): Type = tp match
case tp: TypeRef if tp.typeSymbol.isTypeSplice =>
apply(tp.dealias)
val dealiased = tp.dealias
if tp == dealiased then apply(tp.symbol.info.hiBound)
else apply(tp.dealias)
case tp @ TypeRef(pre, _) if pre == NoPrefix || pre.termSymbol.isLocal =>
val hiBound = tp.typeSymbol.info match
case info @ ClassInfo(_, _, classParents, _, _) => classParents.reduce(_ & _)
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ trait QuotesAndSplices {
case _ => TypeBounds.empty
val typeSym = newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span)
typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuotedMatcher_patternTypeAnnot.typeRef)).withSpan(tree.expr.span)))
val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
val pat = typedPattern(tree.expr, defn.QuotedTypeAliasClass.typeRef.appliedTo(typeSym.typeRef))(
using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
pat.select(tpnme.spliceType)
else
Expand Down Expand Up @@ -298,7 +298,7 @@ trait QuotesAndSplices {
if (variance == -1)
tdef.symbol.addAnnotation(Annotation(New(ref(defn.InternalQuotedMatcher_fromAboveAnnot.typeRef)).withSpan(tdef.span)))
val bindingType = getBinding(tdef.symbol).symbol.typeRef
val bindingTypeTpe = AppliedType(defn.QuotedTypeClass.typeRef, bindingType :: Nil)
val bindingTypeTpe = AppliedType(defn.QuotedTypeAliasClass.typeRef, bindingType :: Nil)
val bindName = tdef.name.toString.stripPrefix("$").toTermName
val sym = newPatternBoundSymbol(bindName, bindingTypeTpe, tdef.span, flags = ImplicitTerm)(using ctx0)
buff += Bind(sym, untpd.Ident(nme.WILDCARD).withType(bindingTypeTpe)).withSpan(tdef.span)
Expand Down Expand Up @@ -436,7 +436,7 @@ trait QuotesAndSplices {
else typed(untpd.Tuple(splices.map(x => untpd.TypedSplice(replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType)

val unapplySym = if (tree.quoted.isTerm) defn.InternalQuotedExpr_unapply else defn.InternalQuotedType_unapply
val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass
val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeAliasClass
val quotedPattern =
if (tree.quoted.isTerm) ref(defn.InternalQuoted_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx)
else ref(defn.QuotedTypeModule_apply.termRef).appliedToTypeTree(shape).select(nme.apply).appliedTo(qctx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package scala.quoted
import scala.annotation.compileTimeOnly
import scala.quoted.show.SyntaxHighlight

/** Quoted type (or kind) `T` */
abstract class Type[X <: AnyKind] private[scala] {
type T = X
/** Quoted type (or kind) `X` */
type Type[X <: AnyKind] = QuotedType { type T = X }

/** A TypeTree with a known type (or kind) `T` */
abstract class QuotedType private[scala] {
type T <: AnyKind

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

/** Some basic type tags, currently incomplete */
object Type {
object QuotedType {

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


// TODO: Move Tags
// these are here for a compiler optimization not for the user. users should write '[Int] directly
// they should be in sclaa.internal.quoted.Type

def UnitTag: QuoteContext ?=> Type[Unit] =
qctx.tasty.defn.UnitType.seal.asInstanceOf[quoted.Type[Unit]]

Expand Down
6 changes: 2 additions & 4 deletions library/src-bootstrapped/scala/quoted/util/ExprMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,8 @@ trait ExprMap {
// TODO improve code
case AppliedType(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "<repeated>"), List(tp0: Type)) =>
// TODO rewrite without using quotes
type T
val qtp: quoted.Type[T] = tp0.seal.asInstanceOf[quoted.Type[T]]
given qtp.type = qtp
'[Seq[T]].unseal.tpe
val t = tp0.seal.asInstanceOf[quoted.QuotedType { type T <: Any }]
'[Seq[$t]].unseal.tpe
case tp => tp
Typed.copy(tree)(transformTerm(expr, tp), transformTypeTree(tpt))
case tree: NamedArg =>
Expand Down
131 changes: 131 additions & 0 deletions library/src-non-bootstrapped/scala/compiletime/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package scala

import scala.quoted._

package object compiletime {

/** Use this method when you have a type, do not have a value for it but want to
* pattern match on it. For example, given a type `Tup <: Tuple`, one can
* pattern-match on it as follows:
* ```
* erasedValue[Tup] match {
* case _: EmptyTuple => ...
* case _: h *: t => ...
* }
* ```
*/
erased def erasedValue[T]: T = ???

/** The error method is used to produce user-defined compile errors during inline expansion.
* If an inline expansion results in a call error(msgStr) the compiler produces an error message containing the given msgStr.
*
* ```scala
* error("My error message")
* ```
* or
* ```scala
* error(code"My error of this code: ${println("foo")}")
* ```
*/
inline def error(inline msg: String): Nothing = ???

extension (inline self: StringContext):
/** Returns the string representation of interpolated elaborated code:
*
* ```scala
* inline def logged(p1: => Any) = {
* val c = code"code: $p1"
* val res = p1
* (c, p1)
* }
* logged(identity("foo"))
* // above is equivalent to:
* // ("code: scala.Predef.identity("foo")", identity("foo"))
* ```
*
* @note only by-name arguments will be displayed as "code".
* Other values may display unintutively.
*/
transparent inline def code (inline args: Any*): String =
throw new Error("Non-bootstrapped lib")
end extension

/** Same as `constValue` but returns a `None` if a constant value
* cannot be constructed from the provided type. Otherwise returns
* that value wrapped in `Some`.
*/
inline def constValueOpt[T]: Option[T] = ???

/** Given a constant, singleton type `T`, convert it to a value
* of the same singleton type. For example: `assert(constValue[1] == 1)`.
*/
inline def constValue[T]: T = ???

/** Given a tuple type `(X1, ..., Xn)`, returns a tuple value
* `(constValue[X1], ..., constValue[Xn])`.
*/
inline def constValueTuple[T <: Tuple]: Tuple.Widen[T]=
val res =
inline erasedValue[T] match
case _: EmptyTuple => EmptyTuple
case _: (t *: ts) => constValue[t] *: constValueTuple[ts]
end match
res.asInstanceOf[Tuple.Widen[T]]
end constValueTuple

/** Summons first given matching one of the listed cases. E.g. in
*
* given B { ... }
*
* summonFrom {
* case given A => 1
* case given B => 2
* case given C => 3
* case _ => 4
* }
*
* the returned value would be `2`.
*/
transparent inline def summonFrom[T](f: Nothing => T): T = ???


/** Summon a given value of type `T`. Usually, the argument is not passed explicitly.
* The summoning is delayed until the call has been fully inlined.
*
* @tparam T the type of the value to be summoned
* @return the given value typed as the provided type parameter
*/
transparent inline def summonInline[T]: T = summonFrom {
case t: T => t
}

/** Given a tuple T, summons each of its member types and returns them in
* a Tuple.
*
* @tparam T the tuple containing the types of the values to be summoned
* @return the given values typed as elements of the tuple
*/
inline def summonAll[T <: Tuple]: Tuple.Widen[T] =
val res =
inline erasedValue[T] match
case _: EmptyTuple => EmptyTuple
case _: (t *: ts) => summonInline[t] *: summonAll[ts]
end match
res.asInstanceOf[Tuple.Widen[T]]
end summonAll

/** Succesor of a natural number where zero is the type 0 and successors are reduced as if the definition was
*
* type S[N <: Int] <: Int = N match {
* case 0 => 1
* case 1 => 2
* case 2 => 3
* ...
* case 2147483646 => 2147483647
* }
*/
type S[N <: Int] <: Int

/** Assertion that an argument is by-name. Used for nullability checking. */
def byName[T](x: => T): T = x
}
7 changes: 5 additions & 2 deletions library/src-non-bootstrapped/scala/quoted/Type.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package scala.quoted

abstract class Type[T <: AnyKind] private[scala]:
type `$splice` = T
abstract class QuotedType private[scala]:
type `$splice`
type T <: AnyKind
def unseal(using qctx: QuoteContext): qctx.tasty.TypeTree

type Type[X <: AnyKind] = QuotedType { type T = X }
8 changes: 4 additions & 4 deletions library/src/scala/internal/quoted/Type.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package scala.internal.quoted
import scala.quoted._

/** Quoted type (or kind) `T` backed by a tree */
final class Type[Tree](val typeTree: Tree, val scopeId: Int) extends scala.quoted.Type[Any] {
final class Type[Tree](val typeTree: Tree, val scopeId: Int) extends scala.quoted.QuotedType {
override def equals(that: Any): Boolean = that match {
case that: Type[_] => typeTree ==
// TastyTreeExpr are wrappers around trees, therfore they are equals if their trees are equal.
Expand Down Expand Up @@ -31,13 +31,13 @@ object Type {
* - scala.internal.Quoted.patternHole[T]: hole that matches an expression `x` of type `Type[U]`
* if `U <:< T` and returns `x` as part of the match.
*
* @param scrutineeType `Type[_]` on which we are pattern matching
* @param patternType `Type[_]` containing the pattern tree
* @param scrutineeType `QuotedType` on which we are pattern matching
* @param patternType `QuotedType` containing the pattern tree
* @param hasTypeSplices `Boolean` notify if the pattern has type splices (if so we use a GADT context)
* @param qctx the current QuoteContext
* @return None if it did not match, `Some(tup)` if it matched where `tup` contains `Type[Ti]``
*/
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutineeType: scala.quoted.Type[_])(using patternType: scala.quoted.Type[_],
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutineeType: scala.quoted.QuotedType)(using patternType: scala.quoted.QuotedType,
hasTypeSplices: Boolean, qctx: QuoteContext): Option[Tup] = {
new Matcher.QuoteMatcher[qctx.type].typeTreeMatch(scrutineeType.unseal, patternType.unseal, hasTypeSplices).asInstanceOf[Option[Tup]]
}
Expand Down
8 changes: 4 additions & 4 deletions library/src/scala/tasty/Reflection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1753,7 +1753,7 @@ class Reflection(private[scala] val internal: CompilerInterface) { self =>

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

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

extension (self: Type):

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

/** Is `self` type the same as `that` type?
* This is the case iff `self <:< that` and `that <:< self`.
Expand Down
2 changes: 1 addition & 1 deletion staging/test-resources/repl-staging/i6263
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ scala> import quoted.staging._
scala> implicit def toolbox: Toolbox = Toolbox.make(getClass.getClassLoader)
def toolbox: quoted.staging.Toolbox
scala> def fn[T : Type](v : T) = println("ok")
def fn[T](v: T)(implicit evidence$1: quoted.Type[T]): Unit
def fn[T](v: T)(implicit evidence$1: scala.quoted.Type[T]): Unit
scala> withQuoteContext { fn("foo") }
ok
scala> withQuoteContext { fn((1,2)) }
Expand Down
9 changes: 7 additions & 2 deletions tests/neg-macros/i7919.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ object Test {
def staged[T](using qctx: QuoteContext) = {
import qctx.tasty._
given typeT as quoted.Type[T] // error
val tTypeTree = typeT.unseal
val typeT2: quoted.Type[Int] = ???
val tTypeTree = typeT2.unseal
val tt = typeOf[T]
'{ "in staged" }
}
Expand All @@ -13,8 +14,12 @@ object Test {
new Expr[Int] // error
class Expr2 extends Expr[Int] // error

given Type // error
new Type // error
class Type2 extends Type // error

given Type[Int] // error
new Type[Int] // error
class Type2 extends Type[Int] // error
class Type3 extends Type[Int] // error

}
Loading