From fea3bf930d76aebec7e83180e9f7ecbadd65513e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 28 Sep 2017 10:16:29 +0200 Subject: [PATCH] Fix #3171: Fix handling default getters in secondary constructors Previous logic would place them in the enclosing class instead of in the companion object where they belong. Also, define position for error that checks against multiple overloaded alternatives with default arguments. --- .../src/dotty/tools/dotc/ast/Desugar.scala | 26 +++++++++++++------ .../src/dotty/tools/dotc/typer/Checking.scala | 2 +- tests/neg/i3171.scala | 10 +++++++ tests/pos/i3171.scala | 10 +++++++ 4 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 tests/neg/i3171.scala create mode 100644 tests/pos/i3171.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 47ad30e0ea5b..644b7620ae06 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -149,7 +149,7 @@ object desugar { * def f$default$1[T] = 1 * def f$default$2[T](x: Int) = x + "m" */ - def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(implicit ctx: Context): Tree = { + private def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(implicit ctx: Context): Tree = { val DefDef(name, tparams, vparamss, tpt, rhs) = meth val mods = meth.mods val epbuf = new ListBuffer[ValDef] @@ -254,11 +254,17 @@ object desugar { .withFlags((mods.flags & AccessFlags).toCommonFlags) .withMods(Nil) - val (constr1, defaultGetters) = defDef(constr0, isPrimaryConstructor = true) match { - case meth: DefDef => (meth, Nil) - case Thicket((meth: DefDef) :: defaults) => (meth, defaults) + var defaultGetters: List[Tree] = Nil + + def decompose(ddef: Tree): DefDef = ddef match { + case meth: DefDef => meth + case Thicket((meth: DefDef) :: defaults) => + defaultGetters = defaults + meth } + val constr1 = decompose(defDef(constr0, isPrimaryConstructor = true)) + // The original type and value parameters in the constructor already have the flags // needed to be type members (i.e. param, and possibly also private and local unless // prefixed by type or val). `tparams` and `vparamss` are the type parameters that @@ -299,9 +305,11 @@ object desugar { // to auxiliary constructors val normalizedBody = impl.body map { case ddef: DefDef if ddef.name.isConstructorName => - addEvidenceParams( - cpy.DefDef(ddef)(tparams = constrTparams), - evidenceParams(constr1).map(toDefParam)) + decompose( + defDef( + addEvidenceParams( + cpy.DefDef(ddef)(tparams = constrTparams), + evidenceParams(constr1).map(toDefParam)))) case stat => stat } @@ -680,7 +688,9 @@ object desugar { def defTree(tree: Tree)(implicit ctx: Context): Tree = tree match { case tree: ValDef => valDef(tree) case tree: TypeDef => if (tree.isClassDef) classDef(tree) else tree - case tree: DefDef => defDef(tree) + case tree: DefDef => + if (tree.name.isConstructorName) tree // was already handled by enclosing classDef + else defDef(tree) case tree: ModuleDef => moduleDef(tree) case tree: PatDef => patDef(tree) } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 6ee57d5cd220..3a143f826ea4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -625,7 +625,7 @@ trait Checking { else doubleDefError(decl, other) } if ((decl is HasDefaultParams) && (other is HasDefaultParams)) { - ctx.error(em"two or more overloaded variants of $decl have default arguments") + ctx.error(em"two or more overloaded variants of $decl have default arguments", decl.pos) decl resetFlag HasDefaultParams } } diff --git a/tests/neg/i3171.scala b/tests/neg/i3171.scala new file mode 100644 index 000000000000..3034e020d61c --- /dev/null +++ b/tests/neg/i3171.scala @@ -0,0 +1,10 @@ +object Test { + class C(x: Int = 1, y: Int) { + def this(x: Int = 1)(y: String) = // error: two or more overloaded methods have default getters + this(x, y.toInt) + } + + def test: Unit = { + new C()("1") // error: missing argument + } +} diff --git a/tests/pos/i3171.scala b/tests/pos/i3171.scala new file mode 100644 index 000000000000..fbc7d9700a35 --- /dev/null +++ b/tests/pos/i3171.scala @@ -0,0 +1,10 @@ +object Test { + class C(x: Int, y: Int) { + def this(x: Int = 1)(y: String) = + this(x, y.toInt) + } + + def test: Unit = { + new C()("1") + } +}