Skip to content

Commit de2d33c

Browse files
committed
Lift only implicitly given Types
We need to come back to the earlier scheme that a lifted type needs to refer to an implicit type tag.
1 parent 5125c8e commit de2d33c

File tree

4 files changed

+47
-23
lines changed

4 files changed

+47
-23
lines changed

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

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import tasty.TreePickler.Hole
1313
import MegaPhase.MiniPhase
1414
import SymUtils._
1515
import NameKinds.OuterSelectName
16+
import typer.Implicits.SearchFailureType
1617

1718
import scala.collection.mutable
1819
import dotty.tools.dotc.core.StdNames._
@@ -101,7 +102,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
101102
* @param level the current level, where quotes add one and splices subtract one level
102103
* @param levels a stacked map from symbols to the levels in which they were defined
103104
*/
104-
private class Reifier(inQuote: Boolean, val outer: Reifier, val level: Int, levels: LevelInfo) extends Transformer {
105+
private class Reifier(inQuote: Boolean, val outer: Reifier, val level: Int, levels: LevelInfo) extends ImplicitsTransformer {
105106
import levels._
106107

107108
/** A nested reifier for a quote (if `isQuote = true`) or a splice (if not) */
@@ -114,29 +115,28 @@ class ReifyQuotes extends MacroTransformWithImplicits {
114115
/** A list of embedded quotes (if `inSplice = true`) or splices (if `inQuote = true`) */
115116
val embedded = new mutable.ListBuffer[Tree]
116117

117-
/** A map from type ref T to "expression of type `quoted.Type[T]`".
118+
/** A map from type ref T to expressions of type `quoted.Type[T]`".
118119
* These will be turned into splices using `addTags`
119120
*/
120-
val importedTypes = new mutable.LinkedHashSet[TypeRef]()
121+
val importedTags = new mutable.LinkedHashMap[TypeRef, Tree]()
121122

122-
/** Assuming typeTagOfRef = `Type1 -> tag1, ..., TypeN -> tagN`, the expression
123+
/** Assuming importedTags = `Type1 -> tag1, ..., TypeN -> tagN`, the expression
123124
*
124125
* { type <Type1> = <tag1>.unary_~
125126
* ...
126127
* type <TypeN> = <tagN>.unary.~
127128
* <expr>
128129
* }
129130
*
130-
* where all references to `TypeI` in `expr` are rewired to point to the locally
131+
* references to `TypeI` in `expr` are rewired to point to the locally
131132
* defined versions. As a side effect, prepend the expressions `tag1, ..., `tagN`
132-
* as splices to `buf`.
133+
* as splices to `embedded`.
133134
*/
134135
def addTags(expr: Tree)(implicit ctx: Context): Tree =
135-
if (importedTypes.isEmpty) expr
136+
if (importedTags.isEmpty) expr
136137
else {
137-
val trefs = importedTypes.toList
138-
val typeDefs = for (tref <- trefs) yield {
139-
val tag = New(defn.QuotedTypeType.appliedTo(tref), Nil) // FIXME: should be an implicitly inferred defn.QuotedTypeType.appliedTo(tref)
138+
val itags = importedTags.toList
139+
val typeDefs = for ((tref, tag) <- itags) yield {
140140
val rhs = transform(tag.select(tpnme.UNARY_~))
141141
val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs)
142142
val original = tref.symbol.asType
@@ -146,9 +146,9 @@ class ReifyQuotes extends MacroTransformWithImplicits {
146146
info = TypeAlias(tag.tpe.select(tpnme.UNARY_~)))
147147
ctx.typeAssigner.assignType(untpd.TypeDef(original.name, alias), local)
148148
}
149-
importedTypes.clear()
149+
importedTags.clear()
150150
Block(typeDefs,
151-
new SubstMap(substFrom = trefs.map(_.symbol), substTo = typeDefs.map(_.symbol))
151+
new SubstMap(substFrom = itags.map(_._1.symbol), substTo = typeDefs.map(_.symbol))
152152
.apply(expr))
153153
}
154154

@@ -180,6 +180,29 @@ class ReifyQuotes extends MacroTransformWithImplicits {
180180
def spliceOutsideQuotes(pos: Position)(implicit ctx: Context) =
181181
ctx.error(i"splice outside quotes", pos)
182182

183+
/** Try to heal phase-inconsistent reference to type `T` using a local type definition.
184+
* @return None if successful
185+
* @return Some(msg) if unsuccessful where `msg` is a potentially empty error message
186+
* to be added to the "inconsistent phase" message.
187+
*/
188+
def tryHeal(tp: Type, pos: Position)(implicit ctx: Context): Option[String] = tp match {
189+
case tp: TypeRef =>
190+
val reqType = defn.QuotedTypeType.appliedTo(tp)
191+
val tag = ctx.typer.inferImplicitArg(reqType, pos)
192+
tag.tpe match {
193+
case fail: SearchFailureType =>
194+
Some(i"""
195+
|
196+
| The access would be accepted with the right type tag, but
197+
| ${ctx.typer.missingArgMsg(tag, reqType, "")}""")
198+
case _ =>
199+
importedTags(tp) = nested(isQuote = false).transform(tag)
200+
None
201+
}
202+
case _ =>
203+
Some("")
204+
}
205+
183206
/** Check reference to `sym` for phase consistency, where `tp` is the underlying type
184207
* by which we refer to `sym`.
185208
*/
@@ -192,14 +215,10 @@ class ReifyQuotes extends MacroTransformWithImplicits {
192215
if (!isThis && sym.maybeOwner.isType)
193216
check(sym.owner, sym.owner.thisType, pos)
194217
else if (sym.exists && !sym.isStaticOwner && !levelOK(sym))
195-
tp match {
196-
case tp: TypeRef =>
197-
importedTypes += tp
198-
case _ =>
199-
ctx.error(em"""access to $symStr from wrong staging level:
200-
| - the definition is at level ${levelOf(sym)},
201-
| - but the access is at level $level.""", pos)
202-
}
218+
for (errMsg <- tryHeal(tp, pos))
219+
ctx.error(em"""access to $symStr from wrong staging level:
220+
| - the definition is at level ${levelOf(sym)},
221+
| - but the access is at level $level.$errMsg""", pos)
203222
}
204223

205224
/** Check all named types and this-types in a given type for phase consistency. */

tests/neg/quote-0.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ class Test {
1515
'((y: Expr[Int]) => ~y ) // error: wrong staging level
1616

1717
def f[T](t: Type[T], x: Expr[T]) = '{
18-
val z2 = ~x // OK
18+
val z2 = ~x // error: wrong staging level
19+
}
20+
21+
def g[T](implicit t: Type[T], x: Expr[T]) = '{
22+
val z2 = ~x // ok
1923
}
2024

2125
}

tests/pos/quote-1.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import scala.quoted._
22

33
object Test {
44

5-
def f[T](x: Expr[T])(t: Type[T]) = '{
5+
def f[T](x: Expr[T])(implicit t: Type[T]) = '{
66
val y: t.unary_~ = x.unary_~
77
val z = ~x
88
}
@@ -13,3 +13,4 @@ object Test {
1313
def g(es: Expr[String], t: Type[String]) =
1414
f('{ (~es + "!") :: Nil })('[List[~t]])
1515
}
16+

tests/pos/quote-liftable.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object Test {
1717
if (b) '(true) else '(false)
1818
}
1919

20-
implicit def ListIsLiftable[T: Liftable]: Liftable[List[T]] = new {
20+
implicit def ListIsLiftable[T: Liftable: Type]: Liftable[List[T]] = new {
2121
def toExpr(xs: List[T]): Expr[List[T]] = xs match {
2222
case x :: xs1 => '{ ~implicitly[Liftable[T]].toExpr(x) :: ~toExpr(xs1) }
2323
case Nil => '(Nil: List[T])

0 commit comments

Comments
 (0)