Skip to content

Re-architecture staging phase and fix all known issues of that phase #8796

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 2 commits into from
May 5, 2020
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
276 changes: 128 additions & 148 deletions compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ class ReifyQuotes extends MacroTransform {

/** Returns true if this tree will be captured by `makeLambda`. Checks phase consistency and presence of capturer. */
private def isCaptured(sym: Symbol, level: Int)(implicit ctx: Context): Boolean =
level == 1 && levelOf(sym).contains(1) && capturers.contains(sym)
level == 1 && levelOf(sym) == 1 && capturers.contains(sym)

/** Transform `tree` and return the resulting tree and all `embedded` quotes
* or splices as a pair.
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/transform/Staging.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,19 @@ class Staging extends MacroTransform {
tree match {
case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass =>
val checker = new PCPCheckAndHeal(freshStagingContext) {
override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SourcePosition)(implicit ctx: Context): Option[tpd.Tree] = {
override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SourcePosition)(implicit ctx: Context): TypeRef = {
def symStr =
if (sym.is(ModuleClass)) sym.sourceModule.show
else i"${sym.name}.this"
val errMsg = s"\nin ${ctx.owner.fullName}"
assert(
ctx.owner.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) ||
(sym.isType && levelOf(sym).getOrElse(0) > 0),
(sym.isType && levelOf(sym) > 0),
em"""access to $symStr from wrong staging level:
| - the definition is at level ${levelOf(sym).getOrElse(0)},
| - the definition is at level ${levelOf(sym)},
| - but the access is at level $level.$errMsg""")

None
tp
}
}
checker.transform(tree)
Expand Down
27 changes: 19 additions & 8 deletions compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,28 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
import TreeMapWithStages._

/** A map from locally defined symbols to their definition quotation level */
private val levelOfMap: mutable.HashMap[Symbol, Int] = ictx.property(LevelOfKey).get
private[this] val levelOfMap: mutable.HashMap[Symbol, Int] = ictx.property(LevelOfKey).get

/** A stack of entered symbols, to be unwound after scope exit */
private var enteredSyms: List[Symbol] = Nil
private[this] var enteredSyms: List[Symbol] = Nil

/** If we are inside a quote or a splice */
private[this] var inQuoteOrSplice = false

/** The quotation level of the definition of the locally defined symbol */
protected def levelOf(sym: Symbol): Option[Int] = levelOfMap.get(sym)
protected def levelOf(sym: Symbol): Int = levelOfMap.getOrElse(sym, 0)

/** Localy defined symbols seen so far by `StagingTransformer.transform` */
protected def localSymbols: List[Symbol] = enteredSyms

/** Enter staging level of symbol defined by `tree`, if applicable. */
/** If we are inside a quote or a splice */
protected def isInQuoteOrSplice: Boolean = inQuoteOrSplice

/** Enter staging level of symbol defined by `tree` */
private def markSymbol(sym: Symbol)(implicit ctx: Context): Unit =
if ((sym.isClass || sym.maybeOwner.isTerm) && !levelOfMap.contains(sym)) {
if level != 0 && !levelOfMap.contains(sym) then
levelOfMap(sym) = level
enteredSyms = sym :: enteredSyms
}

/** Enter staging level of symbol defined by `tree`, if applicable. */
private def markDef(tree: Tree)(implicit ctx: Context): Unit = tree match {
Expand Down Expand Up @@ -89,19 +94,25 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
tree match {

case Quoted(quotedTree) =>
dropEmptyBlocks(quotedTree) match {
val old = inQuoteOrSplice
inQuoteOrSplice = true
try dropEmptyBlocks(quotedTree) match {
case Spliced(t) =>
// '{ $x } --> x
// and adapt the refinment of `QuoteContext { type tasty: ... } ?=> Expr[T]`
transform(t).asInstance(tree.tpe)
case _ => transformQuotation(quotedTree, tree)
}
finally inQuoteOrSplice = old

case tree @ Spliced(splicedTree) =>
dropEmptyBlocks(splicedTree) match {
val old = inQuoteOrSplice
inQuoteOrSplice = true
try dropEmptyBlocks(splicedTree) match {
case Quoted(t) => transform(t) // ${ 'x } --> x
case _ => transformSplice(splicedTree, tree)
}
finally inQuoteOrSplice = old

case Block(stats, _) =>
val last = enteredSyms
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import config.Feature._
import config.SourceVersion._
import rewrites.Rewrites.patch
import NavigateAST._
import dotty.tools.dotc.transform.{PCPCheckAndHeal, Staging, TreeMapWithStages}
import transform.SymUtils._
import transform.TypeUtils._
import reporting.trace
Expand Down
3 changes: 3 additions & 0 deletions compiler/test/dotc/pos-test-pickling.blacklist
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ nullable.scala

# parameter untupling with overloaded functions (see comment in Applications.normArg)
i7757.scala

# splice type tag dealiased in this reference
i8651b.scala
14 changes: 14 additions & 0 deletions tests/neg-macros/quote-this-a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import scala.quoted._

class Foo {

def f(using QuoteContext): Unit = '{
def bar[T](x: T): T = x
bar[
this.type // error
] {
this // error
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,6 @@ import scala.quoted._

class Foo {

def f(using QuoteContext): Unit = '{
def bar[T](x: T): T = x
bar[
this.type // error
] {
this // error
}
}

inline def i(): Unit = ${ Foo.impl[Any]('{
val x: QuoteContext = ???
given x.type = x
Expand Down
12 changes: 12 additions & 0 deletions tests/neg/i7892.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import scala.quoted._

package x {
class CExprResult1[T]
}

def run(using qctx: QuoteContext): Unit = {
val cpsLeft: x.CExprResult1[?] = ???
run1(cpsLeft) // error
}

def run1[L:Type](cpsLeft: x.CExprResult1[L]): Unit = ???
8 changes: 8 additions & 0 deletions tests/pos-macros/i6140.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import scala.quoted._
sealed trait Trait[T] {
type t = T
}

object O {
def fn[T:Type](t : Trait[T])(using QuoteContext): Type[T] = '[t.t]
}
11 changes: 11 additions & 0 deletions tests/pos-macros/i7030/Macros_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import scala.quoted._

inline def inner(exprs: Any): Any = ${innerImpl('exprs)}
def innerImpl(exprs: Expr[Any])(using QuoteContext): Expr[Any] =
'{$exprs ; ()}

inline def outer(expr: => Any): Any = ${outerImpl('expr)}
def outerImpl(body: Expr[Any])(using ctx: QuoteContext): Expr[Any] = {
import ctx.tasty._
body.unseal.underlyingArgument.seal
}
1 change: 1 addition & 0 deletions tests/pos-macros/i7030/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
val x = outer(inner(???))
21 changes: 21 additions & 0 deletions tests/pos-macros/i8100.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import scala.quoted._

class M {
type E
}

def f[T: Type](using QuoteContext) =
Expr.summon[M] match
case Some('{ $mm : $tt }) =>
'{
val m = $mm
type ME = m.E
${ g[ME](using '[ME]) }
${ g[m.E](using '[ME]) }
${ g[ME](using '[m.E]) }
${ g[m.E](using '[m.E]) }
// ${ g[ME] } // FIXME: issue seems to be in ReifyQuotes
// ${ g[m.E] } // FIXME: issue seems to be in ReifyQuotes
}

def g[T](using Type[T]) = ???
4 changes: 4 additions & 0 deletions tests/pos/i7997.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import scala.quoted._
def oops(using QuoteContext) = {
val q = '{ class Foo { val x = 3; ${ val v = 'x; '{} } }}
}
8 changes: 8 additions & 0 deletions tests/pos/i8651a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import scala.quoted._
def coroutineImpl(using QuoteContext): Expr[Any] =
'{
new {
def state: Int = 0
${identity('state)}
}
}
31 changes: 31 additions & 0 deletions tests/pos/i8651b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
abstract class Coroutine[+T] {
def continue: Option[T]
}

object Macros {

import scala.quoted._
import scala.quoted.matching._


inline def coroutine[T](inline body: Any): Coroutine[T] = ${ coroutineImpl('{body}) }

def coroutineImpl[T: Type](expr: Expr[_ <: Any])(implicit qtx: QuoteContext): Expr[Coroutine[T]] = {
import qtx.tasty.{_, given _}

'{
new Coroutine[T] {
var state: Int = 0

def continue: Option[T] = ${
'{
state = 1 //if this line is commented there are no compile errors anymore!!!
None
}
}

}
}

}
}
4 changes: 4 additions & 0 deletions tests/pos/quote-type-with-param.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import scala.quoted._

def f(using QuoteContext): Unit =
'{ type T[X] = List[X] }
14 changes: 14 additions & 0 deletions tests/run-macros/i6772/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import scala.quoted._

object Macros {

inline def m() : Any = ${ mImpl() }

def mImpl()(using QuoteContext): Expr[Any] =
List(Expr(1), Expr(2), Expr(3)).toExprOfList

def (list: List[Expr[T]]).toExprOfList[T](using Type[T], QuoteContext): Expr[List[T]] = '{
val buff = List.newBuilder[T]
${ Expr.block(list.map(v => '{ buff += $v }), '{ buff.result() }) }
}
}
8 changes: 8 additions & 0 deletions tests/run-macros/i6772/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import scala.quoted._
import Macros._

object Test {
def main(args: Array[String]): Unit = {
println(m())
}
}