diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index cf2fc61fbf59..84853749a98a 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -513,11 +513,11 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => ctx.typer.checkClassType(tpe, tree.srcPos, traitReq = false, stablePrefixReq = stablePrefixReq, refinementOK = Feature.enabled(Feature.modularity)) - checkClassType(tree.tpe, true) + checkClassType(tree.tpe, stablePrefixReq = true) if !nu.tpe.isLambdaSub then // Check the constructor type as well; it could be an illegal singleton type // which would not be reflected as `tree.tpe` - checkClassType(nu.tpe, false) + checkClassType(nu.tpe, stablePrefixReq = false) Checking.checkInstantiable(tree.tpe, nu.tpe, nu.srcPos) withNoCheckNews(nu :: Nil)(app1) case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index bdb3e5f28400..532dd486074b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -12,7 +12,7 @@ import ast.desugar, ast.desugar.* import ProtoTypes.* import util.Spans.* import util.Property -import collection.mutable +import collection.mutable, mutable.ListBuffer import tpd.tpes import Variances.alwaysInvariant import config.{Config, Feature} @@ -1183,7 +1183,7 @@ class Namer { typer: Typer => * extension method. */ private def exportForwarders(exp: Export, pathMethod: Symbol)(using Context): List[tpd.MemberDef] = - val buf = new mutable.ListBuffer[tpd.MemberDef] + val buf = ListBuffer.empty[tpd.MemberDef] val Export(expr, selectors) = exp if expr.isEmpty then report.error(em"Export selector must have prefix and `.`", exp.srcPos) @@ -1509,12 +1509,17 @@ class Namer { typer: Typer => forwarders end exportForwarders - /** Add forwarders as required by the export statements in this class */ - private def processExports(using Context): Unit = + /** Add forwarders as required by the export statements in this class. + * @return true if forwarders were added + */ + private def processExports(using Context): Boolean = + + var exported = false def processExport(exp: Export, pathSym: Symbol)(using Context): Unit = for forwarder <- exportForwarders(exp, pathSym) do forwarder.symbol.entered + exported = true def exportPathSym(path: Tree, ext: ExtMethods)(using Context): Symbol = def fail(msg: String): Symbol = @@ -1557,6 +1562,8 @@ class Namer { typer: Typer => // import contexts for nothing. if hasExport(rest) then process(rest) + // was a forwarder entered for an export + exported end processExports /** Ensure constructor is completed so that any parameter accessors @@ -1778,8 +1785,9 @@ class Namer { typer: Typer => cls.setNoInitsFlags(parentsKind(parents), untpd.bodyKind(rest)) cls.setStableConstructor() enterParentRefinementSyms(parentRefinements.toList) - processExports(using localCtx) addConstructorProxies(cls) + if processExports(using localCtx) then + addConstructorProxies(cls) cleanup() } } @@ -1788,7 +1796,7 @@ class Namer { typer: Typer => private enum CanForward: case Yes case No(whyNot: String) - case Skip // for members that have never forwarders + case Skip // for members that never have forwarders class SuspendCompleter extends LazyType, SymbolLoaders.SecondCompleter { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 22c03f8317d6..dfa667573405 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -5055,7 +5055,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if qual.symbol.isAllOf(SyntheticMethod | Exported) then qual.symbol.owner.info.memberBasedOnFlags(qual.symbol.name.toTypeName, required = Exported) else NoDenotation - if exported.exists then exported.symbol.typeRef + if exported.exists then + qual.tpe match + case tp: NamedType => + exported.symbol.typeRef.asSeenFrom(tp.prefix, exported.symbol.owner) + case _ => + exported.symbol.typeRef else ctorResultType.underlyingClassRef(refinementOK = Feature.enabled(modularity)) typed( untpd.Select( diff --git a/tests/neg/i24879.scala b/tests/neg/i24879.scala new file mode 100644 index 000000000000..62a33512207f --- /dev/null +++ b/tests/neg/i24879.scala @@ -0,0 +1,8 @@ +class C: + class D + +class Baz: + val c = C() + export c.* + +@main def Test = Baz().D() // error (Baz#c : C) is not a valid class prefix, since it is not an immutable path diff --git a/tests/pos/i15944.scala b/tests/pos/i15944.scala new file mode 100644 index 000000000000..0b090bd41a54 --- /dev/null +++ b/tests/pos/i15944.scala @@ -0,0 +1,44 @@ +object Foo: + class Wrap(a: String): + def wow = s"$a - WOW!" + extension (s: String) + //private def wrap: Wrap = Wrap(s) // works + private def wrap = Wrap(s) // Not found: Wrap + export wrap.* // comment this line and it works + +class Bar: + class Wrap(a: String): + def wow = s"$a - WOW!" + val wrap = Wrap("string") // Not found: Wrap + export wrap.* + +object WorkingAlternative: + object Foo: + class Wrap(a: String): + def wow = s"$a - WOW!" + + object Bar: + import Foo.Wrap + extension (s: String) + private def wrap = Wrap(s) // works when not a member of enclosing element + export wrap.* + +class C: + class D + +class Baz: + val c = new C + export c.* + +class Bah: + val c = new C + type D = c.D + +@main def Test = + val bah = Bah() + println: + new bah.D + + val baz = Baz() + println: + baz.D() diff --git a/tests/warn/i24562.scala b/tests/warn/i24562.scala index c06657b51635..d025cd41155c 100644 --- a/tests/warn/i24562.scala +++ b/tests/warn/i24562.scala @@ -28,3 +28,15 @@ package aliased: import Wrap.* // warn! not a naming error because a benign alias val localBar = Bar() // not Wrap.Bar! val localNewBar = new Bar + +package i24879: + class C: + class D + + class Baz: + val c = new C + export c.* + + def test = + val baz = Baz() + baz.D() // no warn and no crash