Skip to content

Commit ea1373b

Browse files
committed
Merge pull request #963 from smarter/fix/poly-implicits
Fix ambiguity errors with polymorphic implicits
2 parents 945334c + c2079f2 commit ea1373b

File tree

4 files changed

+48
-11
lines changed

4 files changed

+48
-11
lines changed

src/dotty/tools/dotc/core/Types.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2266,6 +2266,11 @@ object Types {
22662266

22672267
protected def computeSignature(implicit ctx: Context) = resultSignature
22682268

2269+
def isPolymorphicMethodType: Boolean = resType match {
2270+
case _: MethodType => true
2271+
case _ => false
2272+
}
2273+
22692274
def instantiate(argTypes: List[Type])(implicit ctx: Context): Type =
22702275
resultType.substParams(this, argTypes)
22712276

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -848,17 +848,22 @@ trait Applications extends Compatibility { self: Typer =>
848848
else (sym1 is Module) && isDerived(sym1.companionClass, sym2)
849849

850850
/** Is alternative `alt1` with type `tp1` as specific as alternative
851-
* `alt2` with type `tp2` ? This is the case if
851+
* `alt2` with type `tp2` ?
852852
*
853-
* 1. `tp2` is a method or poly type but `tp1` isn't, or `tp1` is nullary.
854-
* 2. `tp2` and `tp1` are method or poly types and `tp2` can be applied to the parameters of `tp1`.
855-
* 3. Neither `tp1` nor `tp2` are method or poly types and `tp1` is compatible with `tp2`.
853+
* 1. A method `alt1` of type (p1: T1, ..., pn: Tn)U is as specific as `alt2`
854+
* if `alt2` is applicable to arguments (p1, ..., pn) of types T1,...,Tn
855+
* or if `alt1` is nullary.
856+
* 2. A polymorphic member of type [a1 >: L1 <: U1, ..., an >: Ln <: Un]T is as
857+
* specific as `alt2` of type `tp2` if T is as specific as `tp2` under the
858+
* assumption that for i = 1,...,n each ai is an abstract type name bounded
859+
* from below by Li and from above by Ui.
860+
* 3. A member of any other type `tp1` is:
861+
* a. always as specific as a method or a polymorphic method.
862+
* b. as specific as a member of any other type `tp2` if `tp1` is compatible
863+
* with `tp2`.
856864
*/
857865
def isAsSpecific(alt1: TermRef, tp1: Type, alt2: TermRef, tp2: Type): Boolean = ctx.traceIndented(i"isAsSpecific $tp1 $tp2", overload) { tp1 match {
858-
case tp1: PolyType =>
859-
val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds)
860-
isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2)
861-
case tp1: MethodType =>
866+
case tp1: MethodType => // (1)
862867
def repeatedToSingle(tp: Type): Type = tp match {
863868
case tp @ ExprType(tp1) => tp.derivedExprType(repeatedToSingle(tp1))
864869
case _ => if (tp.isRepeatedParam) tp.argTypesHi.head else tp
@@ -868,10 +873,22 @@ trait Applications extends Compatibility { self: Typer =>
868873
else tp1.paramTypes
869874
isApplicable(alt2, formals1, WildcardType) ||
870875
tp1.paramTypes.isEmpty && tp2.isInstanceOf[MethodOrPoly]
871-
case _ =>
876+
case tp1: PolyType => // (2)
877+
val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds)
878+
isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2)
879+
case _ => // (3)
872880
tp2 match {
873-
case tp2: MethodOrPoly => true
874-
case _ => isCompatible(tp1, tp2)
881+
case tp2: MethodType => true // (3a)
882+
case tp2: PolyType if tp2.isPolymorphicMethodType => true // (3a)
883+
case tp2: PolyType => // (3b)
884+
val nestedCtx = ctx.fresh.setExploreTyperState
885+
886+
{
887+
implicit val ctx: Context = nestedCtx
888+
isCompatible(tp1, constrained(tp2).resultType)
889+
}
890+
case _ => // (3b)
891+
isCompatible(tp1, tp2)
875892
}
876893
}}
877894

tests/run/implicits_poly.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
barFoo

tests/run/implicits_poly.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class Foo[A](val x: String)
2+
class Bar[A](x: String) extends Foo[A](x)
3+
4+
object Test {
5+
implicit def anyRefFoo[A <: AnyRef]: Foo[A] = new Foo("anyRefFoo")
6+
implicit def fooFoo[A]: Foo[Foo[A]] = new Foo("fooFoo")
7+
implicit def barFoo[A]: Bar[Foo[A]] = new Bar("barFoo")
8+
9+
def getFooFoo(implicit ev: Foo[Foo[Int]]) = ev
10+
11+
def main(args: Array[String]): Unit = {
12+
println(getFooFoo.x)
13+
}
14+
}

0 commit comments

Comments
 (0)