Skip to content

Commit 31a81fb

Browse files
authored
Merge pull request #3867 from dotty-staging/fix-typetags
Fix #3866: Add support for typetags
2 parents fc06068 + 58677ed commit 31a81fb

16 files changed

+202
-79
lines changed

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -615,14 +615,20 @@ class Definitions {
615615
lazy val QuotedExprType = ctx.requiredClassRef("scala.quoted.Expr")
616616
def QuotedExprClass(implicit ctx: Context) = QuotedExprType.symbol.asClass
617617

618-
def QuotedExpr_~(implicit ctx: Context) = QuotedExprClass.requiredMethod(nme.UNARY_~)
619-
def QuotedExpr_run(implicit ctx: Context) = QuotedExprClass.requiredMethod(nme.run)
618+
lazy val QuotedExpr_spliceR = QuotedExprClass.requiredMethod(nme.UNARY_~)
619+
def QuotedExpr_~(implicit ctx: Context) = QuotedExpr_spliceR.symbol
620+
lazy val QuotedExpr_runR = QuotedExprClass.requiredMethodRef(nme.run)
621+
def QuotedExpr_run(implicit ctx: Context) = QuotedExpr_runR.symbol
620622

621623
lazy val QuotedTypeType = ctx.requiredClassRef("scala.quoted.Type")
622624
def QuotedTypeClass(implicit ctx: Context) = QuotedTypeType.symbol.asClass
623625

624-
def QuotedType_~(implicit ctx: Context) =
625-
QuotedTypeClass.info.member(tpnme.UNARY_~).symbol.asType
626+
lazy val QuotedType_spliceR = QuotedTypeClass.requiredType(tpnme.UNARY_~).typeRef
627+
def QuotedType_~ = QuotedType_spliceR.symbol
628+
629+
lazy val QuotedTypeModule = QuotedTypeClass.companionModule
630+
lazy val QuotedType_applyR = QuotedTypeModule.requiredMethodRef(nme.apply)
631+
def QuotedType_apply(implicit ctx: Context) = QuotedType_applyR.symbol
626632

627633
def Unpickler_unpickleExpr = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleExpr")
628634
def Unpickler_unpickleType = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleType")

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ object Denotations {
313313
def requiredClass(name: PreName)(implicit ctx: Context): ClassSymbol =
314314
info.member(name.toTypeName).requiredSymbol(_.isClass).asClass
315315

316+
def requiredType(name: PreName)(implicit ctx: Context): TypeSymbol =
317+
info.member(name.toTypeName).requiredSymbol(_.isType).asType
318+
316319
/** The alternative of this denotation that has a type matching `targetType` when seen
317320
* as a member of type `site`, `NoDenotation` if none exists.
318321
*/

compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ Standard-Section: "ASTs" TopLevelStat*
102102
REFINEDtpt Length underlying_Term refinement_Stat*
103103
APPLIEDtpt Length tycon_Term arg_Term*
104104
POLYtpt Length TypeParam* body_Term
105-
TYPEBOUNDStpt Length low_Term high_Term
105+
TYPEBOUNDStpt Length low_Term high_Term?
106106
ANNOTATEDtpt Length underlying_Term fullAnnotation_Term
107107
ANDtpt Length left_Term right_Term
108108
ORtpt Length left_Term right_Term

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,10 @@ class TreePickler(pickler: TastyPickler) {
552552
withLength { pickleParams(tparams); pickleTree(body) }
553553
case TypeBoundsTree(lo, hi) =>
554554
writeByte(TYPEBOUNDStpt)
555-
withLength { pickleTree(lo); pickleTree(hi) }
555+
withLength {
556+
pickleTree(lo);
557+
if (hi ne lo) pickleTree(hi)
558+
}
556559
case Hole(idx, args) =>
557560
writeByte(HOLE)
558561
withLength {

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,9 @@ class TreeUnpickler(reader: TastyReader,
10381038
val body = readTpt()
10391039
LambdaTypeTree(tparams, body)
10401040
case TYPEBOUNDStpt =>
1041-
TypeBoundsTree(readTpt(), readTpt())
1041+
val lo = readTpt()
1042+
val hi = if (currentAddr == end) lo else readTpt()
1043+
TypeBoundsTree(lo, hi)
10421044
case HOLE =>
10431045
readHole(end)
10441046
case _ =>

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

Lines changed: 41 additions & 22 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._
@@ -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 =>

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

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -553,34 +553,54 @@ trait Implicits { self: Typer =>
553553
}
554554

555555
/** Find an implicit argument for parameter `formal`.
556-
* @param error An error handler that gets an error message parameter
557-
* which is itself parameterized by another string,
558-
* indicating where the implicit parameter is needed
556+
* Return a failure as a SearchFailureType in the type of the returned tree.
559557
*/
560558
def inferImplicitArg(formal: Type, pos: Position)(implicit ctx: Context): Tree = {
561559

562560
/** If `formal` is of the form ClassTag[T], where `T` is a class type,
563561
* synthesize a class tag for `T`.
564562
*/
565-
def synthesizedClassTag(formal: Type)(implicit ctx: Context): Tree =
566-
formal.argInfos match {
567-
case arg :: Nil =>
568-
fullyDefinedType(arg, "ClassTag argument", pos) match {
569-
case defn.ArrayOf(elemTp) =>
570-
val etag = inferImplicitArg(defn.ClassTagType.appliedTo(elemTp), pos)
571-
if (etag.tpe.isError) EmptyTree else etag.select(nme.wrap)
572-
case tp if hasStableErasure(tp) && !defn.isBottomClass(tp.typeSymbol) =>
573-
ref(defn.ClassTagModule)
574-
.select(nme.apply)
575-
.appliedToType(tp)
576-
.appliedTo(clsOf(erasure(tp)))
577-
.withPos(pos)
578-
case tp =>
579-
EmptyTree
563+
def synthesizedClassTag(formal: Type): Tree = formal.argInfos match {
564+
case arg :: Nil =>
565+
fullyDefinedType(arg, "ClassTag argument", pos) match {
566+
case defn.ArrayOf(elemTp) =>
567+
val etag = inferImplicitArg(defn.ClassTagType.appliedTo(elemTp), pos)
568+
if (etag.tpe.isError) EmptyTree else etag.select(nme.wrap)
569+
case tp if hasStableErasure(tp) && !defn.isBottomClass(tp.typeSymbol) =>
570+
ref(defn.ClassTagModule)
571+
.select(nme.apply)
572+
.appliedToType(tp)
573+
.appliedTo(clsOf(erasure(tp)))
574+
.withPos(pos)
575+
case tp =>
576+
EmptyTree
577+
}
578+
case _ =>
579+
EmptyTree
580+
}
581+
582+
def synthesizedTypeTag(formal: Type): Tree = formal.argInfos match {
583+
case arg :: Nil =>
584+
object bindFreeVars extends TypeMap {
585+
var ok = true
586+
def apply(t: Type) = t match {
587+
case t @ TypeRef(NoPrefix, _) =>
588+
inferImplicit(defn.QuotedTypeType.appliedTo(t), EmptyTree, pos) match {
589+
case SearchSuccess(tag, _, _) if tag.tpe.isStable =>
590+
tag.tpe.select(defn.QuotedType_~)
591+
case _ =>
592+
ok = false
593+
t
594+
}
595+
case _ => t
580596
}
581-
case _ =>
582-
EmptyTree
583-
}
597+
}
598+
val tag = bindFreeVars(arg)
599+
if (bindFreeVars.ok) ref(defn.typeQuoteMethod).appliedToType(tag)
600+
else EmptyTree
601+
case _ =>
602+
EmptyTree
603+
}
584604

585605
/** If `formal` is of the form Eq[T, U], where no `Eq` instance exists for
586606
* either `T` or `U`, synthesize `Eq.eqAny[T, U]` as solution.
@@ -644,6 +664,8 @@ trait Implicits { self: Typer =>
644664
tree
645665
else if (formalValue.isRef(defn.ClassTagClass))
646666
synthesizedClassTag(formalValue).orElse(tree)
667+
else if (formalValue.isRef(defn.QuotedTypeClass))
668+
synthesizedTypeTag(formalValue).orElse(tree)
647669
else if (formalValue.isRef(defn.EqClass))
648670
synthesizedEq(formalValue).orElse(tree)
649671
else

0 commit comments

Comments
 (0)