@@ -13,6 +13,7 @@ import tasty.TreePickler.Hole
13
13
import MegaPhase .MiniPhase
14
14
import SymUtils ._
15
15
import NameKinds .OuterSelectName
16
+ import typer .Implicits .SearchFailureType
16
17
17
18
import scala .collection .mutable
18
19
import dotty .tools .dotc .core .StdNames ._
@@ -22,7 +23,7 @@ import dotty.tools.dotc.core.quoted._
22
23
/** Translates quoted terms and types to `unpickle` method calls.
23
24
* Checks that the phase consistency principle (PCP) holds.
24
25
*/
25
- class ReifyQuotes extends MacroTransform {
26
+ class ReifyQuotes extends MacroTransformWithImplicits {
26
27
import ast .tpd ._
27
28
28
29
override def phaseName : String = " reifyQuotes"
@@ -101,7 +102,7 @@ class ReifyQuotes extends MacroTransform {
101
102
* @param level the current level, where quotes add one and splices subtract one level
102
103
* @param levels a stacked map from symbols to the levels in which they were defined
103
104
*/
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 {
105
106
import levels ._
106
107
107
108
/** A nested reifier for a quote (if `isQuote = true`) or a splice (if not) */
@@ -114,29 +115,28 @@ class ReifyQuotes extends MacroTransform {
114
115
/** A list of embedded quotes (if `inSplice = true`) or splices (if `inQuote = true`) */
115
116
val embedded = new mutable.ListBuffer [Tree ]
116
117
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]`".
118
119
* These will be turned into splices using `addTags`
119
120
*/
120
- val importedTypes = new mutable.LinkedHashSet [TypeRef ]()
121
+ val importedTags = new mutable.LinkedHashMap [TypeRef , Tree ]()
121
122
122
- /** Assuming typeTagOfRef = `Type1 -> tag1, ..., TypeN -> tagN`, the expression
123
+ /** Assuming importedTags = `Type1 -> tag1, ..., TypeN -> tagN`, the expression
123
124
*
124
125
* { type <Type1> = <tag1>.unary_~
125
126
* ...
126
127
* type <TypeN> = <tagN>.unary.~
127
128
* <expr>
128
129
* }
129
130
*
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
131
132
* defined versions. As a side effect, prepend the expressions `tag1, ..., `tagN`
132
- * as splices to `buf `.
133
+ * as splices to `embedded `.
133
134
*/
134
135
def addTags (expr : Tree )(implicit ctx : Context ): Tree =
135
- if (importedTypes .isEmpty) expr
136
+ if (importedTags .isEmpty) expr
136
137
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 {
140
140
val rhs = transform(tag.select(tpnme.UNARY_~ ))
141
141
val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree (rhs, rhs), rhs, rhs)
142
142
val original = tref.symbol.asType
@@ -146,9 +146,9 @@ class ReifyQuotes extends MacroTransform {
146
146
info = TypeAlias (tag.tpe.select(tpnme.UNARY_~ )))
147
147
ctx.typeAssigner.assignType(untpd.TypeDef (original.name, alias), local)
148
148
}
149
- importedTypes .clear()
149
+ importedTags .clear()
150
150
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))
152
152
.apply(expr))
153
153
}
154
154
@@ -180,6 +180,29 @@ class ReifyQuotes extends MacroTransform {
180
180
def spliceOutsideQuotes (pos : Position )(implicit ctx : Context ) =
181
181
ctx.error(i " splice outside quotes " , pos)
182
182
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
+
183
206
/** Check reference to `sym` for phase consistency, where `tp` is the underlying type
184
207
* by which we refer to `sym`.
185
208
*/
@@ -192,14 +215,10 @@ class ReifyQuotes extends MacroTransform {
192
215
if (! isThis && sym.maybeOwner.isType)
193
216
check(sym.owner, sym.owner.thisType, pos)
194
217
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)
203
222
}
204
223
205
224
/** Check all named types and this-types in a given type for phase consistency. */
@@ -209,7 +228,7 @@ class ReifyQuotes extends MacroTransform {
209
228
case tp : NamedType if tp.symbol.isSplice =>
210
229
if (inQuote) outer.checkType(pos).foldOver(acc, tp)
211
230
else {
212
- spliceOutsideQuotes(pos)
231
+ if (tp.isTerm) spliceOutsideQuotes(pos)
213
232
tp
214
233
}
215
234
case tp : NamedType =>
0 commit comments