diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 76cb45c1b28c..10d0bfb3c2e0 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -465,7 +465,7 @@ object Flags { Module, Package, Deferred, Method, Case, Enum, Param, ParamAccessor, Scala2SpecialFlags, MutableOrOpen, Opaque, Touched, JavaStatic, OuterOrCovariant, LabelOrContravariant, CaseAccessor, - Extension, NonMember, Implicit, Given, Permanent, Synthetic, + Extension, NonMember, Implicit, Given, Permanent, Synthetic, Exported, SuperParamAliasOrScala2x, Inline, Macro, ConstructorProxy, Invisible) /** Flags that are not (re)set when completing the denotation, or, if symbol is diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index ce525fc5d827..26dd19ebf56f 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -77,10 +77,15 @@ object NamerOps: val ApplyProxyFlags = Synthetic | ConstructorProxy | Inline | Method /** Does symbol `cls` need constructor proxies to be generated? */ - def needsConstructorProxies(cls: Symbol)(using Context): Boolean = - cls.isClass - && !cls.flagsUNSAFE.isOneOf(NoConstructorProxyNeededFlags) - && !cls.isAnonymousClass + def needsConstructorProxies(sym: Symbol)(using Context): Boolean = + sym.isClass + && !sym.flagsUNSAFE.isOneOf(NoConstructorProxyNeededFlags) + && !sym.isAnonymousClass + || + sym.isType && sym.is(Exported) + && sym.info.loBound.underlyingClassRef(refinementOK = false).match + case tref: TypeRef => tref.prefix.isStable + case _ => false /** The completer of a constructor proxy apply method */ class ApplyProxyCompleter(constr: Symbol)(using Context) extends LazyType: @@ -114,7 +119,7 @@ object NamerOps: }.withSourceModule(modul) /** A new symbol that is the constructor companion for class `cls` */ - def constructorCompanion(cls: ClassSymbol)(using Context): TermSymbol = + def classConstructorCompanion(cls: ClassSymbol)(using Context): TermSymbol = val companion = newModuleSymbol( cls.owner, cls.name.toTermName, ConstructorCompanionFlags, ConstructorCompanionFlags, @@ -125,6 +130,10 @@ object NamerOps: cls.registerCompanion(companion.moduleClass) companion + def typeConstructorCompanion(tsym: Symbol, prefix: Type, proxy: Symbol)(using Context): TermSymbol = + newSymbol(tsym.owner, tsym.name.toTermName, + ConstructorCompanionFlags | StableRealizable | Method, ExprType(prefix.select(proxy)), coord = tsym.coord) + /** Add all necesssary constructor proxy symbols for members of class `cls`. This means: * * - if a member is a class that needs a constructor companion, add one, @@ -137,12 +146,21 @@ object NamerOps: def memberExists(cls: ClassSymbol, name: TermName): Boolean = cls.baseClasses.exists(_.info.decls.lookupEntry(name) != null) + for mbr <- cls.info.decls do - if needsConstructorProxies(mbr) - && !mbr.asClass.unforcedRegisteredCompanion.exists - && !memberExists(cls, mbr.name.toTermName) - then - constructorCompanion(mbr.asClass).entered + if needsConstructorProxies(mbr) then + mbr match + case mbr: ClassSymbol => + if !mbr.unforcedRegisteredCompanion.exists + && !memberExists(cls, mbr.name.toTermName) + then + classConstructorCompanion(mbr).entered + case _ => + mbr.info.loBound.underlyingClassRef(refinementOK = false) match + case ref: TypeRef => + val proxy = ref.symbol.registeredCompanion + if proxy.is(ConstructorProxy) && !memberExists(cls, mbr.name.toTermName) then + typeConstructorCompanion(mbr, ref.prefix, proxy).entered if cls.is(Module) && needsConstructorProxies(cls.linkedClass) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 06ba83ae15bf..af66ccd3ecc4 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -731,6 +731,7 @@ class TreePickler(pickler: TastyPickler) { if flags.is(Infix) then writeModTag(INFIX) if flags.is(Invisible) then writeModTag(INVISIBLE) if (flags.is(Erased)) writeModTag(ERASED) + if (flags.is(Exported)) writeModTag(EXPORTED) if (isTerm) { if (flags.is(Implicit)) writeModTag(IMPLICIT) if (flags.is(Given)) writeModTag(GIVEN) @@ -744,7 +745,6 @@ class TreePickler(pickler: TastyPickler) { if (flags.is(Extension)) writeModTag(EXTENSION) if (flags.is(ParamAccessor)) writeModTag(PARAMsetter) if (flags.is(SuperParamAlias)) writeModTag(PARAMalias) - if (flags.is(Exported)) writeModTag(EXPORTED) assert(!(flags.is(Label))) } else { diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index f7e4d14c0ad7..59f2748982ad 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -617,7 +617,7 @@ class Namer { typer: Typer => val classSym = ctx.effectiveScope.lookup(className) val moduleName = className.toTermName if needsConstructorProxies(classSym) && ctx.effectiveScope.lookupEntry(moduleName) == null then - enterSymbol(constructorCompanion(classSym.asClass)) + enterSymbol(classConstructorCompanion(classSym.asClass)) else if ctx.owner.is(PackageClass) then for case cdef @ TypeDef(moduleName, _) <- moduleDef.values do val moduleSym = ctx.effectiveScope.lookup(moduleName) @@ -634,12 +634,12 @@ class Namer { typer: Typer => val moduleName = className.toTermName val companionVals = ctx.effectiveScope.lookupAll(moduleName.encode) if companionVals.isEmpty && needsConstructorProxies(classSym) then - enterSymbol(constructorCompanion(classSym.asClass)) + enterSymbol(classConstructorCompanion(classSym.asClass)) else for moduleSym <- companionVals do if moduleSym.is(Module) && !moduleSym.isDefinedInCurrentRun then val companion = - if needsConstructorProxies(classSym) then constructorCompanion(classSym.asClass) + if needsConstructorProxies(classSym) then classConstructorCompanion(classSym.asClass) else newModuleSymbol( ctx.owner, moduleName, EmptyFlags, EmptyFlags, (_, _) => NoType) enterSymbol(companion) diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 423c429d19e0..4e4609cf34f4 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -16,6 +16,7 @@ tuple-filter.scala i7740a.scala i7740b.scala i6507b.scala +i12299a.scala # Stale symbol: package object scala seqtype-cycle diff --git a/tests/neg/i12299.scala b/tests/neg/i12299.scala new file mode 100644 index 000000000000..424be6cc4fd2 --- /dev/null +++ b/tests/neg/i12299.scala @@ -0,0 +1,24 @@ +object Outer { + + object Inner { + class Bar(x: Int) + object Bar + } + + export Inner.Bar._ + + val _ = apply(2) // error (constructor proxies are not exported) + +} +object Outer2 { + + object Inner { + class Bar(x: Int) + object Bar + } + + export Inner.Bar.apply // error: no eligible member + + val _ = apply(2) // error (constructor proxies are not exported) + +} diff --git a/tests/pos/i12299.scala b/tests/pos/i12299.scala new file mode 100644 index 000000000000..cabd12ed3b05 --- /dev/null +++ b/tests/pos/i12299.scala @@ -0,0 +1,54 @@ +object Outer0 { + + object Inner { + class Bar(x: Int): + def this() = this(0) + } + + export Inner.Bar + + val _ = Bar() + val _ = Bar(2) + +} + +object Outer2 { + + object Inner { + class Bar(x: Int): + def this() = this(0) + } + + object test2: + export Inner._ + + val x = Bar() + val y = Bar(2) + + object test3: + export Inner.Bar + def Bar: () => String = () => "" + val x = Bar() +} + +object Outer3 { + export Outer0._ + + private val x = Bar() + private val y = Bar(2) +} + +object Outer4 { + + object Inner { + class Bar(x: Int): + def this() = this(0) + object Bar + } + + export Inner._ + + val _ = Bar() + val _ = Bar(2) + +} diff --git a/tests/pos/i12299/Outer_1.scala b/tests/pos/i12299/Outer_1.scala new file mode 100644 index 000000000000..200c21beecb8 --- /dev/null +++ b/tests/pos/i12299/Outer_1.scala @@ -0,0 +1,9 @@ +object Outer { + + object Inner { + class Bar(x: Int): + def this() = this(0) + } + + export Inner.Bar +} diff --git a/tests/pos/i12299/Test_2.scala b/tests/pos/i12299/Test_2.scala new file mode 100644 index 000000000000..ce4c9390744d --- /dev/null +++ b/tests/pos/i12299/Test_2.scala @@ -0,0 +1,4 @@ +import Outer._ + +val x = Bar() +val y = Bar(2) diff --git a/tests/pos/i12299a.scala b/tests/pos/i12299a.scala new file mode 100644 index 000000000000..90819f0a0bca --- /dev/null +++ b/tests/pos/i12299a.scala @@ -0,0 +1,11 @@ +object Outer { + + object Wrap { + export Outer.Bar + } + + class Bar + + val wrapBar = Wrap.Bar() +} +