diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 7f47f1696cd6..4fff850e0be7 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -930,7 +930,7 @@ object SymDenotations { def hasDefaultParams(using Context): Boolean = if ctx.erasedTypes then false else if is(HasDefaultParams) then true - else if is(NoDefaultParams) then false + else if is(NoDefaultParams) || !is(Method) then false else val result = rawParamss.nestedExists(_.is(HasDefault)) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 9398b50db5e5..835c8edb2b71 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1070,6 +1070,17 @@ class Namer { typer: Typer => else Yes } + def foreachDefaultGetterOf(sym: TermSymbol, op: TermSymbol => Unit): Unit = + var n = 0 + for params <- sym.paramSymss; param <- params do + if param.isTerm then + if param.is(HasDefault) then + val getterName = DefaultGetterName(sym.name, n) + val getter = path.tpe.member(DefaultGetterName(sym.name, n)).symbol + assert(getter.exists, i"$path does not have a default getter named $getterName") + op(getter.asTerm) + n += 1 + /** Add a forwarder with name `alias` or its type name equivalent to `mbr`, * provided `mbr` is accessible and of the right implicit/non-implicit kind. */ @@ -1092,6 +1103,7 @@ class Namer { typer: Typer => if canForward(mbr) == CanForward.Yes then val sym = mbr.symbol + val hasDefaults = sym.hasDefaultParams // compute here to ensure HasDefaultParams and NoDefaultParams flags are set val forwarder = if mbr.isType then val forwarderName = checkNoConflict(alias.toTypeName, isPrivate = false, span) @@ -1116,28 +1128,29 @@ class Namer { typer: Typer => (StableRealizable, ExprType(path.tpe.select(sym))) else (EmptyFlags, mbr.info.ensureMethodic) - var mbrFlags = Exported | Method | Final | maybeStable | sym.flags & RetainedExportFlags + var flagMask = RetainedExportFlags + if sym.isTerm then flagMask |= HasDefaultParams | NoDefaultParams + var mbrFlags = Exported | Method | Final | maybeStable | sym.flags & flagMask if sym.is(ExtensionMethod) then mbrFlags |= ExtensionMethod val forwarderName = checkNoConflict(alias, isPrivate = false, span) newSymbol(cls, forwarderName, mbrFlags, mbrInfo, coord = span) forwarder.info = avoidPrivateLeaks(forwarder) - forwarder.addAnnotations(sym.annotations) - - val forwarderDef = - if (forwarder.isType) tpd.TypeDef(forwarder.asType) - else { - import tpd._ - val ref = path.select(sym.asTerm) - val ddef = tpd.DefDef(forwarder.asTerm, prefss => - ref.appliedToArgss(adaptForwarderParams(Nil, sym.info, prefss)) - ) - if forwarder.isInlineMethod then - PrepareInlineable.registerInlineInfo(forwarder, ddef.rhs) - ddef - } + forwarder.addAnnotations(sym.annotations.filterConserve(_.symbol != defn.BodyAnnot)) - buf += forwarderDef.withSpan(span) + if forwarder.isType then + buf += tpd.TypeDef(forwarder.asType).withSpan(span) + else + import tpd._ + val ref = path.select(sym.asTerm) + val ddef = tpd.DefDef(forwarder.asTerm, prefss => + ref.appliedToArgss(adaptForwarderParams(Nil, sym.info, prefss))) + if forwarder.isInlineMethod then + PrepareInlineable.registerInlineInfo(forwarder, ddef.rhs) + buf += ddef.withSpan(span) + if hasDefaults then + foreachDefaultGetterOf(sym.asTerm, + getter => addForwarder(getter.name.asTermName, getter, span)) end addForwarder def addForwardersNamed(name: TermName, alias: TermName, span: Span): Unit = @@ -1161,11 +1174,15 @@ class Namer { typer: Typer => def isCaseClassSynthesized(mbr: Symbol) = fromCaseClass && defn.caseClassSynthesized.contains(mbr) for mbr <- path.tpe.membersBasedOnFlags(required = EmptyFlags, excluded = PrivateOrSynthetic) do - if !mbr.symbol.isSuperAccessor && !isCaseClassSynthesized(mbr.symbol) then - // Scala 2 superaccessors have neither Synthetic nor Artfact set, so we - // need to filter them out here (by contrast, Scala 3 superaccessors are Artifacts) - // Symbols from base traits of case classes that will get synthesized implementations - // at PostTyper are also excluded. + if !mbr.symbol.isSuperAccessor + // Scala 2 superaccessors have neither Synthetic nor Artfact set, so we + // need to filter them out here (by contrast, Scala 3 superaccessors are Artifacts) + // Symbols from base traits of case classes that will get synthesized implementations + // at PostTyper are also excluded. + && !isCaseClassSynthesized(mbr.symbol) + && !mbr.symbol.name.is(DefaultGetterName) + // default getters are exported with the members they belong to + then val alias = mbr.name.toTermName if mbr.symbol.is(Given) then if !seen.contains(alias) && mbr.matchesImportBound(givenBound) then diff --git a/tests/run/i14020.check b/tests/run/i14020.check new file mode 100644 index 000000000000..304f6c16f72a --- /dev/null +++ b/tests/run/i14020.check @@ -0,0 +1,7 @@ +Hello you +Hello John +Hello you +Hello you +Hello John +Hello you +bark: Woof! diff --git a/tests/run/i14020.scala b/tests/run/i14020.scala new file mode 100644 index 000000000000..86123874c034 --- /dev/null +++ b/tests/run/i14020.scala @@ -0,0 +1,39 @@ +class A: + def greeting(name: String = "you") = s"Hello $name" + +class A2: + inline def greeting(name: String = "you") = s"Hello $name" + +class B: + val a = A() + export a.* + +class C: + val a = A2() + export a.greeting + +@main def Test = + val b = B() + + println(b.a.greeting()) // works + println(b.greeting("John")) // works + println(b.greeting()) // nope ! + + val c = C() + + println(c.a.greeting()) // works + println(c.greeting("John")) // works + println(c.greeting()) // nope ! + + val w = Wolf() + import w.given + + println(summon[String]) // error: I found: w.bark(/* missing */summon[String]) + + +class Dog: + given bark(using msg: String = "Woof!"): String = s"bark: $msg" + +class Wolf: + private val dog = Dog() + export dog.given // needs to be `export dog.{given, *}` to export the default arguments