scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.83k stars 1.05k forks source link

Multi-module export of methods: default parameters are not honoured #18767

Open benhutchison opened 11 months ago

benhutchison commented 11 months ago

Compiler version

3.3.1

Minimized code

Problem only seems to show when compiling incrementally, ie across multiple SBT subprojects

https://github.com/benhutchison/export_default_param_repro

See also initial discussion at https://users.scala-lang.org/t/does-multi-module-incremental-compilation-affect-scala-3-compiler-semantics-behavior/9576/4

Output

[error] -- [E171] Type Error: /Users/ben/workspace/export_default_param_repro/module2/Module2.scala:1:22
[error] 1 |val x = Module1.method()
[error]   |        ^^^^^^^^^^^^^^^^
[error]   |missing argument for parameter param of method method in object Module1: (param: String): Unit
[error] one error found

Expectation

Code compiles correctly

sjrd commented 11 months ago

Vulpix-style minimization:

// Exporter_1.scala

object Exporter:
  export Exportee.*

object Exportee:
  def method(param: String = "defaultValue"): Unit = ???
// Test_2.scala

object Test:
  val x = Exporter.method()

  val y = Exportee.method()
end Test

Output:

[E171] Type Error: tests/pos/i18767/Test_2.scala:2:25 ---------------------------------------------------------------
2 |  val x = Exporter.method()
  |          ^^^^^^^^^^^^^^^^^
  |          missing argument for parameter param of method method in object Exporter: (param: String): Unit
bishabosha commented 8 months ago

So the default argument is exported as can be seen:

~/workspace/scripts/qux » cs launch scalac:3.3.1 -- -d out -Xprint:typer -Ydebug-flags Exporter_1.scala
[[syntax trees at end of                     typer]] // Exporter_1.scala
package <empty> {
  final lazy module <stable> <touched> val Exporter: Exporter = new Exporter()
  final module <touched> class Exporter <method> <touched>() extends Object() {
    this: Exporter.type =>
    export Exportee.*
    final <method> exported <has-default-params> def method(
      <param> param: String): Unit = Exportee.method(param)
    final <method> exported <no-default-params> def method$default$1:
      String @uncheckedVariance = Exportee.method$default$1
  }
  final lazy module <stable> <touched> val Exportee: Exportee = new Exportee()
  final module <noinits> <touched> class Exportee <method> <stable> <touched>()
     extends Object() { this: Exportee.type =>
    <method> <touched> <has-default-params> def method(
      <param> <hasdefault> <touched> param: String): Unit = ???
    <method> <touched> <no-default-params> def method$default$1:
      String @uncheckedVariance = "defaultValue"
  }
}

it seems that only in a second compilation run (and not in REPL) that the compiler doesn't find the default argument - it is probably because Exporter.method()#param doesnt have the <hasdefault> flag