Skip to content

Commit 9de8784

Browse files
committed
pickle exported definitions with names
1 parent 8fedddf commit 9de8784

File tree

16 files changed

+141
-19
lines changed

16 files changed

+141
-19
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
361361
def Import(expr: Tree, selectors: List[untpd.ImportSelector])(using Context): Import =
362362
ta.assignType(untpd.Import(expr, selectors), newImportSymbol(ctx.owner, expr))
363363

364-
def Export(expr: Tree, selectors: List[untpd.ImportSelector])(using Context): Export =
365-
ta.assignType(untpd.Export(expr, selectors))
364+
def Export(expr: Tree, selectors: List[untpd.ImportSelector], forwarders: List[Name])(using Context): Export =
365+
ta.assignType(untpd.Export(expr, selectors), newExportSymbol(ctx.owner, forwarders))
366366

367367
def PackageDef(pid: RefTree, stats: List[Tree])(using Context): PackageDef =
368368
ta.assignType(untpd.PackageDef(pid, stats), pid)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ object StdNames {
129129
val EXCEPTION_RESULT_PREFIX: N = "exceptionResult"
130130
val EXPAND_SEPARATOR: N = str.EXPAND_SEPARATOR
131131
val IMPORT: N = "<import>"
132+
val EXPORT: N = "<export>"
132133
val MODULE_SUFFIX: N = str.MODULE_SUFFIX
133134
val OPS_PACKAGE: N = "<special-ops>"
134135
val OVERLOADED: N = "<overloaded>"

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,25 @@ object SymDenotations {
330330
else recurWithParamss(info, rawParamss)
331331
end paramSymss
332332

333+
final def exportedMembers(tree: tpd.Export)(using Context): List[Symbol] =
334+
if !(isAllOf(Synthetic | NonMember) && name == nme.EXPORT) then Nil
335+
else
336+
ensureCompleted()
337+
info match
338+
case info: ExportType =>
339+
def denot(pre: Type, name: Name, sig: Signature, target: Name) = {
340+
pre.findMember(name, owner.thisType, excluded = Private).atSignature(sig, target).symbol
341+
.ensuring(_.exists)
342+
}
343+
val selectFrom = tree.expr.tpe
344+
selectFrom.termSymbol.ensureCompleted()
345+
for name <- info.exported yield name match
346+
case NameKinds.SignedName(name, sig, target) =>
347+
denot(selectFrom, name, sig, target)
348+
case name =>
349+
denot(selectFrom, name, Signature.NotAMethod, EmptyTermName)
350+
case _ => Nil
351+
333352
/** The extension parameter of this extension method
334353
* @pre this symbol is an extension method
335354
*/

compiler/src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,10 @@ object Symbols {
720720
def newImportSymbol(owner: Symbol, info: Type, coord: Coord)(using Context): TermSymbol =
721721
newSymbol(owner, nme.IMPORT, Synthetic | NonMember, info, coord = coord)
722722

723+
/** Create an export symbol with given `info`. */
724+
def newExportSymbol(owner: Symbol, exported: List[Name], coord: Coord = NoCoord)(using Context): TermSymbol =
725+
newSymbol(owner, nme.EXPORT, Synthetic | NonMember, ExportType(exported), coord = coord)
726+
723727
/** Create a class constructor symbol for given class `cls`. */
724728
def newConstructor(
725729
cls: ClassSymbol,

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4815,10 +4815,8 @@ object Types {
48154815
/** The type of an import clause tree */
48164816
case class ImportType(expr: Tree) extends UncachedGroundType
48174817

4818-
/** Sentinal for typed export clauses */
4819-
@sharable case object ExportType extends CachedGroundType {
4820-
override def computeHash(bs: Binders): Int = hashSeed
4821-
}
4818+
/** The type of an import clause tree */
4819+
case class ExportType(exported: List[Name]) extends UncachedGroundType
48224820

48234821
/** Sentinel for "missing type" */
48244822
@sharable case object NoType extends CachedGroundType {

compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ class TastyPrinter(bytes: Array[Byte]) {
100100
printTrees()
101101
case PARAMtype =>
102102
printNat(); printNat()
103+
case EXPORT =>
104+
printTree()
105+
while nextByte == IMPORTED do printTree()
106+
until(end) {
107+
newLine()
108+
val tag = readByte()
109+
sb.append(" ").append(astTagToString(tag))
110+
printName();
111+
}
103112
case _ =>
104113
printTrees()
105114
}

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class TreePickler(pickler: TastyPickler) {
7979
case _ =>
8080
}
8181

82-
def registerDef(sym: Symbol): Unit = {
82+
def registerDef(sym: Symbol)(using Context): Unit = {
8383
symRefs(sym) = currentAddr
8484
forwardSymRefs.get(sym) match {
8585
case Some(refs) =>
@@ -593,11 +593,22 @@ class TreePickler(pickler: TastyPickler) {
593593
pickleTree(expr)
594594
pickleSelectors(selectors)
595595
}
596-
case Export(expr, selectors) =>
596+
case tree @ Export(expr, selectors) =>
597597
writeByte(EXPORT)
598598
withLength {
599599
pickleTree(expr)
600600
pickleSelectors(selectors)
601+
tree.tpe match {
602+
case tpe: TermRef =>
603+
tpe.symbol.info match {
604+
case info: ExportType => info.exported.foreach { name =>
605+
writeByte(if (name.isTypeName) SELECTtpt else SELECT)
606+
pickleName(name)
607+
}
608+
case _ => assert(false, s"expected ExportType")
609+
}
610+
case _ => assert(false, s"expected TermRef")
611+
}
601612
}
602613
case PackageDef(pid, stats) =>
603614
writeByte(PACKAGE)

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,7 @@ class TreeUnpickler(reader: TastyReader,
945945
.withType(localDummy.termRef))
946946
}
947947

948-
def skipToplevel()(using Context): Unit= {
948+
def skipToplevel()(using Context): Unit = {
949949
if (!isAtEnd && isTopLevel) {
950950
skipTree()
951951
skipToplevel()
@@ -970,9 +970,16 @@ class TreeUnpickler(reader: TastyReader,
970970
case TYPEDEF | VALDEF | DEFDEF =>
971971
readIndexedDef()
972972
case IMPORT =>
973-
readImportOrExport(Import(_, _))()
973+
readImportOrExport((expr, selectors, _) => Import(expr, selectors))()
974974
case EXPORT =>
975-
readImportOrExport(Export(_, _))()
975+
readImportOrExport({ (expr, selectors, end) =>
976+
Export(expr, selectors, until(end) {
977+
readByte() match {
978+
case SELECTtpt => readName().toTypeName
979+
case SELECT => readName()
980+
}
981+
})
982+
})()
976983
case PACKAGE =>
977984
val start = currentAddr
978985
processPackage { (pid, end) =>
@@ -983,13 +990,13 @@ class TreeUnpickler(reader: TastyReader,
983990
}
984991

985992
inline def readImportOrExport(inline mkTree:
986-
(Tree, List[untpd.ImportSelector]) => Tree)()(using Context): Tree = {
993+
(Tree, List[untpd.ImportSelector], Addr) => Tree)()(using Context): Tree = {
987994
val start = currentAddr
988995
assert(sourcePathAt(start).isEmpty)
989996
readByte()
990-
readEnd()
997+
val end = readEnd()
991998
val expr = readTerm()
992-
setSpan(start, mkTree(expr, readSelectors()))
999+
setSpan(start, mkTree(expr, readSelectors(), end))
9931000
}
9941001

9951002
def readSelectors()(using Context): List[untpd.ImportSelector] =

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,7 @@ class Namer { typer: Typer =>
943943
*/
944944
def exportForwarders(exp: Export): List[tpd.MemberDef] = {
945945
val buf = new mutable.ListBuffer[tpd.MemberDef]
946+
val exported = new mutable.ListBuffer[Symbol]
946947
val Export(expr, selectors) = exp
947948
val path = typedAheadExpr(expr, AnySelectionProto)
948949
checkLegalImportPath(path)
@@ -1011,6 +1012,7 @@ class Namer { typer: Typer =>
10111012
ddef
10121013
}
10131014

1015+
exported += sym
10141016
buf += forwarderDef.withSpan(span)
10151017
}
10161018

@@ -1047,9 +1049,16 @@ class Namer { typer: Typer =>
10471049
addForwarders(sels1, sel.name :: seen)
10481050
case _ =>
10491051

1052+
def asRefs(exported: List[Symbol]): List[Name] =
1053+
def nameAndSig(name: Name, sig: Signature, target: Name): Name =
1054+
if name.isTypeName || sig.eq(Signature.NotAMethod) then name
1055+
else NameKinds.SignedName(name.toTermName, sig, target.asTermName)
1056+
exported.map(sym => nameAndSig(sym.name, sym.signature, sym.targetName))
1057+
10501058
addForwarders(selectors, Nil)
10511059
val forwarders = buf.toList
10521060
exp.pushAttachment(ExportForwarders, forwarders)
1061+
recordSym(newExportSymbol(ctx.owner, asRefs(exported.toList), exp.span), exp)
10531062
forwarders
10541063
}
10551064

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,8 @@ trait TypeAssigner {
511511
def assignType(tree: untpd.Import, sym: Symbol)(using Context): Import =
512512
tree.withType(sym.termRef)
513513

514-
def assignType(tree: untpd.Export)(using Context): Export =
515-
tree.withType(ExportType)
514+
def assignType(tree: untpd.Export, sym: Symbol)(using Context): Export =
515+
tree.withType(sym.termRef)
516516

517517
def assignType(tree: untpd.Annotated, arg: Tree, annot: Tree)(using Context): Annotated = {
518518
assert(tree.isType) // annotating a term is done via a Typed node, can't use Annotate directly

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2254,9 +2254,9 @@ class Typer extends Namer
22542254
)
22552255
}
22562256

2257-
def typedExport(exp: untpd.Export)(using Context): Export = {
2257+
def typedExport(exp: untpd.Export, sym: Symbol)(using Context): Export = {
22582258
typedImportOrExport(exp)((expr1, selectors1) =>
2259-
assignType(cpy.Export(exp)(expr1, selectors1))
2259+
assignType(cpy.Export(exp)(expr1, selectors1), sym)
22602260
)
22612261
}
22622262

@@ -2494,7 +2494,7 @@ class Typer extends Namer
24942494
case tree: untpd.Function => typedFunction(tree, pt)
24952495
case tree: untpd.Closure => typedClosure(tree, pt)
24962496
case tree: untpd.Import => typedImport(tree, retrieveSym(tree))
2497-
case tree: untpd.Export => typedExport(tree)
2497+
case tree: untpd.Export => typedExport(tree, retrieveSym(tree))
24982498
case tree: untpd.Match => typedMatch(tree, pt)
24992499
case tree: untpd.Return => typedReturn(tree)
25002500
case tree: untpd.WhileDo => typedWhileDo(tree)

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2252,6 +2252,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
22522252
type Symbol = dotc.core.Symbols.Symbol
22532253

22542254
object Symbol extends SymbolModule:
2255+
def exportedMembers(tree: Export): List[Symbol] =
2256+
tree.tpe.termSymbol.denot.exportedMembers(tree)
2257+
22552258
def spliceOwner: Symbol = ctx.owner
22562259
def requiredPackage(path: String): Symbol = dotc.core.Symbols.requiredPackage(path)
22572260
def requiredClass(path: String): Symbol = dotc.core.Symbols.requiredClass(path)

library/src/scala/quoted/Quotes.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3437,6 +3437,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
34373437
/** Methods of the module object `val Symbol` */
34383438
trait SymbolModule { this: Symbol.type =>
34393439

3440+
/** All exported symbols from the prefix of an export tree */
3441+
def exportedMembers(tree: Export): List[Symbol]
3442+
34403443
/** Symbol of the definition that encloses the current splicing context.
34413444
*
34423445
* For example, the following call to `spliceOwner` would return the symbol `x`.

tests/run-macros/exports.check

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,12 @@ reflection show extractors:
1414
Inlined(None, Nil, Block(List(ValDef("Observer", TypeIdent("Observer$"), Some(Apply(Select(New(TypeIdent("Observer$")), "<init>"), Nil))), ClassDef("Observer$", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, Some(ValDef("_", Singleton(Ident("Observer")), None)), List(Export(Ident("Messages"), List(SimpleSelector(count))), DefDef("count", Nil, Nil, Inferred(), Some(Select(Ident("Messages"), "count")))))), Literal(Constant.Unit())))
1515
visited exports with splice
1616
visited exports with splice inverted
17+
[exported from NatModule within Nats$:
18+
val Zero: NatModule.Nat
19+
type Nat
20+
def Succ(pred: NatModule.Nat): NatModule.Nat
21+
]
22+
[exported from Messages within $anon:
23+
def logMessage(a: scala.Predef.String): scala.Unit [renamed to: log]
24+
def count: scala.Int
25+
]

tests/run-macros/exports/Macro_2.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ inline def visitExportsShow[T](inline x: T): Any = ${ visitExportsShowImpl('x) }
77
inline def visitExportsShowExtract[T](inline x: T): Any = ${ visitExportsShowExtractImpl('x) }
88
inline def visitExportsSplice(inline l: Logger): Logger = ${ mixinLoggerImpl('l) }
99
inline def visitExportsSpliceInverse(inline op: Logger => Logger): Logger = ${ mixinLoggerInverseImpl('op) }
10+
inline def visitExportsShowMembers[T](inline x: T): Any = ${ visitExportsShowMembersImpl('x) }
1011

1112
private def visitExportsExprMapImpl[T: Type](e: Expr[T], f: Expr[T => Any])(using Quotes): Expr[Any] =
1213
'{$f(${IdempotentExprMap.transform(e)})}
@@ -20,6 +21,35 @@ private def visitExportsShowImpl[T: Type](e: Expr[T])(using Quotes): Expr[Any] =
2021
import quotes.reflect._
2122
'{println(${Expr(Term.of(e).show)})}
2223

24+
private def visitExportsShowMembersImpl[T: Type](e: Expr[T])(using Quotes): Expr[Any] =
25+
import quotes.reflect._
26+
import collection.mutable
27+
28+
object ExportedMembers extends TreeAccumulator[mutable.Buffer[String]] {
29+
def foldTree(x: mutable.Buffer[String], tree: Tree)(owner: Symbol): mutable.Buffer[String] = tree match {
30+
case tree: Export =>
31+
32+
def showSym(symbol: Symbol, renames: Map[String, String]): String =
33+
renames.get(symbol.name) match
34+
case Some(alt) => s"${symbol.tree.show} [renamed to: $alt]"
35+
case none => symbol.tree.show
36+
37+
val renames = tree.selectors.collect {
38+
case RenameSelector(from, to) => from -> to
39+
}.toMap
40+
val exported = Symbol.exportedMembers(tree).map(showSym(_, renames)).mkString("\n ", "\n ", "\n")
41+
foldOverTree(x += s"[exported from ${tree.expr.show} within ${owner.name}:$exported]", tree)(owner)
42+
case _ => foldOverTree(x, tree)(owner)
43+
}
44+
}
45+
46+
val res =
47+
ExportedMembers.foldTree(mutable.Buffer.empty, Term.of(e))(Symbol.spliceOwner).mkString(", ")
48+
49+
'{ println(${Expr(res)}) }
50+
51+
end visitExportsShowMembersImpl
52+
2353
private def visitExportsShowExtractImpl[T: Type](e: Expr[T])(using Quotes): Expr[Any] =
2454
import quotes.reflect._
2555
'{println(${Expr(Term.of(e).showExtractors)})}

tests/run-macros/exports/Test_3.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,22 @@ object Messages {
2929
private val delegate = logger
3030
export delegate._
3131
}).log("visited exports with splice inverted")
32+
visitExportsShowMembers({
33+
object NatModule {
34+
35+
type Nat = Int
36+
37+
val Zero: Nat = 0
38+
39+
def Succ(pred: Nat): Nat = pred + 1
40+
41+
}
42+
object Nats {
43+
export NatModule._
44+
}
45+
})
46+
visitExportsShowMembers({
47+
new Logger {
48+
export Messages.{logMessage => log, _}
49+
}
50+
})

0 commit comments

Comments
 (0)