@@ -13,6 +13,7 @@ import tasty.TreePickler.Hole
1313import MegaPhase .MiniPhase
1414import SymUtils ._
1515import NameKinds .OuterSelectName
16+ import typer .Implicits .SearchFailureType
1617
1718import scala .collection .mutable
1819import dotty .tools .dotc .core .StdNames ._
@@ -22,7 +23,7 @@ import dotty.tools.dotc.core.quoted._
2223/** Translates quoted terms and types to `unpickle` method calls.
2324 * Checks that the phase consistency principle (PCP) holds.
2425 */
25- class ReifyQuotes extends MacroTransform {
26+ class ReifyQuotes extends MacroTransformWithImplicits {
2627 import ast .tpd ._
2728
2829 override def phaseName : String = " reifyQuotes"
@@ -101,7 +102,7 @@ class ReifyQuotes extends MacroTransform {
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 MacroTransform {
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 MacroTransform {
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 MacroTransform {
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 MacroTransform {
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. */
@@ -209,7 +228,7 @@ class ReifyQuotes extends MacroTransform {
209228 case tp : NamedType if tp.symbol.isSplice =>
210229 if (inQuote) outer.checkType(pos).foldOver(acc, tp)
211230 else {
212- spliceOutsideQuotes(pos)
231+ if (tp.isTerm) spliceOutsideQuotes(pos)
213232 tp
214233 }
215234 case tp : NamedType =>
0 commit comments