Skip to content

encode packages in tasty and signatures #14560

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

Closed
wants to merge 1 commit into from
Closed
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
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import java.nio.CharBuffer
import scala.io.Codec
import Int.MaxValue
import Names._, StdNames._, Contexts._, Symbols._, Flags._, NameKinds._, Types._
import SymDenotations.PrefixSeparator
import util.Chars.{isOperatorPart, digit2int}
import Definitions._
import nme._
Expand Down Expand Up @@ -141,7 +142,7 @@ object NameOps {
* followed by `kind` and the name.
*/
def expandedName(base: Symbol, kind: QualifiedNameKind = ExpandedName)(using Context): N =
likeSpacedN { base.fullNameSeparated(ExpandPrefixName, kind, name) }
likeSpacedN { base.fullNameSeparated(PrefixSeparator.ExpandPrefix, kind, name) }

/** Revert the expanded name. */
def unexpandedName: N = likeSpacedN {
Expand Down
36 changes: 23 additions & 13 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -455,17 +455,18 @@ object SymDenotations {
final def originalOwner(using Context): Symbol = initial.maybeOwner

/** The encoded full path name of this denotation, where outer names and inner names
* are separated by `separator` strings as indicated by the given name kind.
* are separated by `separator` strings as indicated by the given prefix separator.
* Drops package objects. Represents each term in the owner chain by a simple `_$`.
*/
def fullNameSeparated(kind: QualifiedNameKind)(using Context): Name =
maybeOwner.fullNameSeparated(kind, kind, name)
def fullNameSeparated(prefixSep: PrefixSeparator)(using Context): Name =
val doEncodeName = prefixSep.encodePackages && is(Package) && !isEffectiveRoot
maybeOwner.fullNameSeparated(prefixSep, prefixSep.kind, if doEncodeName then name.encode else name)

/** The encoded full path name of this denotation (separated by `prefixKind`),
/** The encoded full path name of this denotation (separated by `prefixSep.kind`),
* followed by the separator implied by `kind` and the given `name`.
* Drops package objects. Represents each term in the owner chain by a simple `_$`.
*/
def fullNameSeparated(prefixKind: QualifiedNameKind, kind: QualifiedNameKind, name: Name)(using Context): Name =
def fullNameSeparated(prefixSep: PrefixSeparator, kind: QualifiedNameKind, name: Name)(using Context): Name =
if (symbol == NoSymbol || isEffectiveRoot || kind == FlatName && is(PackageClass))
name
else {
Expand All @@ -475,7 +476,7 @@ object SymDenotations {
encl = encl.owner
filler += "_$"
}
var prefix = encl.fullNameSeparated(prefixKind)
var prefix = encl.fullNameSeparated(prefixSep)
if (kind.separator == "$")
// duplicate scalac's behavior: don't write a double '$$' for module class members.
prefix = prefix.exclude(ModuleClassName)
Expand All @@ -490,10 +491,13 @@ object SymDenotations {
}

/** The encoded flat name of this denotation, where joined names are separated by `separator` characters. */
def flatName(using Context): Name = fullNameSeparated(FlatName)
def flatName(using Context): Name = fullNameSeparated(PrefixSeparator.Flat)

/** `fullName` where `.' is the separator character */
def fullName(using Context): Name = fullNameSeparated(QualifiedName)
def fullName(using Context): Name = fullNameSeparated(PrefixSeparator.Full)

/** `fullName` where `.' is the separator character, and package names are encoded */
def tastyCompatibleFullName(using Context): Name = fullNameSeparated(PrefixSeparator.TastyCompatibleFull)

private var myTargetName: Name = null

Expand Down Expand Up @@ -1685,6 +1689,12 @@ object SymDenotations {
final def sealedDescendants(using Context): List[Symbol] = this.symbol :: sealedStrictDescendants
}

enum PrefixSeparator(final val kind: QualifiedNameKind, final val encodePackages: Boolean):
case TastyCompatibleFull extends PrefixSeparator(QualifiedName, encodePackages = true)
case Full extends PrefixSeparator(QualifiedName, encodePackages = false)
case Flat extends PrefixSeparator(FlatName, encodePackages = false)
case ExpandPrefix extends PrefixSeparator(ExpandPrefixName, encodePackages = false)

/** The contents of a class definition during a period
*/
class ClassDenotation private[SymDenotations] (
Expand All @@ -1701,7 +1711,7 @@ object SymDenotations {
// ----- caches -------------------------------------------------------

private var myTypeParams: List[TypeSymbol] = null
private var fullNameCache: SimpleIdentityMap[QualifiedNameKind, Name] = SimpleIdentityMap.empty
private var fullNameCache: SimpleIdentityMap[PrefixSeparator, Name] = SimpleIdentityMap.empty

private var myMemberCache: EqHashMap[Name, PreDenotation] = null
private var myMemberCachePeriod: Period = Nowhere
Expand Down Expand Up @@ -2252,12 +2262,12 @@ object SymDenotations {
}
}

override final def fullNameSeparated(kind: QualifiedNameKind)(using Context): Name = {
val cached = fullNameCache(kind)
override final def fullNameSeparated(prefixKind: PrefixSeparator)(using Context): Name = {
val cached = fullNameCache(prefixKind)
if (cached != null) cached
else {
val fn = super.fullNameSeparated(kind)
fullNameCache = fullNameCache.updated(kind, fn)
val fn = super.fullNameSeparated(prefixKind)
fullNameCache = fullNameCache.updated(prefixKind, fn)
fn
}
}
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -799,15 +799,15 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
sigName(defn.functionTypeErasure(sym))
else
val cls = normalizeClass(sym.asClass)
val fullName =
val tastyCompatibleFullName =
if !ctx.erasedTypes then
// It's important to use the initial symbol to compute the full name
// because the current symbol might have a different name or owner
// and signatures are required to be stable before erasure.
cls.initial.fullName
cls.initial.tastyCompatibleFullName
else
cls.fullName
fullName.asTypeName
cls.tastyCompatibleFullName
tastyCompatibleFullName.asTypeName
case tp: AppliedType =>
val sym = tp.tycon.typeSymbol
sigName( // todo: what about repeatedParam?
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ class TreePickler(pickler: TastyPickler) {
}
if (sym.is(Flags.Package)) {
writeByte(if (tpe.isType) TYPEREFpkg else TERMREFpkg)
pickleName(sym.fullName)
pickleName(sym.tastyCompatibleFullName)
}
else if (tpe.prefix == NoPrefix) {
writeByte(if (tpe.isType) TYPEREFdirect else TERMREFdirect)
Expand Down Expand Up @@ -397,7 +397,8 @@ class TreePickler(pickler: TastyPickler) {
|| tree.denot.asSingleDenotation.isRefinedMethod // refined methods have no defining class symbol
if selectFromQualifier then
writeByte(if name.isTypeName then SELECTtpt else SELECT)
pickleNameAndSig(name, sig, ename)
val isPackage = tree.symbol.is(Package)
pickleNameAndSig(if isPackage then name.encode else name, sig, ename)
pickleTree(qual)
else // select from owner
writeByte(SELECTin)
Expand Down
13 changes: 10 additions & 3 deletions compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,9 @@ class PlainPrinter(_ctx: Context) extends Printer {
ParamRefNameString(param.binder.paramNames(param.paramNum))

/** The name of the symbol without a unique id. */
protected def simpleNameString(sym: Symbol): String = nameString(sym.name)
protected def simpleNameString(sym: Symbol): String =
val doEncode = homogenizedView && sym.is(Package)
nameString(if doEncode then sym.name.encode else sym.name)

/** If -uniqid is set, the hashcode of the lambda type, after a # */
protected def lambdaHash(pt: LambdaType): Text =
Expand Down Expand Up @@ -308,7 +310,13 @@ class PlainPrinter(_ctx: Context) extends Printer {

protected def selectionString(tp: NamedType): String = {
val sym = if (homogenizedView) tp.symbol else tp.currentSymbol
if (sym.exists) nameString(sym) else nameString(tp.name)
if sym.exists then
if homogenizedView && sym.is(Flags.Package) && !sym.isEffectiveRoot then
nameString(sym.name.encode)
else
nameString(sym)
else
nameString(tp.name)
}

/** The string representation of this type used as a prefix */
Expand Down Expand Up @@ -674,4 +682,3 @@ class PlainPrinter(_ctx: Context) extends Printer {
protected def coloredText(text: Text, color: String): Text =
if (ctx.useColors) color ~ text ~ SyntaxHighlighting.NoColor else text
}

3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
else super.nameString(strippedName)

override protected def simpleNameString(sym: Symbol): String =
nameString(if (ctx.property(XprintMode).isEmpty) sym.initial.name else sym.name)
val theSym: SymDenotation = if (ctx.property(XprintMode).isEmpty) sym.initial else sym
nameString(if homogenizedView && theSym.is(Package) then theSym.name.encode else theSym.name)

override def fullNameString(sym: Symbol): String =
if !sym.exists || isEmptyPrefix(sym.effectiveOwner) then nameString(sym)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Flags._
import dotty.tools.dotc.core.NameKinds.FlatName
import dotty.tools.dotc.core.Names.Name
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.SymDenotations.PrefixSeparator
import dotty.tools.dotc.core.Denotations.staticRef
import dotty.tools.dotc.core.TypeErasure
import dotty.tools.dotc.core.Constants.Constant
Expand Down Expand Up @@ -508,7 +508,7 @@ object Splicer {
// Take the flatten name of the class and the full package name
val pack = tpe.classSymbol.topLevelClass.owner
val packageName = if (pack == defn.EmptyPackageClass) "" else s"${pack.fullName}."
packageName + tpe.classSymbol.fullNameSeparated(FlatName).toString
packageName + tpe.classSymbol.fullNameSeparated(PrefixSeparator.Flat).toString
}

val sym = param.classSymbol
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2483,7 +2483,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
else None

def name: String = self.denot.name.toString
def fullName: String = self.denot.fullName.toString
def fullName: String = self.denot.tastyCompatibleFullName.toString

def pos: Option[Position] =
if self.exists then Some(self.sourcePos) else None
Expand Down
6 changes: 6 additions & 0 deletions tests/neg/symbolic-packages/App.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package example

class Example:

def foo: symbolic_>> = ??? // error: No type found
def bar: _root_.symbolic_>> = ??? // ok - will "mock superclass"
3 changes: 3 additions & 0 deletions tests/neg/symbolic-packages/Lib.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package symbolic_>>

class |+|
6 changes: 6 additions & 0 deletions tests/pos/symbolic-packages-dep/App_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package example

class Example:

def inferred = new fully_+.symbolic_>>.package_*.|+|()
def explicit: fully_+.symbolic_>>.package_*.|+| = new fully_+.symbolic_>>.package_*.|+|()
3 changes: 3 additions & 0 deletions tests/pos/symbolic-packages-dep/Lib_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package fully_+.symbolic_>>.package_*

class |+|
6 changes: 6 additions & 0 deletions tests/pos/symbolic-packages/App.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package example

class Example:

def inferred = new fully_+.symbolic_>>.package_*.|+|()
def explicit: fully_+.symbolic_>>.package_*.|+| = new fully_+.symbolic_>>.package_*.|+|()
3 changes: 3 additions & 0 deletions tests/pos/symbolic-packages/Lib.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package fully_+.symbolic_>>.package_*

class |+|
2 changes: 2 additions & 0 deletions tests/run-macros/symbolic-packages.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fully_$plus.symbolic_$greater$greater.package_$times.|+|
fully_+.symbolic_>>.package_*.|+|
3 changes: 3 additions & 0 deletions tests/run-macros/symbolic-packages/Lib_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package fully_+.symbolic_>>.package_*

class |+|
4 changes: 4 additions & 0 deletions tests/run-macros/symbolic-packages/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@main def Test =
type Queried = fully_+.symbolic_>>.package_*.|+|
println(getFullName[Queried](decoded = false))
println(getFullName[Queried](decoded = true))
27 changes: 27 additions & 0 deletions tests/run-macros/symbolic-packages/macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import scala.quoted.*
import scala.reflect.NameTransformer

inline def getFullName[T](inline decoded: Boolean) = ${ Macro.getFullName[T]('decoded) }

object Macro:
def getFullName[T: Type](decodedExpr: Expr[Boolean])(using Quotes): Expr[String] =
import quotes.reflect.*

val decode = decodedExpr.valueOrAbort

val tpe = TypeRepr.of[T]

val cls = tpe.classSymbol match
case Some(cls) => cls
case _ =>
report.error("Not a class")
return '{"<?>"}

val name = cls.fullName

if decode then
val parts = name.split('.').toSeq
val decoded = (parts.init.map(NameTransformer.decode) :+ parts.last).mkString(".")
Expr(decoded)
else
Expr(name)