Scala Programming Language
  1. Scala Programming Language
  2. SI-4881

Incorrect inferred type in contravariant position

    Details

    • Type: Bug Bug
    • Status: Open
    • Priority: Critical Critical
    • Resolution: Unresolved
    • Affects Version/s: Scala 2.9.0-1
    • Fix Version/s: Backlog
    • Component/s: Type Inference
    • Labels:
      None

      Description

      class Contra[-T]
      trait A
      trait B extends A
      trait C extends B
      def contraLBUB[a >: C <: A](): Contra[a] = null
      def contraLB[a >: C](): Contra[a] = null
      contraLBUB() //inferred Contra[C] instead of Contra[A]
      contraLB() //inferred Contra[C] instead of Contra[Any]
      

      According to Language specification (6.26.4 Case 2):
      If several substitutions exist,
      local-type inference will choose for each type variable ai a minimal or maximal type
      Ti of the solution space. A maximal type Ti will be chosen if the type parameter ai
      appears contravariantly (§4.5) in the type T of the expression. A minimal type Ti
      will be chosen in all other situations, i.e. if the variable appears covariantly, nonvariantly
      or not at all in the type T.

      What we can see here. Value has Type Contra[a]. So position of a is contravariant. There is no covariant or invariant positions of a => maximal solution should be chosen.

        Issue Links

          Activity

          Hide
          Jason Zaugg added a comment -

          Actually, it works as expected as a Value (Case 2)

          scala> contraLBUB
          res9: Contra[A] = null
          
          scala> contraLB
          res10: Contra[Any] = null
          

          So the question is, does "Case 3: Methods" explain these?

          scala> contraLBUB()
          res11: Contra[C] = null
          
          scala> contraLB()
          res12: Contra[C] = null
          
          Show
          Jason Zaugg added a comment - Actually, it works as expected as a Value (Case 2) scala> contraLBUB res9: Contra[A] = null scala> contraLB res10: Contra[Any] = null So the question is, does "Case 3: Methods" explain these? scala> contraLBUB() res11: Contra[C] = null scala> contraLB() res12: Contra[C] = null
          Hide
          Alexander added a comment -

          I'm sure this is wrong if it's different:

          foo
          foo()
          
          Show
          Alexander added a comment - I'm sure this is wrong if it's different: foo foo()
          Hide
          Paul Phillips added a comment -

          Yeah, that's a bit hard to defend:

          scala> contraLBUB
          res1: Contra[A] = null
          
          scala> contraLBUB()
          res2: Contra[C] = null
          
          scala> (contraLBUB _)()
          res3: Contra[A] = null
          
          Show
          Paul Phillips added a comment - Yeah, that's a bit hard to defend: scala> contraLBUB res1: Contra[A] = null scala> contraLBUB() res2: Contra[C] = null scala> (contraLBUB _)() res3: Contra[A] = null
          Hide
          Jason Zaugg added a comment -

          Without having checked the consequences (sky falling, etc), but this is enough to restore consistency between inference for the value and method application:

          diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.s
          index 688dcd9..a34b93d 100644
          --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
          +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
          @@ -575,8 +575,9 @@ trait Infer {
                       "argument expression's type is not compatible with formal parameter type" + foundReqMsg(tp1, pt1))
                   }
                 }
                 val targs = solvedTypes(
          -        tvars, tparams, tparams map varianceInTypes(formals),
          +        tvars, tparams, tparams map varianceInTypes(restpe +: formals),
                   false, lubDepth(formals) max lubDepth(argtpes)
                 )
                 adjustTypeArgs(tparams, tvars, targs, restpe)
          

          Compare with:

          https://github.com/scala/scala/blob/039d826e/src/compiler/scala/tools/nsc/typechecker/Infer.scala#L384

          Show
          Jason Zaugg added a comment - Without having checked the consequences (sky falling, etc), but this is enough to restore consistency between inference for the value and method application: diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.s index 688dcd9..a34b93d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -575,8 +575,9 @@ trait Infer { "argument expression's type is not compatible with formal parameter type" + foundReqMsg(tp1, pt1)) } } val targs = solvedTypes( - tvars, tparams, tparams map varianceInTypes(formals), + tvars, tparams, tparams map varianceInTypes(restpe +: formals), false, lubDepth(formals) max lubDepth(argtpes) ) adjustTypeArgs(tparams, tvars, targs, restpe) Compare with: https://github.com/scala/scala/blob/039d826e/src/compiler/scala/tools/nsc/typechecker/Infer.scala#L384
          Hide
          Adriaan Moors added a comment - - edited

          I think that needs to be adjusted slightly since the variance in formals should be flipped (arguments = contravariant), whereas restpe is in covariant position (the flipping is achieved by the up=false argument to solvedTypes, off the top of my head)

          Show
          Adriaan Moors added a comment - - edited I think that needs to be adjusted slightly since the variance in formals should be flipped (arguments = contravariant), whereas restpe is in covariant position (the flipping is achieved by the up=false argument to solvedTypes, off the top of my head)
          Show
          Adriaan Moors added a comment - https://github.com/scala/scala/pull/975
          Hide
          Paul Phillips added a comment -

          This is being reverted for causing SI-6311.

          Show
          Paul Phillips added a comment - This is being reverted for causing SI-6311 .
          Hide
          Paolo G. Giarrusso added a comment -

          Fixing this means that Coll would be inferred differently for these two signatures, which look instead equivalent:

          def f[T, Coll <: Traversable[T]](p0: String)(p1: Traversable[T]): (Coll => Traversable[T]) = ??? //Here Coll would be maximized.
          def f[T, Coll <: Traversable[T]](p0: String)(p1: Traversable[T])(p2: Coll): Traversable[T] = ??? //Here Coll would be minimized.
          

          This is if I get the new expected behavior right, which says that (ignoring mixed variance) a type argument must be minimized (made most specific) if it appears either:
          (a) covariantly in the result type (per 4881), hence covariantly in the method type or
          (b) covariantly in argument types, hence contravariantly in the method type.

          Show
          Paolo G. Giarrusso added a comment - Fixing this means that Coll would be inferred differently for these two signatures, which look instead equivalent: def f[T, Coll <: Traversable[T]](p0: String)(p1: Traversable[T]): (Coll => Traversable[T]) = ??? //Here Coll would be maximized. def f[T, Coll <: Traversable[T]](p0: String)(p1: Traversable[T])(p2: Coll): Traversable[T] = ??? //Here Coll would be minimized. This is if I get the new expected behavior right, which says that (ignoring mixed variance) a type argument must be minimized (made most specific) if it appears either: (a) covariantly in the result type (per 4881), hence covariantly in the method type or (b) covariantly in argument types, hence contravariantly in the method type.
          Hide
          Grzegorz Kossakowski added a comment -

          Unassigning and rescheduling to M7 as previous deadline was missed.

          Show
          Grzegorz Kossakowski added a comment - Unassigning and rescheduling to M7 as previous deadline was missed.

            People

            • Assignee:
              Unassigned
              Reporter:
              Alexander
            • Votes:
              2 Vote for this issue
              Watchers:
              8 Start watching this issue

              Dates

              • Created:
                Updated:

                Development