Skip to content

Fix #6588: Dealias inferred quoted types #6599

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 3 commits into from
Jun 5, 2019
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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
case New(_) | Closure(_, _, _) =>
Pure
case TypeApply(fn, _) =>
if (fn.symbol.is(Erased)) Pure else exprPurity(fn)
if (fn.symbol.is(Erased) || fn.symbol == defn.InternalQuoted_typeQuote) Pure else exprPurity(fn)
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be good if we could find a more systematic way to do this. Ideally, we could mark typeQuote to be a pure function. We have a flag for that (it's StableRealizable), but so far this cannot be set from source.

Maybe leave a TODO that we should do the change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should we have some internal flag to mark those methods?

case Apply(fn, args) =>
def isKnownPureOp(sym: Symbol) =
sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass
Expand Down
8 changes: 6 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,17 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
val reqType = defn.QuotedTypeType.appliedTo(tp)
val tag = ctx.typer.inferImplicitArg(reqType, pos.span)
tag.tpe match {
case fail: SearchFailureType =>
case _: TermRef =>
Some(tag.select(tpnme.splice))
case _: SearchFailureType =>
levelError(i"""
|
| The access would be accepted with the right type tag, but
| ${ctx.typer.missingArgMsg(tag, reqType, "")}""")
case _ =>
Some(tag.select(tpnme.splice))
levelError(i"""
|
| The access would be accepted with an implict $reqType""")
}
}
case _ =>
Expand Down
9 changes: 9 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/Staging.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ class Staging extends MacroTransform {
checker.transform(tree)
case _ =>
}

tree.tpe match {
case tpe @ TypeRef(prefix, _) if tpe.typeSymbol eq defn.QuotedType_splice =>
// Type splices must have a know term ref, usually to an implicit argument
// This is mostly intended to catch `quoted.Type[T]#splice` types which should just be `T`
assert(prefix.isInstanceOf[TermRef] || prefix.isInstanceOf[ThisType], prefix)
case _ =>
// OK
}
}
}

Expand Down
26 changes: 7 additions & 19 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,9 @@ trait Implicits { self: Typer =>
EmptyTree
}

/** Synthesize the tree for `'[T]` for an implicit `scala.quoted.Type[T]`.
* `T` is deeply dealiassed to avoid references to local type aliases.
*/
lazy val synthesizedTypeTag: SpecialHandler =
(formal: Type, span: Span) => implicit (ctx: Context) => {
def quotedType(t: Type) = {
Expand All @@ -682,26 +685,11 @@ trait Implicits { self: Typer =>
ref(defn.InternalQuoted_typeQuote).appliedToType(t)
}
formal.argInfos match {
case arg :: Nil if !arg.typeSymbol.is(Param) =>
object bindFreeVars extends TypeMap {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This TypeMap is completely useless here. This is already performed by the PCP check in the Staging phase which emits errors with extra level information if the implicit is not found.

var ok = true
def apply(t: Type) = t match {
case t @ TypeRef(NoPrefix, _) =>
inferImplicit(defn.QuotedTypeType.appliedTo(t), EmptyTree, span) match {
case SearchSuccess(tag, _, _) if tag.tpe.isStable =>
tag.tpe.select(defn.QuotedType_splice)
case _ =>
ok = false
t
}
case _ => t
}
case arg :: Nil =>
val deepDealias = new TypeMap {
def apply(tp: Type): Type = mapOver(tp.dealias)
}
val tag = bindFreeVars(arg)
if (bindFreeVars.ok) quotedType(tag)
else EmptyTree
case arg :: Nil if ctx.inInlineMethod =>
quotedType(arg)
quotedType(deepDealias(arg))
case _ =>
EmptyTree
}
Expand Down
8 changes: 0 additions & 8 deletions tests/neg/quote-0.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,4 @@ class Test {

'{(y: Expr[Int]) => $y } // error: wrong staging level

def f[T](t: Type[T], x: Expr[T]) = '{
val z2 = $x // error // error: wrong staging level
}

def g[T](implicit t: Type[T], x: Expr[T]) = '{
val z2 = $x // ok
}

}
13 changes: 13 additions & 0 deletions tests/neg/quote-1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import scala.quoted._

class Test {

def f[T](t: Type[T], x: Expr[T]) = '{
val z2 = $x // error // error: wrong staging level
}

def g[T](implicit t: Type[T], x: Expr[T]) = '{
val z2 = $x // ok
}

}
27 changes: 27 additions & 0 deletions tests/pos/i6588.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import scala.quoted._

inline def foo[T:Type]: Int = 10

def main = {
type S = Int
foo[S]
foo[Int]

type T = Int => Int
foo[T]
foo[Int => Int]

type U = List[Int]
foo[U]
foo[List[Int]]

type N = List
foo[N]
foo[List]

type V = List[S]
foo[V]

type B = V => T
foo[B]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The code generated for the type tags is now

def main: Int = 
      {
        type S = Int
        {
          val evidence$1: quoted.Type[Int] = scala.quoted.Type.IntTag
          10:Int
        }
        {
          val evidence$1: quoted.Type[Int] = scala.quoted.Type.IntTag
          10:Int
        }
        type T = Function1[Int, Int]
        {
          val evidence$1: quoted.Type[Int => Int] = '[Int => Int]
          10:Int
        }
        {
          val evidence$1: quoted.Type[Int => Int] = '[Int => Int]
          10:Int
        }
        type U = List[Int]
        {
          val evidence$1: quoted.Type[List[Int]] = '[List[Int]]
          10:Int
        }
        {
          val evidence$1: quoted.Type[List[Int]] = '[List[Int]]
          10:Int
        }
        type N = List
        {
          val evidence$1: quoted.Type[List] = '[List]
          10:Int
        }
        {
          val evidence$1: quoted.Type[List] = '[List]
          10:Int
        }
        type V = List[S]
        {
          val evidence$1: quoted.Type[List[Int]] = '[List[Int]]
          10:Int
        }
        type B = Function1[V, T]
        {
          val evidence$1: quoted.Type[List[Int] => Int => Int] = 
            '[List[Int] => Int => Int]
          10:Int
        }
      }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After the second commit, we get

      ...
      type V = List[S]
      {
        10:Int
      }
      type B = Function1[V, T]
      {
        10:Int
      }

}
2 changes: 1 addition & 1 deletion tests/run-with-compiler/i4350.check
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
(null: scala.Any).asInstanceOf[java.lang.Object]
(null: scala.Any).asInstanceOf[scala.Predef.String]
(null: scala.Any).asInstanceOf[java.lang.String]