Skip to content

Commit 03fae3b

Browse files
committed
Experimental feature to alias a package
1 parent 9d7ba92 commit 03fae3b

File tree

8 files changed

+181
-10
lines changed

8 files changed

+181
-10
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett
275275
val YpickleWrite = StringSetting("-Ypickle-write", "directory|jar", "destination for generated .sig files containing type signatures.", "", None).internalOnly()
276276
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()
277277
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)
278+
val YaliasPackage = MultiStringSetting("-Yalias-package", "alias.name=underlying.name", "Alias package")
278279

279280
sealed abstract class CachePolicy(val name: String, val help: String)
280281
object CachePolicy {

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

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

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,22 @@ trait Analyzer extends AnyRef
4545
override val checkable = false
4646
override def keepsTypeParams = false
4747

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

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,8 @@ trait Contexts { self: Analyzer =>
12311231
else if (mt1 =:= mt2 && name.isTypeName && imp1Symbol.isMonomorphicType && imp2Symbol.isMonomorphicType) {
12321232
log(s"Suppressing ambiguous import: $mt1 =:= $mt2 && $imp1Symbol and $imp2Symbol are equivalent")
12331233
Some(imp1)
1234+
} else if (imp1Symbol == imp2Symbol && imp1Symbol.owner.isTopLevel) {
1235+
Some(imp1)
12341236
}
12351237
else {
12361238
log(s"""Import is genuinely ambiguous:
@@ -1393,7 +1395,10 @@ trait Contexts { self: Analyzer =>
13931395
else sym match {
13941396
case NoSymbol if inaccessible ne null => inaccessible
13951397
case NoSymbol => LookupNotFound
1396-
case _ => LookupSucceeded(qual, sym)
1398+
case _ =>
1399+
if (qual.symbol != null && qual.symbol.isPackage && qual.symbol.moduleClass != sym.owner)
1400+
qual.setSymbol(sym.owner.sourceModule)
1401+
LookupSucceeded(qual, sym)
13971402
}
13981403
)
13991404
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
@@ -51,6 +51,48 @@ trait Namers extends MethodSynthesis {
5151
case _ => false
5252
}
5353

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

@@ -370,7 +412,6 @@ trait Namers extends MethodSynthesis {
370412
case x => throw new MatchError(x)
371413
}
372414
val existing = pkgOwner.info.decls.lookup(pid.name)
373-
374415
if (existing.hasPackageFlag && pkgOwner == existing.owner)
375416
existing
376417
else {

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -553,13 +553,18 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
553553
private def makeAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): Any /*Type | (Tree, Type)*/ =
554554
if (!unit.isJava && context.isInPackageObject(sym, pre.typeSymbol)) {
555555
val qual = typedQualifier { atPos(tree.pos.makeTransparent) {
556+
def packageObject =
557+
if (!sym.isOverloaded && sym.owner.isModuleClass) sym.owner.sourceModule // historical optimization, perhaps no longer needed
558+
else pre.typeSymbol.packageObject
556559
tree match {
557560
case Ident(_) =>
558-
val packageObject =
559-
if (!sym.isOverloaded && sym.owner.isModuleClass) sym.owner.sourceModule // historical optimization, perhaps no longer needed
560-
else pre.typeSymbol.packageObject
561561
Ident(packageObject)
562-
case Select(qual, _) => Select(qual, nme.PACKAGEkw)
562+
case Select(qual, _) =>
563+
if (qual.symbol != sym.owner.owner) {
564+
Ident(packageObject)
565+
} else {
566+
Select(qual, nme.PACKAGEkw)
567+
}
563568
case SelectFromTypeTree(qual, _) => Select(qual, nme.PACKAGEkw)
564569
case x => throw new MatchError(x)
565570
}

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

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

0 commit comments

Comments
 (0)