From 62531258003dba513a21847d58395823d6840363 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 14 Jul 2016 15:01:00 +0200 Subject: [PATCH 1/4] Changes to overloading Fix #1381: Overloading is now changed so that we first try without implicit searches. Only if that leaves no applicable alternatives we try again with implicit search turned on. This also fixes test case t2660, which got moved from neg to pos. --- src/dotty/tools/dotc/ast/tpd.scala | 3 +- src/dotty/tools/dotc/typer/Applications.scala | 138 +++++++++--------- tests/{neg => pos}/t2660.scala | 8 +- 3 files changed, 73 insertions(+), 76 deletions(-) rename tests/{neg => pos}/t2660.scala (60%) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 4593b9554346..2b0e63a19f60 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -854,8 +854,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { var allAlts = denot.alternatives .map(_.termRef).filter(tr => typeParamCount(tr) == targs.length) if (targs.isEmpty) allAlts = allAlts.filterNot(_.widen.isInstanceOf[PolyType]) - val alternatives = - ctx.typer.resolveOverloaded(allAlts, proto, Nil) + val alternatives = ctx.typer.resolveOverloaded(allAlts, proto) assert(alternatives.size == 1, i"${if (alternatives.isEmpty) "no" else "multiple"} overloads available for " + i"$method on ${receiver.tpe.widenDealias} with targs: $targs%, %; args: $args%, % of types ${args.tpes}%, %; expectedType: $expectedType." + diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index aba073f3daf5..7825d6b26ef0 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -1039,31 +1039,7 @@ trait Applications extends Compatibility { self: Typer => * to form the method type. * todo: use techniques like for implicits to pick candidates quickly? */ - def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type] = Nil)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { - - def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty - - /** The shape of given tree as a type; cannot handle named arguments. */ - def typeShape(tree: untpd.Tree): Type = tree match { - case untpd.Function(args, body) => - defn.FunctionOf(args map Function.const(defn.AnyType), typeShape(body)) - case _ => - defn.NothingType - } - - /** The shape of given tree as a type; is more expensive than - * typeShape but can can handle named arguments. - */ - def treeShape(tree: untpd.Tree): Tree = tree match { - case NamedArg(name, arg) => - val argShape = treeShape(arg) - cpy.NamedArg(tree)(name, argShape).withType(argShape.tpe) - case _ => - dummyTreeOfType(typeShape(tree)) - } - - def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] = - alts filter (isApplicable(_, argTypes, resultType)) + def resolveOverloaded(alts: List[TermRef], pt: Type)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { /** Is `alt` a method or polytype whose result type after the first value parameter * section conforms to the expected type `resultType`? If `resultType` @@ -1092,23 +1068,63 @@ trait Applications extends Compatibility { self: Typer => * probability of pruning the search. result type comparisons are neither cheap nor * do they prune much, on average. */ - def adaptByResult(alts: List[TermRef], chosen: TermRef) = { - def nestedCtx = ctx.fresh.setExploreTyperState - pt match { - case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) => - alts.filter(alt => - (alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match { - case Nil => chosen - case alt2 :: Nil => alt2 - case alts2 => - resolveOverloaded(alts2, pt) match { - case alt2 :: Nil => alt2 - case _ => chosen - } - } - case _ => chosen - } + def adaptByResult(chosen: TermRef) = { + def nestedCtx = ctx.fresh.setExploreTyperState + pt match { + case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) => + alts.filter(alt => + (alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match { + case Nil => chosen + case alt2 :: Nil => alt2 + case alts2 => + resolveOverloaded(alts2, pt) match { + case alt2 :: Nil => alt2 + case _ => chosen + } + } + case _ => chosen } + } + + var found = resolveOverloaded(alts, pt, Nil)(ctx.retractMode(Mode.ImplicitsEnabled)) + if (found.isEmpty && ctx.mode.is(Mode.ImplicitsEnabled)) + found = resolveOverloaded(alts, pt, Nil) + found match { + case alt :: Nil => adaptByResult(alt) :: Nil + case _ => found + } + } + + /** This private version of `resolveOverloaded` does the bulk of the work of + * overloading resolution, but does not do result adaptation. It might be + * called twice from the public `resolveOverloaded` method, once with + * implicits enabled, and once without. + */ + private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type])(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { + + def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty + + /** The shape of given tree as a type; cannot handle named arguments. */ + def typeShape(tree: untpd.Tree): Type = tree match { + case untpd.Function(args, body) => + defn.FunctionOf(args map Function.const(defn.AnyType), typeShape(body)) + case _ => + defn.NothingType + } + + /** The shape of given tree as a type; is more expensive than + * typeShape but can can handle named arguments. + */ + def treeShape(tree: untpd.Tree): Tree = tree match { + case NamedArg(name, arg) => + val argShape = treeShape(arg) + cpy.NamedArg(tree)(name, argShape).withType(argShape.tpe) + case _ => + dummyTreeOfType(typeShape(tree)) + } + + def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] = + alts filter (isApplicable(_, argTypes, resultType)) val candidates = pt match { case pt @ FunProto(args, resultType, _) => @@ -1168,9 +1184,10 @@ trait Applications extends Compatibility { self: Typer => } } - case pt @ PolyProto(targs, pt1) => + case pt @ PolyProto(targs1, pt1) => + assert(targs.isEmpty) val alts1 = alts filter pt.isMatchedBy - resolveOverloaded(alts1, pt1, targs) + resolveOverloaded(alts1, pt1, targs1) case defn.FunctionOf(args, resultType) => narrowByTypes(alts, args, resultType) @@ -1178,23 +1195,16 @@ trait Applications extends Compatibility { self: Typer => case pt => alts filter (normalizedCompatible(_, pt)) } - narrowMostSpecific(candidates) match { - case Nil => Nil - case alt :: Nil => - adaptByResult(alts, alt) :: Nil - // why `alts` and not `candidates`? pos/array-overload.scala gives a test case. - // Here, only the Int-apply is a candidate, but it is not compatible with the result - // type. Picking the Byte-apply as the only result-compatible solution then forces - // the arguments (which are constants) to be adapted to Byte. If we had picked - // `candidates` instead, no solution would have been found. - case alts => - val noDefaults = alts.filter(!_.symbol.hasDefaultParams) - if (noDefaults.length == 1) noDefaults // return unique alternative without default parameters if it exists - else { - val deepPt = pt.deepenProto - if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs) - else alts - } + val found = narrowMostSpecific(candidates) + if (found.length <= 1) found + else { + val noDefaults = alts.filter(!_.symbol.hasDefaultParams) + if (noDefaults.length == 1) noDefaults // return unique alternative without default parameters if it exists + else { + val deepPt = pt.deepenProto + if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs) + else alts + } } } @@ -1297,11 +1307,3 @@ trait Applications extends Compatibility { self: Typer => harmonizeWith(tpes)(identity, (tp, pt) => pt) } -/* - def typedApply(app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context): Tree = track("typedApply") { - new ApplyToTyped(app, fun, methRef, args, resultType).result - } - - def typedApply(fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context): Tree = - typedApply(untpd.Apply(untpd.TypedSplice(fun), args), fun, methRef, args, resultType) -*/ diff --git a/tests/neg/t2660.scala b/tests/pos/t2660.scala similarity index 60% rename from tests/neg/t2660.scala rename to tests/pos/t2660.scala index 17fe262585ab..695db67b9576 100644 --- a/tests/neg/t2660.scala +++ b/tests/pos/t2660.scala @@ -1,5 +1,3 @@ -// Dotty deviation. The calls here now are classified as ambiguous. - package hoho class G @@ -22,9 +20,7 @@ class A[T](x: T) { object T { def main(args: Array[String]): Unit = { implicit def g2h(g: G): H = new H - new A[Int](new H, 23) // error - // in the context here, either secondary constructor is applicable - // to the other, due to the implicit in scope. So the call is ambiguous. + new A[Int](new H, 23) } } @@ -40,7 +36,7 @@ object X { object T2 { def main(args: Array[String]): Unit = { implicit def g2h(g: G): H = new H - X.f(new H, 23) // error + X.f(new H, 23) } } From 12ba8af12b7157f7f8c4ff6e106f4b30b0bd472a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 15 Jul 2016 09:37:45 +0200 Subject: [PATCH 2/4] Additions to test --- tests/pos/overloaded.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/pos/overloaded.scala b/tests/pos/overloaded.scala index 6a8e727148d3..56cfbccefbec 100644 --- a/tests/pos/overloaded.scala +++ b/tests/pos/overloaded.scala @@ -47,4 +47,13 @@ object overloaded { val t5: Int = r5 val r6 = combine((x: String, y) => x ++ y.toString) val t6: String = r6 + + // test result disambiguation + trait A + trait B + class C extends A with B + def fr(x: A): A = x + def fr(x: B): B = x + val a: A = fr(new C) + val b: B = fr(new C) } From 91f067143c9564a052058fa02c73ae6cde3020e6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 15 Jul 2016 15:14:38 +0200 Subject: [PATCH 3/4] Add tests Adds the original test form #1381. t2660 looks similar. Also adds some unrelated tests I had in the queue that now compile. --- tests/pos/hkgadt.scala | 9 +++++++++ tests/pos/i618.scala | 3 +++ tests/pos/overloaded.scala | 27 +++++++++++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 tests/pos/hkgadt.scala create mode 100644 tests/pos/i618.scala diff --git a/tests/pos/hkgadt.scala b/tests/pos/hkgadt.scala new file mode 100644 index 000000000000..ac8caa6f3d7b --- /dev/null +++ b/tests/pos/hkgadt.scala @@ -0,0 +1,9 @@ +object HKGADT { + sealed trait Foo[F[_]] + final case class Bar() extends Foo[List] + + def frob[F[_]](foo: Foo[F]) = + foo match { + case Bar() => () + } +} diff --git a/tests/pos/i618.scala b/tests/pos/i618.scala new file mode 100644 index 000000000000..70be56cc2b41 --- /dev/null +++ b/tests/pos/i618.scala @@ -0,0 +1,3 @@ +class C(val f: Any*) + +class D(override val f: Nothing) extends C(f) diff --git a/tests/pos/overloaded.scala b/tests/pos/overloaded.scala index 56cfbccefbec..0ccc553a8ea9 100644 --- a/tests/pos/overloaded.scala +++ b/tests/pos/overloaded.scala @@ -57,3 +57,30 @@ object overloaded { val a: A = fr(new C) val b: B = fr(new C) } + +// from #1381 + +object Foo { + class Bar[T] + implicit def const[T](x: T): Bar[T] = ??? + + def bar[T](e: T): Any = ??? + def bar[T](e: Bar[T]): Any = ??? + + val b: Bar[Int] = ??? + bar(b) +} + +object Test2 { + trait A; trait B + class C1 { + def f(x: A): Unit = println("A") + } + class C2 extends C1 { + def f(x: B): Unit = println("B") + } + object Test extends C2 with App { + implicit def a2b(x: A): B = new B {} + f(new A {}) + } +} From 762375cb41c23fc912dd9c9e1cc273b706a65631 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 18 Jul 2016 10:11:04 +0200 Subject: [PATCH 4/4] Make run tests for #1381. --- tests/pos/overloaded.scala | 36 ----------------------- tests/run/t1381.check | 7 +++++ tests/run/t1381.scala | 59 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 36 deletions(-) create mode 100644 tests/run/t1381.check create mode 100644 tests/run/t1381.scala diff --git a/tests/pos/overloaded.scala b/tests/pos/overloaded.scala index 0ccc553a8ea9..6a8e727148d3 100644 --- a/tests/pos/overloaded.scala +++ b/tests/pos/overloaded.scala @@ -47,40 +47,4 @@ object overloaded { val t5: Int = r5 val r6 = combine((x: String, y) => x ++ y.toString) val t6: String = r6 - - // test result disambiguation - trait A - trait B - class C extends A with B - def fr(x: A): A = x - def fr(x: B): B = x - val a: A = fr(new C) - val b: B = fr(new C) -} - -// from #1381 - -object Foo { - class Bar[T] - implicit def const[T](x: T): Bar[T] = ??? - - def bar[T](e: T): Any = ??? - def bar[T](e: Bar[T]): Any = ??? - - val b: Bar[Int] = ??? - bar(b) -} - -object Test2 { - trait A; trait B - class C1 { - def f(x: A): Unit = println("A") - } - class C2 extends C1 { - def f(x: B): Unit = println("B") - } - object Test extends C2 with App { - implicit def a2b(x: A): B = new B {} - f(new A {}) - } } diff --git a/tests/run/t1381.check b/tests/run/t1381.check new file mode 100644 index 000000000000..84aec1df28b9 --- /dev/null +++ b/tests/run/t1381.check @@ -0,0 +1,7 @@ +4 +3 +2 +A +B +frA +frB diff --git a/tests/run/t1381.scala b/tests/run/t1381.scala new file mode 100644 index 000000000000..c7f49c6c369f --- /dev/null +++ b/tests/run/t1381.scala @@ -0,0 +1,59 @@ +object Test { + def main(args: Array[String]): Unit = { + Test1.test() + Test2.test() + Test3.test() + } +} + +object Test1 { + class Bar[T](n: Int) { + println(n) + } + implicit def const[T](x: T): Bar[T] = new Bar[T](1) + + def bar[T](e: T): Any = new Bar[T](2) + def bar[T](e: Bar[T]): Any = new Bar[T](3) + + val b: Bar[Int] = new Bar[Int](4) + + def test(): Unit = { + bar(b) + bar(5) + } +} + +object Test2 { + trait A; trait B + class C1 { + def f(x: A): Unit = println("A") + } + class C2 extends C1 { + def f(x: B): Unit = println("B") + } + object Test extends C2 with App { + implicit def a2b(x: A): B = new B {} + def test(): Unit = { + f(new A {}) + f(new B {}) + } + } + def test(): Unit = Test.test() +} + +object Test3 { + trait A; trait B + class C extends A with B + def fr(x: A): A = { + println("frA") + x + } + def fr(x: B): B = { + println("frB") + x + } + def test(): Unit = { + val a: A = fr(new C) + val b: B = fr(new C) + } +}