From e29c481d790330c688fd1840fe5d95de71c70d6b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 27 Jan 2020 10:13:00 +0100 Subject: [PATCH 1/2] Fix #7048: Check for splice stability --- .../dotc/transform/PCPCheckAndHeal.scala | 6 ++- tests/neg/i7048e.scala | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 tests/neg/i7048e.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index dd1e4ea50717..d7fa5ababa5f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -18,6 +18,7 @@ import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.Spans._ import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.transform.TreeMapWithStages._ +import dotty.tools.dotc.typer.Checking import dotty.tools.dotc.typer.Implicits.SearchFailureType import dotty.tools.dotc.typer.Inliner @@ -31,7 +32,7 @@ import scala.annotation.constructorOnly * * Type healing consists in transforming a phase inconsistent type `T` into a splice of `implicitly[Type[T]]`. */ -class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) { +class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) with Checking { import tpd._ private val InAnnotation = Property.Key[Unit]() @@ -212,7 +213,8 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp) val tag = ctx.typer.inferImplicitArg(reqType, pos.span) tag.tpe match - case _: TermRef => + case tp: TermRef => + checkStable(tp, pos) Some(tag.select(tpnme.splice)) case _: SearchFailureType => levelError(sym, tp, pos, diff --git a/tests/neg/i7048e.scala b/tests/neg/i7048e.scala new file mode 100644 index 000000000000..be1953a41291 --- /dev/null +++ b/tests/neg/i7048e.scala @@ -0,0 +1,39 @@ +import scala.quoted.{given, _} + +abstract class Test { + type T + + val T: Type[T] + val getT: Type[T] = T // need this to avoid getting `null` + given Type[T] = getT + + def foo with QuoteContext: Expr[Any] = { + + val r = '{Option.empty[T]} // error + + { + val t: Test = this + import t.{given} + println(summon[Type[t.T]].show) + // val r = '{Option.empty[t.T]} // access to value t from wrong staging level + val r2 = '{Option.empty[${t.T}]} // works + } + + { + val r1 = '{Option.empty[${T}]} // works + val r2 = '{Option.empty[List[${T}]]} // works + // val r3 = '{summon[Type[${T}]]} // access to Test.this from wrong staging level + val r4 = '{summon[${T} <:< Any]} // error + } + + { + val s = '{Option.empty[${T}]} + val r = '{identity($s)} // works + val r2 = '{identity(${s: Expr[Option[T]]})} // error // error + } + + r + } +} + +@main def main = () From a00d1842f71ae8740b5c7bb0febcfb95a960224b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 28 Jan 2020 11:01:57 +0100 Subject: [PATCH 2/2] Add test --- .../test/dotc/pos-test-pickling.blacklist | 1 + tests/pos/i7048e.scala | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/pos/i7048e.scala diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index e7ff44c068fe..3d112e422a7a 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -10,6 +10,7 @@ t3486 t3612.scala reference scala-days-2019-slides +i7048e.scala # Stale symbol: package object scala seqtype-cycle diff --git a/tests/pos/i7048e.scala b/tests/pos/i7048e.scala new file mode 100644 index 000000000000..fc69af1e5a63 --- /dev/null +++ b/tests/pos/i7048e.scala @@ -0,0 +1,39 @@ +import scala.quoted.{given, _} + +abstract class Test { + type T + + val T: Type[T] + val getT: Type[T] = T // need this to avoid getting `null` + given getT.type = getT + + def foo with QuoteContext: Expr[Any] = { + + val r = '{Option.empty[T]} + + { + val t: Test = this + import t.{given} + println(summon[Type[t.T]].show) + // val r = '{Option.empty[t.T]} // access to value t from wrong staging level + val r2 = '{Option.empty[${t.T}]} + } + + { + val r1 = '{Option.empty[${T}]} // works + val r2 = '{Option.empty[List[${T}]]} // works + // val r3 = '{summon[Type[${T}]]} // access to Test.this from wrong staging level + val r4 = '{summon[${T} <:< Any]} + } + + { + val s = '{Option.empty[${T}]} + val r = '{identity($s)} // works + val r2 = '{identity(${s: Expr[Option[T]]})} + } + + r + } +} + +@main def main = ()