Skip to content

Commit cc3fade

Browse files
committed
Experimental feature to alias a package
1 parent e3c1d41 commit cc3fade

File tree

8 files changed

+175
-9
lines changed

8 files changed

+175
-9
lines changed

src/compiler/scala/tools/nsc/settings/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett
265265
val YpickleWrite = StringSetting("-Ypickle-write", "directory|jar", "destination for generated .sig files containing type signatures.", "", None).internalOnly()
266266
val YpickleWriteApiOnly = BooleanSetting("-Ypickle-write-api-only", "Exclude private members (other than those material to subclass compilation, such as private trait vals) from generated .sig files containing type signatures.").internalOnly()
267267
val YtrackDependencies = BooleanSetting("-Ytrack-dependencies", "Record references to in unit.depends. Deprecated feature that supports SBT 0.13 with incOptions.withNameHashing(false) only.", default = true)
268+
val YaliasPackage = MultiStringSetting("-Yalias-package", "alias.name=underlying.name", "Alias package")
268269

269270
sealed abstract class CachePolicy(val name: String, val help: String)
270271
object CachePolicy {

src/compiler/scala/tools/nsc/transform/Erasure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ abstract class Erasure extends InfoTransform
746746
} else if (isMethodTypeWithEmptyParams(qual1.tpe)) { // see also adaptToType in TypeAdapter
747747
assert(qual1.symbol.isStable, qual1.symbol)
748748
adaptMember(selectFrom(applyMethodWithEmptyParams(qual1)))
749-
} else if (!qual1.isInstanceOf[Super] && (!isJvmAccessible(qual1.tpe.typeSymbol, context) || !qual1.tpe.typeSymbol.isSubClass(tree.symbol.owner))) {
749+
} else if (!tree.symbol.isTopLevel && !qual1.isInstanceOf[Super] && (!isJvmAccessible(qual1.tpe.typeSymbol, context) || !qual1.tpe.typeSymbol.isSubClass(tree.symbol.owner))) {
750750
// A selection requires a cast:
751751
// - In `(foo: Option[String]).get.trim`, the qualifier has type `Object`. We cast
752752
// to the owner of `trim` (`String`), unless the owner is a non-accessible Java

src/compiler/scala/tools/nsc/typechecker/Analyzer.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,19 @@ trait Analyzer extends AnyRef
4444
override val checkable = false
4545
override def keepsTypeParams = false
4646

47+
override def run(): Unit = {
48+
val aliases: List[String] = settings.YaliasPackage.value
49+
aliases.foreach {
50+
alias =>
51+
alias.split('=').toList match {
52+
case a :: b :: Nil =>
53+
processPackageAliases(a, b)
54+
case _ =>
55+
globalError(s"Cannot parse value for ${settings.YaliasPackage}: $alias")
56+
}
57+
}
58+
super.run()
59+
}
4760
def apply(unit: CompilationUnit) {
4861
newNamer(rootContext(unit)).enterSym(unit.body)
4962
}

src/compiler/scala/tools/nsc/typechecker/Contexts.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,8 @@ trait Contexts { self: Analyzer =>
10161016
else if (mt1 =:= mt2 && name.isTypeName && imp1Symbol.isMonomorphicType && imp2Symbol.isMonomorphicType) {
10171017
log(s"Suppressing ambiguous import: $mt1 =:= $mt2 && $imp1Symbol and $imp2Symbol are equivalent")
10181018
Some(imp1)
1019+
} else if (imp1Symbol == imp2Symbol && imp1Symbol.owner.isTopLevel) {
1020+
Some(imp1)
10191021
}
10201022
else {
10211023
log(s"Import is genuinely ambiguous:\n " + characterize)
@@ -1145,7 +1147,10 @@ trait Contexts { self: Analyzer =>
11451147
else sym match {
11461148
case NoSymbol if inaccessible ne null => inaccessible
11471149
case NoSymbol => LookupNotFound
1148-
case _ => LookupSucceeded(qual, sym)
1150+
case _ =>
1151+
if (qual.symbol != null && qual.symbol.isPackage && qual.symbol.moduleClass != sym.owner)
1152+
qual.setSymbol(sym.owner.sourceModule)
1153+
LookupSucceeded(qual, sym)
11491154
}
11501155
)
11511156
def finishDefSym(sym: Symbol, pre0: Type): NameLookup =

src/compiler/scala/tools/nsc/typechecker/Namers.scala

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,48 @@ trait Namers extends MethodSynthesis {
5353
case _ => false
5454
}
5555

56+
def processPackageAliases(package1: String, package2: String): Unit = {
57+
class Pack(sym: Symbol) {
58+
val packageClassSym: Symbol = sym.moduleClass
59+
val declsList = packageClassSym.info.decls.toList
60+
val (packDecls, nonPackDecls) = declsList.partition(_.hasPackageFlag)
61+
val packDeclNames = packDecls.map(_.name).toSet
62+
def enter(sym: Symbol): Unit = {
63+
val scope = packageClassSym.info.decls
64+
if (scope.lookupSymbolEntry(sym) == null) {
65+
scope.enter(sym)
66+
}
67+
}
68+
}
69+
def smoosh(p1: Pack, p2: Pack): Unit = {
70+
p2.nonPackDecls.foreach(p1.enter(_))
71+
72+
val sharedNames = p1.packDeclNames.intersect(p2.packDeclNames)
73+
p1.packDecls.foreach { p1Decl =>
74+
p2.packDecls.find(_.name == p1Decl.name) match {
75+
case None =>
76+
case Some(p2Decl) =>
77+
smoosh(new Pack(p1Decl), new Pack(p2Decl))
78+
}
79+
}
80+
p2.packDecls.foreach { p2Decl =>
81+
if (sharedNames.contains(p2Decl.name)) {
82+
val p1Decl = p1.packDecls.find(_.name == p2Decl.name).get
83+
smoosh(new Pack(p1Decl), new Pack(p2Decl))
84+
} else {
85+
val dummySelect = Select(gen.mkAttributedRef(p1.packageClassSym.sourceModule), p2Decl.name)
86+
val p2DeclClone = newNamer(NoContext.make(EmptyTree, RootClass)).createPackageSymbol(NoPosition, dummySelect)
87+
p1.enter(p2DeclClone)
88+
}
89+
}
90+
}
91+
val package1Sym = rootMirror.getPackageIfDefined(package1)
92+
val package2Sym = rootMirror.getPackageIfDefined(package2)
93+
// for now require both packages to be defined
94+
if (package1Sym != NoSymbol && package2Sym != NoSymbol)
95+
smoosh(new Pack(package1Sym), new Pack(package2Sym))
96+
}
97+
5698
private class NormalNamer(context: Context) extends Namer(context)
5799
def newNamer(context: Context): Namer = new NormalNamer(context)
58100

@@ -362,7 +404,6 @@ trait Namers extends MethodSynthesis {
362404
case Select(qual: RefTree, _) => createPackageSymbol(pos, qual).moduleClass
363405
}
364406
val existing = pkgOwner.info.decls.lookup(pid.name)
365-
366407
if (existing.hasPackageFlag && pkgOwner == existing.owner)
367408
existing
368409
else {

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -571,13 +571,18 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
571571
}
572572
}
573573
val qual = typedQualifier { atPos(tree.pos.makeTransparent) {
574+
def packageObject =
575+
if (!sym.isOverloaded && sym.owner.isModuleClass) sym.owner.sourceModule // historical optimization, perhaps no longer needed
576+
else pre.typeSymbol.packageObject
574577
tree match {
575578
case Ident(_) =>
576-
val packageObject =
577-
if (!sym.isOverloaded && sym.owner.isModuleClass) sym.owner.sourceModule // historical optimization, perhaps no longer needed
578-
else pre.typeSymbol.packageObject
579579
Ident(packageObject)
580-
case Select(qual, _) => Select(qual, nme.PACKAGEkw)
580+
case Select(qual, _) =>
581+
if (qual.symbol != sym.owner.owner) {
582+
Ident(packageObject)
583+
} else {
584+
Select(qual, nme.PACKAGEkw)
585+
}
581586
case SelectFromTypeTree(qual, _) => Select(qual, nme.PACKAGEkw)
582587
}
583588
}}

src/reflect/scala/reflect/internal/Symbols.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2647,8 +2647,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
26472647
else if (isAnonymousClass) ("anonymous class", "anonymous class", "AC")
26482648
else if (isRefinementClass) ("refinement class", "", "RC")
26492649
else if (isJavaAnnotation) ("Java annotation", "Java annotation", "JANN")
2650-
else if (isJavaEnum
2651-
|| companion.isJavaEnum) ("Java enumeration", "Java enum", "JENUM")
2650+
// else if (isJavaEnum
2651+
// || companion.isJavaEnum) ("Java enumeration", "Java enum", "JENUM")
26522652
else if (isJava && isModule) ("Java module", "class", "JMOD")
26532653
else if (isJava && isModuleClass) ("Java module class", "class", "JMODC")
26542654
else if (isModule) ("module", "object", "MOD")
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package scala.tools.nsc
2+
3+
import java.nio.file.Files
4+
5+
object PackageSmoosherTest {
6+
def main(args: Array[String]): Unit = {
7+
val g = new Global(new Settings)
8+
g.settings.usejavacp.value = true
9+
import g._
10+
val tmpDir = Files.createTempDirectory("test-classes-")
11+
12+
val code =
13+
s"""
14+
package o {
15+
package platform {
16+
package a {
17+
class A
18+
object `package` {}
19+
object SomeObject
20+
}
21+
object `package` {
22+
implicit def foo: a.A = null
23+
}
24+
object SomeObject
25+
}
26+
package prime {
27+
package b {
28+
class B
29+
}
30+
package a {
31+
class A2
32+
}
33+
package q {
34+
class Query
35+
}
36+
class Query
37+
}
38+
}
39+
"""
40+
41+
def compile(enabled: Boolean)(code: String) = {
42+
settings.Xprint.value = List("all")
43+
settings.outdir.value = tmpDir.toAbsolutePath.toString
44+
if (enabled)
45+
settings.YaliasPackage.value = "o.prime=o.platform" :: Nil
46+
reporter.reset()
47+
val r = new Run
48+
r.compileSources(newSourceFile(code) :: Nil)
49+
assert(!reporter.hasErrors)
50+
}
51+
52+
compile(enabled = false)(code)
53+
compile(enabled = true)(
54+
"""
55+
| package client {
56+
| object Client {
57+
| new o.platform.a.A
58+
| new o.prime.a.A
59+
| o.prime.SomeObject
60+
| o.prime.a.SomeObject
61+
| }
62+
| }
63+
|""".stripMargin)
64+
compile(enabled = true)(
65+
"""
66+
|import o.platform._
67+
|import o.prime._
68+
|class Test {
69+
| foo
70+
| implicitly[o.platform.a.A]
71+
|}
72+
|""".stripMargin)
73+
compile(enabled = true)(
74+
"""
75+
|package o.platform.bt
76+
|object `package` {
77+
|}
78+
|object O1
79+
|trait T1
80+
|""".stripMargin
81+
)
82+
compile(enabled = true)(
83+
"""
84+
|package o.prime.bt
85+
|
86+
|class C2
87+
|""".stripMargin
88+
)
89+
90+
compile(enabled = true)(
91+
"""
92+
|import o.platform._
93+
|import o.prime.q._
94+
|
95+
|class Test extends Query {
96+
|
97+
|}
98+
|""".stripMargin
99+
)
100+
}
101+
}

0 commit comments

Comments
 (0)