Skip to content

Commit 56c40f8

Browse files
committed
Export constructor proxies
When creating an exported type alias of a class that has a constructor proxy companion, export an alias for that companion with it. The alias is again a constructor proxy. Fixes scala#12299
1 parent be5043a commit 56c40f8

File tree

10 files changed

+136
-15
lines changed

10 files changed

+136
-15
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ object Flags {
465465
Module, Package, Deferred, Method, Case, Enum, Param, ParamAccessor,
466466
Scala2SpecialFlags, MutableOrOpen, Opaque, Touched, JavaStatic,
467467
OuterOrCovariant, LabelOrContravariant, CaseAccessor,
468-
Extension, NonMember, Implicit, Given, Permanent, Synthetic,
468+
Extension, NonMember, Implicit, Given, Permanent, Synthetic, Exported,
469469
SuperParamAliasOrScala2x, Inline, Macro, ConstructorProxy, Invisible)
470470

471471
/** Flags that are not (re)set when completing the denotation, or, if symbol is

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

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,15 @@ object NamerOps:
7777
val ApplyProxyFlags = Synthetic | ConstructorProxy | Inline | Method
7878

7979
/** Does symbol `cls` need constructor proxies to be generated? */
80-
def needsConstructorProxies(cls: Symbol)(using Context): Boolean =
81-
cls.isClass
82-
&& !cls.flagsUNSAFE.isOneOf(NoConstructorProxyNeededFlags)
83-
&& !cls.isAnonymousClass
80+
def needsConstructorProxies(sym: Symbol)(using Context): Boolean =
81+
sym.isClass
82+
&& !sym.flagsUNSAFE.isOneOf(NoConstructorProxyNeededFlags)
83+
&& !sym.isAnonymousClass
84+
||
85+
sym.isType && sym.is(Exported)
86+
&& sym.info.loBound.underlyingClassRef(refinementOK = false).match
87+
case tref: TypeRef => tref.prefix.isStable
88+
case _ => false
8489

8590
/** The completer of a constructor proxy apply method */
8691
class ApplyProxyCompleter(constr: Symbol)(using Context) extends LazyType:
@@ -114,7 +119,7 @@ object NamerOps:
114119
}.withSourceModule(modul)
115120

116121
/** A new symbol that is the constructor companion for class `cls` */
117-
def constructorCompanion(cls: ClassSymbol)(using Context): TermSymbol =
122+
def classConstructorCompanion(cls: ClassSymbol)(using Context): TermSymbol =
118123
val companion = newModuleSymbol(
119124
cls.owner, cls.name.toTermName,
120125
ConstructorCompanionFlags, ConstructorCompanionFlags,
@@ -125,6 +130,10 @@ object NamerOps:
125130
cls.registerCompanion(companion.moduleClass)
126131
companion
127132

133+
def typeConstructorCompanion(tsym: Symbol, prefix: Type, proxy: Symbol)(using Context): TermSymbol =
134+
newSymbol(tsym.owner, tsym.name.toTermName,
135+
ConstructorCompanionFlags | StableRealizable | Method, ExprType(prefix.select(proxy)), coord = tsym.coord)
136+
128137
/** Add all necesssary constructor proxy symbols for members of class `cls`. This means:
129138
*
130139
* - if a member is a class that needs a constructor companion, add one,
@@ -137,12 +146,21 @@ object NamerOps:
137146

138147
def memberExists(cls: ClassSymbol, name: TermName): Boolean =
139148
cls.baseClasses.exists(_.info.decls.lookupEntry(name) != null)
149+
140150
for mbr <- cls.info.decls do
141-
if needsConstructorProxies(mbr)
142-
&& !mbr.asClass.unforcedRegisteredCompanion.exists
143-
&& !memberExists(cls, mbr.name.toTermName)
144-
then
145-
constructorCompanion(mbr.asClass).entered
151+
if needsConstructorProxies(mbr) then
152+
mbr match
153+
case mbr: ClassSymbol =>
154+
if !mbr.unforcedRegisteredCompanion.exists
155+
&& !memberExists(cls, mbr.name.toTermName)
156+
then
157+
classConstructorCompanion(mbr).entered
158+
case _ =>
159+
mbr.info.loBound.underlyingClassRef(refinementOK = false) match
160+
case ref: TypeRef =>
161+
val proxy = ref.symbol.registeredCompanion
162+
if proxy.is(ConstructorProxy) && !memberExists(cls, mbr.name.toTermName) then
163+
typeConstructorCompanion(mbr, ref.prefix, proxy).entered
146164

147165
if cls.is(Module)
148166
&& needsConstructorProxies(cls.linkedClass)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ class TreePickler(pickler: TastyPickler) {
731731
if flags.is(Infix) then writeModTag(INFIX)
732732
if flags.is(Invisible) then writeModTag(INVISIBLE)
733733
if (flags.is(Erased)) writeModTag(ERASED)
734+
if (flags.is(Exported)) writeModTag(EXPORTED)
734735
if (isTerm) {
735736
if (flags.is(Implicit)) writeModTag(IMPLICIT)
736737
if (flags.is(Given)) writeModTag(GIVEN)
@@ -744,7 +745,6 @@ class TreePickler(pickler: TastyPickler) {
744745
if (flags.is(Extension)) writeModTag(EXTENSION)
745746
if (flags.is(ParamAccessor)) writeModTag(PARAMsetter)
746747
if (flags.is(SuperParamAlias)) writeModTag(PARAMalias)
747-
if (flags.is(Exported)) writeModTag(EXPORTED)
748748
assert(!(flags.is(Label)))
749749
}
750750
else {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ class Namer { typer: Typer =>
617617
val classSym = ctx.effectiveScope.lookup(className)
618618
val moduleName = className.toTermName
619619
if needsConstructorProxies(classSym) && ctx.effectiveScope.lookupEntry(moduleName) == null then
620-
enterSymbol(constructorCompanion(classSym.asClass))
620+
enterSymbol(classConstructorCompanion(classSym.asClass))
621621
else if ctx.owner.is(PackageClass) then
622622
for case cdef @ TypeDef(moduleName, _) <- moduleDef.values do
623623
val moduleSym = ctx.effectiveScope.lookup(moduleName)
@@ -634,12 +634,12 @@ class Namer { typer: Typer =>
634634
val moduleName = className.toTermName
635635
val companionVals = ctx.effectiveScope.lookupAll(moduleName.encode)
636636
if companionVals.isEmpty && needsConstructorProxies(classSym) then
637-
enterSymbol(constructorCompanion(classSym.asClass))
637+
enterSymbol(classConstructorCompanion(classSym.asClass))
638638
else
639639
for moduleSym <- companionVals do
640640
if moduleSym.is(Module) && !moduleSym.isDefinedInCurrentRun then
641641
val companion =
642-
if needsConstructorProxies(classSym) then constructorCompanion(classSym.asClass)
642+
if needsConstructorProxies(classSym) then classConstructorCompanion(classSym.asClass)
643643
else newModuleSymbol(
644644
ctx.owner, moduleName, EmptyFlags, EmptyFlags, (_, _) => NoType)
645645
enterSymbol(companion)

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ tuple-filter.scala
1616
i7740a.scala
1717
i7740b.scala
1818
i6507b.scala
19+
i12299a.scala
1920

2021
# Stale symbol: package object scala
2122
seqtype-cycle

tests/neg/i12299.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
object Outer {
2+
3+
object Inner {
4+
class Bar(x: Int)
5+
object Bar
6+
}
7+
8+
export Inner.Bar._
9+
10+
val _ = apply(2) // error (constructor proxies are not exported)
11+
12+
}
13+
object Outer2 {
14+
15+
object Inner {
16+
class Bar(x: Int)
17+
object Bar
18+
}
19+
20+
export Inner.Bar.apply // error: no eligible member
21+
22+
val _ = apply(2) // error (constructor proxies are not exported)
23+
24+
}

tests/pos/i12299.scala

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
object Outer0 {
2+
3+
object Inner {
4+
class Bar(x: Int):
5+
def this() = this(0)
6+
}
7+
8+
export Inner.Bar
9+
10+
val _ = Bar()
11+
val _ = Bar(2)
12+
13+
}
14+
15+
object Outer2 {
16+
17+
object Inner {
18+
class Bar(x: Int):
19+
def this() = this(0)
20+
}
21+
22+
object test2:
23+
export Inner._
24+
25+
val x = Bar()
26+
val y = Bar(2)
27+
28+
object test3:
29+
export Inner.Bar
30+
def Bar: () => String = () => ""
31+
val x = Bar()
32+
}
33+
34+
object Outer3 {
35+
export Outer0._
36+
37+
private val x = Bar()
38+
private val y = Bar(2)
39+
}
40+
41+
object Outer4 {
42+
43+
object Inner {
44+
class Bar(x: Int):
45+
def this() = this(0)
46+
object Bar
47+
}
48+
49+
export Inner._
50+
51+
val _ = Bar()
52+
val _ = Bar(2)
53+
54+
}

tests/pos/i12299/Outer_1.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object Outer {
2+
3+
object Inner {
4+
class Bar(x: Int):
5+
def this() = this(0)
6+
}
7+
8+
export Inner.Bar
9+
}

tests/pos/i12299/Test_2.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import Outer._
2+
3+
val x = Bar()
4+
val y = Bar(2)

tests/pos/i12299a.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
object Outer {
2+
3+
object Wrap {
4+
export Outer.Bar
5+
}
6+
7+
class Bar
8+
9+
val wrapBar = Wrap.Bar()
10+
}
11+

0 commit comments

Comments
 (0)