Skip to content

Heal type within quote internal encoding #11590

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

Merged
merged 1 commit into from
Mar 4, 2021
Merged
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
22 changes: 15 additions & 7 deletions compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ import scala.annotation.constructorOnly
*
* Type healing consists in transforming a phase inconsistent type `T` into a splice of `${summon[Type[T]]}`.
*
* As references to types do not necessarily have an assosiated tree it is not always possible to replace the types directly.
* Instead we always generate a type alias for it and palce it at the start of the surounding quote. This also avoids duplication.
* As references to types do not necessarily have an associated tree it is not always possible to replace the types directly.
* Instead we always generate a type alias for it and place it at the start of the surrounding quote. This also avoids duplication.
* For example:
* '{
* val x: List[T] = List[T]()
Expand Down Expand Up @@ -118,7 +118,13 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
case Nil => body1
case tags => tpd.Block(tags, body1).withSpan(body.span)

super.transformQuotation(body2, quote)
quote match {
case Apply(fn1 @ TypeApply(fn0, targs), _) =>
val targs1 = targs.map(targ => TypeTree(healTypeOfTerm(fn1.srcPos)(targ.tpe)))
cpy.Apply(quote)(cpy.TypeApply(fn1)(fn0, targs1), body2 :: Nil)
case quote: TypeApply =>
cpy.TypeApply(quote)(quote.fun, body2 :: Nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At line 119, body2 could be a block, and it's used as a type. I guess this case not reachable if body2 is a block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, is not reachable. This is essentially the same logic as in super.transformQuotation.

}
}

/** Transform splice
Expand Down Expand Up @@ -168,8 +174,8 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
/** If the type refers to a locally defined symbol (either directly, or in a pickled type),
* check that its staging level matches the current level.
* - Static types and term are allowed at any level.
* - If a type reference is used a higher level, then it is insosistent. Will atempt to heal before failing.
* - If a term reference is used a different level, then it is insosistent.
* - If a type reference is used a higher level, then it is inconsistent. Will attempt to heal before failing.
* - If a term reference is used a different level, then it is inconsistent.
*
* If `T` is a reference to a type at the wrong level, try to heal it by replacing it with
* a type tag of type `quoted.Type[T]`.
Expand Down Expand Up @@ -207,7 +213,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
mapOver(tp)
}

/** Check phase consistency of terms and heal incosistent type references. */
/** Check phase consistency of terms and heal inconsistent type references. */
private def healTypeOfTerm(pos: SrcPos)(using Context) = new TypeMap {
def apply(tp: Type): Type =
tp match
Expand All @@ -217,14 +223,16 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
levelError(tp.symbol, tp, pos)
case tp: ThisType if level != -1 && level != levelOf(tp.cls) =>
levelError(tp.cls, tp, pos)
case tp: AnnotatedType =>
derivedAnnotatedType(tp, apply(tp.parent), tp.annot)
case _ =>
if tp.typeSymbol.is(Package) then tp
else mapOver(tp)
}

/** Try to heal reference to type `T` used in a higher level than its definition.
* Returns a reference to a type tag generated by `QuoteTypeTags` that contains a
* refercence to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`.
* reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`.
* Emits and error if `T` cannot be healed and returns `T`.
*/
protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): TypeRef = {
Expand Down
15 changes: 15 additions & 0 deletions tests/pos-macros/i11587.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import scala.quoted._
class Foo:
def foo[T: Type](using Quotes): Unit = '{ // level 1
given Quotes = ???

Type.of[T]

'{ ??? : T } // level 2

'{ // level 2
given Quotes = ???
Type.of[T]
'{ ??? : T } // level 3
}
}