Skip to content

Respect export alias for default arg forwarder #21109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1223,20 +1223,21 @@ class Namer { typer: Typer =>
Yes
}

def foreachDefaultGetterOf(sym: TermSymbol, op: TermSymbol => Unit): Unit =
def foreachDefaultGetterOf(sym: TermSymbol, alias: TermName)(op: (TermSymbol, TermName) => Unit): Unit =
var n = 0
val methodName =
if sym.name == nme.apply && sym.is(Synthetic) && sym.owner.companionClass.is(Case) then
// The synthesized `apply` methods of case classes use the constructor's default getters
nme.CONSTRUCTOR
else sym.name
// The synthesized `apply` methods of case classes use the constructor's default getters
val useConstructor = sym.name == nme.apply && sym.is(Synthetic) && sym.owner.companionClass.is(Case)
val methodName = if useConstructor then nme.CONSTRUCTOR else sym.name
val aliasedName = if useConstructor then nme.CONSTRUCTOR else alias
val useAliased = !useConstructor && methodName != aliasedName
for params <- sym.paramSymss; param <- params do
if param.isTerm then
if param.is(HasDefault) then
val getterName = DefaultGetterName(methodName, n)
val getter = pathType.member(getterName).symbol
assert(getter.exists, i"$path does not have a default getter named $getterName")
op(getter.asTerm)
val targetName = if useAliased then DefaultGetterName(aliasedName, n) else getterName
op(getter.asTerm, targetName)
n += 1

/** Add a forwarder with name `alias` or its type name equivalent to `mbr`,
Expand Down Expand Up @@ -1358,9 +1359,8 @@ class Namer { typer: Typer =>
})
buf += ddef.withSpan(span)
if hasDefaults then
foreachDefaultGetterOf(sym.asTerm,
getter => addForwarder(
getter.name.asTermName, getter.asSeenFrom(path.tpe), span))
foreachDefaultGetterOf(sym.asTerm, alias): (getter, getterName) =>
addForwarder(getterName, getter.asSeenFrom(path.tpe), span)

// adding annotations and flags at the parameter level
// TODO: This probably needs to be filtered to avoid adding some annotation
Expand Down Expand Up @@ -1415,13 +1415,13 @@ class Namer { typer: Typer =>
addWildcardForwardersNamed(alias, span)

def addForwarders(sels: List[untpd.ImportSelector], seen: List[TermName]): Unit = sels match
case sel :: sels1 =>
case sel :: sels =>
if sel.isWildcard then
addWildcardForwarders(seen, sel.span)
else
if !sel.isUnimport then
addForwardersNamed(sel.name, sel.rename, sel.span)
addForwarders(sels1, sel.name :: seen)
addForwarders(sels, sel.name :: seen)
case _ =>

/** Avoid a clash of export forwarder `forwarder` with other forwarders in `forwarders`.
Expand Down
34 changes: 34 additions & 0 deletions tests/run/i19587.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
case class Foo(bar: String, baz: Int)
object Foo:
def withDefaults(bar: String = "", baz: Int = 42) = Foo(bar, baz)

object Test1:
export Foo.withDefaults

object Test2:
export Foo.withDefaults as fooWithDefaults

class Bar:
infix def bar(other: Bar) = 42

object Baz:
val b = Bar()
export b.bar
export b.bar as baz

@main def Test =
// this works
assert:
Test1.withDefaults("test1") == Foo("test1", 42)

// this doesn't work
assert:
Test2.fooWithDefaults("test2") == Foo("test2", 42)

val b = Bar()
println:
b bar Bar()
println:
Baz bar Bar()
println:
Baz baz Bar()
Loading