From 7d76151109db168d025dfc0f6501fa7694e17053 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 17 Nov 2015 23:41:02 +0100 Subject: [PATCH 1/2] Fix ambiguity errors with polymorphic implicits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, `isAsSpecific(alt1, tp1, alt2, tp2)` did not handle having `tp2` be a polymorphic non-method type like `[A]Foo[A]`. Also update the documentation of `isAsSpecific` to account for this change, the new documentation is based on SLS ยง 6.26.3 but adapted to reflect the code. --- src/dotty/tools/dotc/core/Types.scala | 5 ++++ src/dotty/tools/dotc/typer/Applications.scala | 29 +++++++++++++++---- tests/run/implicits_poly.check | 1 + tests/run/implicits_poly.scala | 14 +++++++++ 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 tests/run/implicits_poly.check create mode 100644 tests/run/implicits_poly.scala diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 559839db2a62..db518d95939c 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2266,6 +2266,11 @@ object Types { protected def computeSignature(implicit ctx: Context) = resultSignature + def isPolymorphicMethodType: Boolean = resType match { + case _: MethodType => true + case _ => false + } + def instantiate(argTypes: List[Type])(implicit ctx: Context): Type = resultType.substParams(this, argTypes) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 64047cc1ee71..c57dd7aab54f 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -848,11 +848,19 @@ trait Applications extends Compatibility { self: Typer => else (sym1 is Module) && isDerived(sym1.companionClass, sym2) /** Is alternative `alt1` with type `tp1` as specific as alternative - * `alt2` with type `tp2` ? This is the case if + * `alt2` with type `tp2` ? * - * 1. `tp2` is a method or poly type but `tp1` isn't, or `tp1` is nullary. - * 2. `tp2` and `tp1` are method or poly types and `tp2` can be applied to the parameters of `tp1`. - * 3. Neither `tp1` nor `tp2` are method or poly types and `tp1` is compatible with `tp2`. + * 1. A method `alt1` of type (p1: T1, ..., pn: Tn)U is as specific as `alt2` + * if `alt2` is applicable to arguments (p1, ..., pn) of types T1,...,Tn + * or if `alt1` is nullary. + * 2. A polymorphic member of type [a1 >: L1 <: U1, ..., an >: Ln <: Un]T is as + * specific as `alt2` of type `tp2` if T is as specific as `tp2` under the + * assumption that for i = 1,...,n each ai is an abstract type name bounded + * from below by Li and from above by Ui. + * 3. A member of any other type `tp1` is: + * a. always as specific as a method or a polymorphic method. + * b. as specific as a member of any other type `tp2` if `tp1` is compatible + * with `tp2`. */ def isAsSpecific(alt1: TermRef, tp1: Type, alt2: TermRef, tp2: Type): Boolean = ctx.traceIndented(i"isAsSpecific $tp1 $tp2", overload) { tp1 match { case tp1: PolyType => @@ -870,8 +878,17 @@ trait Applications extends Compatibility { self: Typer => tp1.paramTypes.isEmpty && tp2.isInstanceOf[MethodOrPoly] case _ => tp2 match { - case tp2: MethodOrPoly => true - case _ => isCompatible(tp1, tp2) + case tp2: MethodType => true + case tp2: PolyType if tp2.isPolymorphicMethodType => true + case tp2: PolyType => + val nestedCtx = ctx.fresh.setExploreTyperState + + { + implicit val ctx: Context = nestedCtx + isCompatible(tp1, constrained(tp2).resultType) + } + case _ => + isCompatible(tp1, tp2) } }} diff --git a/tests/run/implicits_poly.check b/tests/run/implicits_poly.check new file mode 100644 index 000000000000..0b148ee47940 --- /dev/null +++ b/tests/run/implicits_poly.check @@ -0,0 +1 @@ +barFoo diff --git a/tests/run/implicits_poly.scala b/tests/run/implicits_poly.scala new file mode 100644 index 000000000000..2a5d68f737af --- /dev/null +++ b/tests/run/implicits_poly.scala @@ -0,0 +1,14 @@ +class Foo[A](val x: String) +class Bar[A](x: String) extends Foo[A](x) + +object Test { + implicit def anyRefFoo[A <: AnyRef]: Foo[A] = new Foo("anyRefFoo") + implicit def fooFoo[A]: Foo[Foo[A]] = new Foo("fooFoo") + implicit def barFoo[A]: Bar[Foo[A]] = new Bar("barFoo") + + def getFooFoo(implicit ev: Foo[Foo[Int]]) = ev + + def main(args: Array[String]): Unit = { + println(getFooFoo.x) + } +} From c2079f2f38d81577dd9710c9d0b044c599b18ec7 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 18 Nov 2015 03:30:34 +0100 Subject: [PATCH 2/2] Annotate `isAsSpecific` with references to its documentation Also reorder the cases to be in the same order as the documentation. --- src/dotty/tools/dotc/typer/Applications.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index c57dd7aab54f..4ca9c39af5b3 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -863,10 +863,7 @@ trait Applications extends Compatibility { self: Typer => * with `tp2`. */ def isAsSpecific(alt1: TermRef, tp1: Type, alt2: TermRef, tp2: Type): Boolean = ctx.traceIndented(i"isAsSpecific $tp1 $tp2", overload) { tp1 match { - case tp1: PolyType => - val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds) - isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2) - case tp1: MethodType => + case tp1: MethodType => // (1) def repeatedToSingle(tp: Type): Type = tp match { case tp @ ExprType(tp1) => tp.derivedExprType(repeatedToSingle(tp1)) case _ => if (tp.isRepeatedParam) tp.argTypesHi.head else tp @@ -876,18 +873,21 @@ trait Applications extends Compatibility { self: Typer => else tp1.paramTypes isApplicable(alt2, formals1, WildcardType) || tp1.paramTypes.isEmpty && tp2.isInstanceOf[MethodOrPoly] - case _ => + case tp1: PolyType => // (2) + val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds) + isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2) + case _ => // (3) tp2 match { - case tp2: MethodType => true - case tp2: PolyType if tp2.isPolymorphicMethodType => true - case tp2: PolyType => + case tp2: MethodType => true // (3a) + case tp2: PolyType if tp2.isPolymorphicMethodType => true // (3a) + case tp2: PolyType => // (3b) val nestedCtx = ctx.fresh.setExploreTyperState { implicit val ctx: Context = nestedCtx isCompatible(tp1, constrained(tp2).resultType) } - case _ => + case _ => // (3b) isCompatible(tp1, tp2) } }}