From 98975925e325430a12fa4fb2e51fc9e40ca7600e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 18 Apr 2023 10:34:42 +0200 Subject: [PATCH 1/2] Add new EXPLICITtpt to TASTy format This is a new encoding of HOLE that differentiates between type and term arguments of the hole. ``` -- pickled quote trees: These trees can only appear in pickled quotes. They will never be in a TASTy file. EXPLICITtpt tpt_Term -- Tag for a type tree that in a context where it is not explicitly known that this tree is a type. HOLE Length idx_Nat tpe_Type arg_Tree* -- Splice hole with index `idx`, the type of the hole `tpe`, type and term arguments of the hole `arg`s ``` We will only have hole captured types if there is a type defined in a quote and used in a nested quote. Most of the time we do not have those types and therefore no overhead in the encoding compared to before this change. --- .../tools/dotc/core/tasty/TreePickler.scala | 5 ++++- .../tools/dotc/core/tasty/TreeUnpickler.scala | 18 ++++++++++-------- project/MiMaFilters.scala | 1 + tasty/src/dotty/tools/tasty/TastyFormat.scala | 6 ++++++ tests/pos-macros/captured-type/Macro_1.scala | 8 ++++++++ tests/pos-macros/captured-type/Test_2.scala | 3 +++ 6 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 tests/pos-macros/captured-type/Macro_1.scala create mode 100644 tests/pos-macros/captured-type/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 93d15eca265d..8a3eb9e01799 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -694,7 +694,10 @@ class TreePickler(pickler: TastyPickler) { withLength { writeNat(idx) pickleType(tree.tpe, richTypes = true) - args.foreach(pickleTree) + args.foreach { arg => + if arg.isType then writeByte(EXPLICITtpt) + pickleTree(arg) + } } } catch { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 804a1f8244f3..0645ebecd1d0 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1243,6 +1243,8 @@ class TreeUnpickler(reader: TastyReader, ByNameTypeTree(if withPureFuns then arg else arg.adaptByNameArgUnderPureFuns) case NAMEDARG => NamedArg(readName(), readTree()) + case EXPLICITtpt => + readTpt() case _ => readPathTree() } @@ -1468,10 +1470,7 @@ class TreeUnpickler(reader: TastyReader, val alias = if currentAddr == end then EmptyTree else readTpt() createNullableTypeBoundsTree(lo, hi, alias) case HOLE => - val idx = readNat() - val tpe = readType() - val args = until(end)(readTree()) - Hole(true, idx, args, EmptyTree, tpe) + readHole(end, isTerm = true) case _ => readPathTree() } @@ -1502,10 +1501,7 @@ class TreeUnpickler(reader: TastyReader, case HOLE => readByte() val end = readEnd() - val idx = readNat() - val tpe = readType() - val args = until(end)(readTree()) - Hole(false, idx, args, EmptyTree, tpe) + readHole(end, isTerm = false) case _ => if (isTypeTreeTag(nextByte)) readTree() else { @@ -1538,6 +1534,12 @@ class TreeUnpickler(reader: TastyReader, setSpan(start, CaseDef(pat, guard, rhs)) } + def readHole(end: Addr, isTerm: Boolean)(using Context): Tree = + val idx = readNat() + val tpe = readType() + val args = until(end)(readTree()) + Hole(isTerm, idx, args, EmptyTree, tpe) + def readLater[T <: AnyRef](end: Addr, op: TreeReader => Context ?=> T)(using Context): Trees.Lazy[T] = readLaterWithOwner(end, op)(ctx.owner) diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index b40e53cb4f39..c93dbd2c2f00 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -5,6 +5,7 @@ object MiMaFilters { val Library: Seq[ProblemFilter] = Seq( ) val TastyCore: Seq[ProblemFilter] = Seq( + ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.TastyFormat.EXPLICITtpt"), ) val Interfaces: Seq[ProblemFilter] = Seq( ) diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 226fc14acb39..d91295f06af5 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -122,6 +122,8 @@ Standard-Section: "ASTs" TopLevelStat* MATCHtpt Length bound_Term? sel_Term CaseDef* -- sel match { CaseDef } where `bound` is optional upper bound of all rhs BYNAMEtpt underlying_Term -- => underlying SHAREDterm term_ASTRef -- Link to previously serialized term + -- pickled quote trees: -- These trees can only appear in pickled quotes. They will never be in a TASTy file. + EXPLICITtpt tpt_Term -- Tag for a type tree that in a context where it is not explicitly known that this tree is a type. HOLE Length idx_Nat tpe_Type arg_Tree* -- Splice hole with index `idx`, the type of the hole `tpe`, type and term arguments of the hole `arg`s @@ -511,6 +513,8 @@ object TastyFormat { final val RECtype = 100 final val SINGLETONtpt = 101 final val BOUNDED = 102 + final val EXPLICITtpt = 103 + // Cat. 4: tag Nat AST @@ -659,6 +663,7 @@ object TastyFormat { | ANNOTATEDtpt | BYNAMEtpt | MATCHtpt + | EXPLICITtpt | BIND => true case _ => false } @@ -803,6 +808,7 @@ object TastyFormat { case ANNOTATION => "ANNOTATION" case PRIVATEqualified => "PRIVATEqualified" case PROTECTEDqualified => "PROTECTEDqualified" + case EXPLICITtpt => "EXPLICITtpt" case HOLE => "HOLE" } diff --git a/tests/pos-macros/captured-type/Macro_1.scala b/tests/pos-macros/captured-type/Macro_1.scala new file mode 100644 index 000000000000..3f094487ee4f --- /dev/null +++ b/tests/pos-macros/captured-type/Macro_1.scala @@ -0,0 +1,8 @@ +import scala.quoted.* + +inline def foo[U](u: U): U = ${ fooImpl[U]('u) } + +def fooImpl[U: Type](u: Expr[U])(using Quotes): Expr[U] = '{ + def f[T](x: T): T = ${ identity('{ x: T }) } + f[U]($u) +} diff --git a/tests/pos-macros/captured-type/Test_2.scala b/tests/pos-macros/captured-type/Test_2.scala new file mode 100644 index 000000000000..ed1baab565e0 --- /dev/null +++ b/tests/pos-macros/captured-type/Test_2.scala @@ -0,0 +1,3 @@ +def test = + foo(1) + foo("abc") From 46a8d132543c9d987d75cc3ec93e14b7a05f03f4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 7 Aug 2023 16:53:09 +0200 Subject: [PATCH 2/2] Only use EXPLICITtpt for TypeTree containing TermRefs --- compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 8a3eb9e01799..a04f05cb820c 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -695,7 +695,9 @@ class TreePickler(pickler: TastyPickler) { writeNat(idx) pickleType(tree.tpe, richTypes = true) args.foreach { arg => - if arg.isType then writeByte(EXPLICITtpt) + arg.tpe match + case _: TermRef if arg.isType => writeByte(EXPLICITtpt) + case _ => pickleTree(arg) } }