Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

SI-7289 Less strict type application for TypeVar. #2354

Merged
merged 1 commit into from

2 participants

@adriaanm
Owner

(Proposed alternative for #2294)

When a type constructor variable is applied to the wrong number of arguments,
return a new type variable whose instance is ErrorType.

Let's dissect the reported test case.

Define the first implicit:

scala> trait Schtroumpf[T]
defined trait Schtroumpf

scala> implicit def schtroumpf[T, U <: Coll[T], Coll[X] <: Traversable[X]]
     |     (implicit minorSchtroumpf: Schtroumpf[T]): Schtroumpf[U] = ???
schtroumpf: [T, U <: Coll[T], Coll[X] <: Traversable[X]](implicit minorSchtroumpf: Schtroumpf[T])Schtroumpf[U]

Call it explicitly => kind error during type inference reported.

scala> schtroumpf(null): Schtroumpf[Int]
<console>:10: error: inferred kinds of the type arguments (Nothing,Int,Int) do not conform to the expected kinds of the type parameters (type T,type U,type Coll).
Int's type parameters do not match type Coll's expected parameters:
class Int has no type parameters, but type Coll has one
              schtroumpf(null): Schtroumpf[Int]
              ^
<console>:10: error: type mismatch;
 found   : Schtroumpf[U]
 required: Schtroumpf[Int]
              schtroumpf(null): Schtroumpf[Int]
                        ^

Add another implicit, and let implicit search weigh them up.

scala> implicitly[Schtroumpf[Int]]
<console>:10: error: diverging implicit expansion for type Schtroumpf[Int]
starting with method schtroumpf
              implicitly[Schtroumpf[Int]]
                        ^

scala> implicit val qoo = new Schtroumpf[Int]{}
qoo: Schtroumpf[Int] = $anon$1@c1b9b03

scala> implicitly[Schtroumpf[Int]]
<crash>

Implicit search compares the two in-scope implicits in isStrictlyMoreSpecific,
which constructs an existential type:

type ET = Schtroumpf[U] forSome { type T; type U <: Coll[T]; type Coll[] <: Traversable[] }

A subsequent subtype check ET <:< Schtroumpf[Int] gets to withTypeVars, which
replaces the quantified types with type variables, checks conformance of that
substitued underlying type against Schtroumpf[Int], and then tries to solve
the collected type constraints. The type var trace looks like:

[    create] ?T                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
[    create] ?U                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
[    create] ?Coll                    ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
[   setInst] Nothing                  ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Nothing )
[   setInst] scala.collection.immutable.Nil.type( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], U=scala.collection.immutable.Nil.type )
[   setInst] =?scala.collection.immutable.Nil.type( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], Coll==?scala.collection.immutable.Nil.type )
[    create] ?T                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
[   setInst] Int                      ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Int )
[    create] ?T                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
[    create] ?U                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
[    create] ?Coll                    ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
[   setInst] Nothing                  ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Nothing )
[   setInst] Int                      ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], U=Int )
[   setInst] =?Int                    ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], Coll==?Int )

The problematic part is when ?Int (the type var originated from U) is registered
as a lower bound for Coll. That happens in solveOne:

for (tparam2 <- tparams)
  tparam2.info.bounds.hi.dealias match {
    case TypeRef(_, `tparam`, _) =>
      log(s"$tvar addLoBound $tparam2.tpeHK.instantiateTypeParams($tparams, $tvars)")
      tvar addLoBound tparam2.tpeHK.instantiateTypeParams(tparams, tvars)
    case _ =>
  }
@adriaanm
Owner

review by @retronym

src/reflect/scala/reflect/internal/Types.scala
@@ -3090,8 +3092,11 @@ trait Types extends api.Types { self: SymbolTable =>
val tv = TypeVar(origin, constr, newArgs, params)
TypeVar.trace("applyArgs", "In " + originLocation + ", apply args " + newArgs.mkString(", ") + " to " + originName)(tv)
}
- else
- throw new Error("Invalid type application in TypeVar: " + params + ", " + newArgs)
+ else {
+ val tv = TypeVar(typeSymbol)
+ tv.setInst(ErrorType)
+ tv
@retronym Owner
retronym added a note

For consistency with other setXxx methods, we could change setInst to return this.type and shed a few lines here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@retronym
Owner

Looks like like fixing it here is cleaner and more comprehensive than my proposal.

Could you add Paul's test case from the comments the old PR, and opine its desired results?

@adriaanm
Owner

reviewer comments addressed

@adriaanm adriaanm SI-7289 Less strict type application for TypeVar.
When a type constructor variable is applied to the wrong number of arguments,
return a new type variable whose instance is `ErrorType`.

Dissection of the reported test case by @retronym:

Define the first implicit:

    scala> trait Schtroumpf[T]
    defined trait Schtroumpf

    scala> implicit def schtroumpf[T, U <: Coll[T], Coll[X] <: Traversable[X]]
         |     (implicit minorSchtroumpf: Schtroumpf[T]): Schtroumpf[U] = ???
    schtroumpf: [T, U <: Coll[T], Coll[X] <: Traversable[X]](implicit minorSchtroumpf: Schtroumpf[T])Schtroumpf[U]

Call it explicitly => kind error during type inference reported.

    scala> schtroumpf(null): Schtroumpf[Int]
    <console>:10: error: inferred kinds of the type arguments (Nothing,Int,Int) do not conform to the expected kinds of the type parameters (type T,type U,type Coll).
    Int's type parameters do not match type Coll's expected parameters:
    class Int has no type parameters, but type Coll has one
                  schtroumpf(null): Schtroumpf[Int]
                  ^
    <console>:10: error: type mismatch;
     found   : Schtroumpf[U]
     required: Schtroumpf[Int]
                  schtroumpf(null): Schtroumpf[Int]
                            ^

Add another implicit, and let implicit search weigh them up.

    scala> implicitly[Schtroumpf[Int]]
    <console>:10: error: diverging implicit expansion for type Schtroumpf[Int]
    starting with method schtroumpf
                  implicitly[Schtroumpf[Int]]
                            ^

    scala> implicit val qoo = new Schtroumpf[Int]{}
    qoo: Schtroumpf[Int] = $anon$1@c1b9b03

    scala> implicitly[Schtroumpf[Int]]
    <crash>

Implicit search compares the two in-scope implicits in `isStrictlyMoreSpecific`,
which constructs an existential type:

   type ET = Schtroumpf[U] forSome { type T; type U <: Coll[T]; type Coll[_] <: Traversable[_] }

A subsequent subtype check `ET <:< Schtroumpf[Int]` gets to `withTypeVars`, which
replaces the quantified types with type variables, checks conformance of that
substitued underlying type against `Schtroumpf[Int]`, and then tries to solve
the collected type constraints. The type var trace looks like:

    [    create] ?T                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
    [    create] ?U                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
    [    create] ?Coll                    ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
    [   setInst] Nothing                  ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Nothing )
    [   setInst] scala.collection.immutable.Nil.type( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], U=scala.collection.immutable.Nil.type )
    [   setInst] =?scala.collection.immutable.Nil.type( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], Coll==?scala.collection.immutable.Nil.type )
    [    create] ?T                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
    [   setInst] Int                      ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Int )
    [    create] ?T                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
    [    create] ?U                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
    [    create] ?Coll                    ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
    [   setInst] Nothing                  ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Nothing )
    [   setInst] Int                      ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], U=Int )
    [   setInst] =?Int                    ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], Coll==?Int )

The problematic part is when `?Int` (the type var originated from `U`) is registered
as a lower bound for `Coll`. That happens in `solveOne`:

    for (tparam2 <- tparams)
      tparam2.info.bounds.hi.dealias match {
        case TypeRef(_, `tparam`, _) =>
          log(s"$tvar addLoBound $tparam2.tpeHK.instantiateTypeParams($tparams, $tvars)")
          tvar addLoBound tparam2.tpeHK.instantiateTypeParams(tparams, tvars)
        case _ =>
      }
6a61e17
@retronym
Owner

LGTM

@adriaanm adriaanm merged commit 2a22b86 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 9, 2013
  1. @adriaanm

    SI-7289 Less strict type application for TypeVar.

    adriaanm authored
    When a type constructor variable is applied to the wrong number of arguments,
    return a new type variable whose instance is `ErrorType`.
    
    Dissection of the reported test case by @retronym:
    
    Define the first implicit:
    
        scala> trait Schtroumpf[T]
        defined trait Schtroumpf
    
        scala> implicit def schtroumpf[T, U <: Coll[T], Coll[X] <: Traversable[X]]
             |     (implicit minorSchtroumpf: Schtroumpf[T]): Schtroumpf[U] = ???
        schtroumpf: [T, U <: Coll[T], Coll[X] <: Traversable[X]](implicit minorSchtroumpf: Schtroumpf[T])Schtroumpf[U]
    
    Call it explicitly => kind error during type inference reported.
    
        scala> schtroumpf(null): Schtroumpf[Int]
        <console>:10: error: inferred kinds of the type arguments (Nothing,Int,Int) do not conform to the expected kinds of the type parameters (type T,type U,type Coll).
        Int's type parameters do not match type Coll's expected parameters:
        class Int has no type parameters, but type Coll has one
                      schtroumpf(null): Schtroumpf[Int]
                      ^
        <console>:10: error: type mismatch;
         found   : Schtroumpf[U]
         required: Schtroumpf[Int]
                      schtroumpf(null): Schtroumpf[Int]
                                ^
    
    Add another implicit, and let implicit search weigh them up.
    
        scala> implicitly[Schtroumpf[Int]]
        <console>:10: error: diverging implicit expansion for type Schtroumpf[Int]
        starting with method schtroumpf
                      implicitly[Schtroumpf[Int]]
                                ^
    
        scala> implicit val qoo = new Schtroumpf[Int]{}
        qoo: Schtroumpf[Int] = $anon$1@c1b9b03
    
        scala> implicitly[Schtroumpf[Int]]
        <crash>
    
    Implicit search compares the two in-scope implicits in `isStrictlyMoreSpecific`,
    which constructs an existential type:
    
       type ET = Schtroumpf[U] forSome { type T; type U <: Coll[T]; type Coll[_] <: Traversable[_] }
    
    A subsequent subtype check `ET <:< Schtroumpf[Int]` gets to `withTypeVars`, which
    replaces the quantified types with type variables, checks conformance of that
    substitued underlying type against `Schtroumpf[Int]`, and then tries to solve
    the collected type constraints. The type var trace looks like:
    
        [    create] ?T                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
        [    create] ?U                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
        [    create] ?Coll                    ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
        [   setInst] Nothing                  ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Nothing )
        [   setInst] scala.collection.immutable.Nil.type( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], U=scala.collection.immutable.Nil.type )
        [   setInst] =?scala.collection.immutable.Nil.type( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], Coll==?scala.collection.immutable.Nil.type )
        [    create] ?T                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
        [   setInst] Int                      ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Int )
        [    create] ?T                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
        [    create] ?U                       ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
        [    create] ?Coll                    ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
        [   setInst] Nothing                  ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Nothing )
        [   setInst] Int                      ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], U=Int )
        [   setInst] =?Int                    ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], Coll==?Int )
    
    The problematic part is when `?Int` (the type var originated from `U`) is registered
    as a lower bound for `Coll`. That happens in `solveOne`:
    
        for (tparam2 <- tparams)
          tparam2.info.bounds.hi.dealias match {
            case TypeRef(_, `tparam`, _) =>
              log(s"$tvar addLoBound $tparam2.tpeHK.instantiateTypeParams($tparams, $tvars)")
              tvar addLoBound tparam2.tpeHK.instantiateTypeParams(tparams, tvars)
            case _ =>
          }
This page is out of date. Refresh to see the latest.
View
4 bincompat-backward.whitelist.conf
@@ -186,6 +186,10 @@ filter {
{
matchName="scala.reflect.internal.ClassfileConstants.xxxunusedxxxx"
problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.internal.Types#TypeVar.setInst"
+ problemName=IncompatibleResultTypeProblem
}
]
}
View
4 bincompat-forward.whitelist.conf
@@ -418,6 +418,10 @@ filter {
{
matchName="scala.reflect.internal.ClassfileConstants.CONSTANT_METHODTYPE"
problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.internal.Types#TypeVar.setInst"
+ problemName=IncompatibleResultTypeProblem
}
]
}
View
17 src/reflect/scala/reflect/internal/Types.scala
@@ -3076,12 +3076,14 @@ trait Types extends api.Types { self: SymbolTable =>
/** The variable's skolemization level */
val level = skolemizationLevel
- /** Two occurrences of a higher-kinded typevar, e.g. `?CC[Int]` and `?CC[String]`, correspond to
- * ''two instances'' of `TypeVar` that share the ''same'' `TypeConstraint`.
+ /** Applies this TypeVar to type arguments, if arity matches.
*
- * `constr` for `?CC` only tracks type constructors anyway,
- * so when `?CC[Int] <:< List[Int]` and `?CC[String] <:< Iterable[String]`
- * `?CC's` hibounds contains List and Iterable.
+ * Different applications of the same type constructor variable `?CC`,
+ * e.g. `?CC[Int]` and `?CC[String]`, are modeled as distinct instances of `TypeVar`
+ * that share a `TypeConstraint`, so that the comparisons `?CC[Int] <:< List[Int]`
+ * and `?CC[String] <:< Iterable[String]` result in `?CC` being upper-bounded by `List` and `Iterable`.
+ *
+ * Applying the wrong number of type args results in a TypeVar whose instance is set to `ErrorType`.
*/
def applyArgs(newArgs: List[Type]): TypeVar = (
if (newArgs.isEmpty && typeArgs.isEmpty)
@@ -3091,7 +3093,7 @@ trait Types extends api.Types { self: SymbolTable =>
TypeVar.trace("applyArgs", "In " + originLocation + ", apply args " + newArgs.mkString(", ") + " to " + originName)(tv)
}
else
- throw new Error("Invalid type application in TypeVar: " + params + ", " + newArgs)
+ TypeVar(typeSymbol).setInst(ErrorType)
)
// newArgs.length may differ from args.length (could've been empty before)
//
@@ -3121,13 +3123,14 @@ trait Types extends api.Types { self: SymbolTable =>
// <region name="constraint mutators + undoLog">
// invariant: before mutating constr, save old state in undoLog
// (undoLog is used to reset constraints to avoid piling up unrelated ones)
- def setInst(tp: Type) {
+ def setInst(tp: Type): this.type = {
// assert(!(tp containsTp this), this)
undoLog record this
// if we were compared against later typeskolems, repack the existential,
// because skolems are only compatible if they were created at the same level
val res = if (shouldRepackType) repackExistential(tp) else tp
constr.inst = TypeVar.trace("setInst", "In " + originLocation + ", " + originName + "=" + res)(res)
+ this
}
def addLoBound(tp: Type, isNumericBound: Boolean = false) {
View
4 test/files/neg/t7289.check
@@ -0,0 +1,4 @@
+t7289.scala:8: error: could not find implicit value for parameter e: Test.Schtroumpf[scala.collection.immutable.Nil.type]
+ implicitly[Schtroumpf[Nil.type]]
+ ^
+one error found
View
39 test/files/neg/t7289.scala
@@ -0,0 +1,39 @@
+object Test extends App {
+ trait Schtroumpf[T]
+
+ implicit def schtroumpf[T, U <: Coll[T], Coll[X] <: Traversable[X]]
+ (implicit minorSchtroumpf: Schtroumpf[T]): Schtroumpf[U] = ???
+
+ implicit val qoo: Schtroumpf[Int] = new Schtroumpf[Int]{}
+ implicitly[Schtroumpf[Nil.type]]
+}
+
+/*
+info1 = {scala.tools.nsc.typechecker.Implicits$ImplicitInfo@3468}"qoo: => Test.Schtroumpf[Int]"
+info2 = {scala.tools.nsc.typechecker.Implicits$ImplicitInfo@3469}"schtroumpf: [T, U <: Coll[T], Coll[_] <: Traversable[_]](implicit minorSchtroumpf: Test.Schtroumpf[T])Test.Schtroumpf[U]"
+isStrictlyMoreSpecific(info1, info2)
+ isSubType(Test.Schtroumpf[Int], Test.Schtroumpf[U] forSome { T; U <: Coll[T]; Coll[_] <: Traversable[_] })
+ isAsSpecificValueType(Test.Schtroumpf[Int], Test.Schtroumpf[U], undef2 = List(type T, type U, type Coll))
+
+ val et: ExistentialType = Test.Schtroumpf[U] forSome { T; U <: Coll[T]; Coll[_] <: Traversable[_] }
+ val tp1 = Test.Schtroumpf[Int]
+ et.withTypeVars(isSubType(tp1, _, depth))
+ solve()
+ tvars = tList(=?Nothing, =?Int, =?=?Int)
+
+
+[ create] ?T ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
+[ create] ?U ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
+[ create] ?Coll ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
+[ setInst] Nothing ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Nothing )
+[ setInst] scala.collection.immutable.Nil.type( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], U=scala.collection.immutable.Nil.type )
+[ setInst] =?scala.collection.immutable.Nil.type( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], Coll==?scala.collection.immutable.Nil.type )
+[ create] ?T ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
+[ setInst] Int ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Int )
+[ create] ?T ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
+[ create] ?U ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
+[ create] ?Coll ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] )
+[ setInst] Nothing ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Nothing )
+[ setInst] Int ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], U=Int )
+[ setInst] =?Int ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], Coll==?Int )
+*/
View
22 test/files/neg/t7289_status_quo.check
@@ -0,0 +1,22 @@
+t7289_status_quo.scala:9: error: could not find implicit value for parameter e: Test1.Ext[List[Int]]
+ implicitly[Ext[List[Int]]] // fails - not found
+ ^
+t7289_status_quo.scala:11: error: could not find implicit value for parameter e: Test1.Ext[List[List[List[Int]]]]
+ implicitly[Ext[List[List[List[Int]]]]] // fails - not found
+ ^
+t7289_status_quo.scala:15: error: ambiguous implicit values:
+ both method f in object Test1 of type [A, Coll <: CC[A], CC[X] <: Traversable[X]](implicit xi: Test1.Ext[A])Test1.Ext[Coll]
+ and value m in object Test1 of type => Test1.Ext[List[List[Int]]]
+ match expected type Test1.Ext[_ <: List[List[Int]]]
+ implicitly[Ext[_ <: List[List[Int]]]] // fails - ambiguous
+ ^
+t7289_status_quo.scala:20: error: could not find implicit value for parameter e: Test1.ExtCov[List[Int]]
+ implicitly[ExtCov[List[Int]]] // fails - not found
+ ^
+t7289_status_quo.scala:21: error: could not find implicit value for parameter e: Test1.ExtCov[List[List[Int]]]
+ implicitly[ExtCov[List[List[Int]]]] // fails - not found
+ ^
+t7289_status_quo.scala:22: error: could not find implicit value for parameter e: Test1.ExtCov[List[List[List[Int]]]]
+ implicitly[ExtCov[List[List[List[Int]]]]] // fails - not found
+ ^
+6 errors found
View
23 test/files/neg/t7289_status_quo.scala
@@ -0,0 +1,23 @@
+// record the status quo after this fix
+// not clear to @adriaanm why an upper-bounded existential in an invariant position
+// is different from putting that upper bound in a covariant position
+object Test1 {
+ trait Ext[T]
+ implicit def f[A, Coll <: CC[A], CC[X] <: Traversable[X]](implicit xi: Ext[A]): Ext[Coll] = ???
+ implicit val m: Ext[List[List[Int]]] = new Ext[List[List[Int]]]{}
+
+ implicitly[Ext[List[Int]]] // fails - not found
+ implicitly[Ext[List[List[Int]]]] // compiles
+ implicitly[Ext[List[List[List[Int]]]]] // fails - not found
+
+ // Making Ext[+T] should incur the same behavior as these. (so says @paulp)
+ implicitly[Ext[_ <: List[Int]]] // compiles
+ implicitly[Ext[_ <: List[List[Int]]]] // fails - ambiguous
+ implicitly[Ext[_ <: List[List[List[Int]]]]] // compiles
+
+ // But, we currently get:
+ trait ExtCov[+T]
+ implicitly[ExtCov[List[Int]]] // fails - not found
+ implicitly[ExtCov[List[List[Int]]]] // fails - not found
+ implicitly[ExtCov[List[List[List[Int]]]]] // fails - not found
+}
Something went wrong with that request. Please try again.