From 5e95030606324ee663a2ad659a56f8ab195997a4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 18 Apr 2023 10:34:42 +0200 Subject: [PATCH] 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 ++++++++++-------- 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 +++ 5 files changed, 31 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 8a396921f32b..05219184e224 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -670,7 +670,10 @@ class TreePickler(pickler: TastyPickler) { withLength { writeNat(idx) pickleType(tpt.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 9078a8959112..88e5a0437f4a 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1233,6 +1233,8 @@ class TreeUnpickler(reader: TastyReader, ByNameTypeTree(if knowsPureFuns then arg else arg.adaptByNameArgUnderPureFuns) case NAMEDARG => NamedArg(readName(), readTerm()) + case EXPLICITtpt => + readTpt() case _ => readPathTerm() } @@ -1436,10 +1438,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)(readTerm()) - Hole(true, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe) + readHole(end, isTerm = true) case _ => readPathTerm() } @@ -1470,10 +1469,7 @@ class TreeUnpickler(reader: TastyReader, case HOLE => readByte() val end = readEnd() - val idx = readNat() - val tpe = readType() - val args = until(end)(readTerm()) - Hole(false, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe) + readHole(end, isTerm = false) case _ => if (isTypeTreeTag(nextByte)) readTerm() else { @@ -1506,6 +1502,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)(readTerm()) + Hole(isTerm, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe) + def readLater[T <: AnyRef](end: Addr, op: TreeReader => Context ?=> T)(using Context): Trees.Lazy[T] = readLaterWithOwner(end, op)(ctx.owner) 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")