Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: Scala 2.10.0
    • Fix Version/s: None
    • Component/s: Misc Compiler
    • Labels:

      Description

      It seems there is a serious regression of lub between 2.9 and 2.10, which leads to illogical types.
      Here is one scenario which works:

      scala> trait S { type T }
      defined trait S
      
      scala> trait A extends S { type T <: A }
      defined trait A
      
      scala> trait B extends S { type T <: B }
      defined trait B
      
      scala> val a = new A{}
      a: Object with A = $anon$1@edfd5b8
      
      scala> val b = new B{}
      b: Object with B = $anon$1@68d88d5
      
      scala> val x = if (true) a else b
      x: S{type T <: S} = $anon$1@edfd5b8
      

      This one looks good. Now, add a field x to types S, A, B:

      scala> trait S { type T; val x: AnyRef }
      defined trait S
      
      scala> trait A extends S { type T <: A; val x: A = null }
      defined trait A
      
      scala> trait B extends S { type T <: B; val x: B = null }
      defined trait B
      
      scala> val a = new A{}
      a: Object with A = $anon$1@370410a7
      
      scala> val b = new B{}
      b: Object with B = $anon$1@2a3fa87a
      
      scala> val x = if (true) a else b
      x: ScalaObject with S = $anon$1@370410a7
      

      I would have expected to see S { type T <: S; val x: S }} as the type but instead I get {{ScalaObject with S.
      Note this is a regression from 2.9.1, where I get {{ ScalaObject with S

      {val x: ScalaObject with S; type T <: ScalaObject with S}

      }}.

      Since this affects fundamental type operations I classify a solution as critical for 2.10.

        Activity

        Hide
        Paul Phillips added a comment -

        Eugene recently opened this as SI-5302. See my comment there; this will be easy to fix one way or another, I only need to understand why/how this situation arises:

        type C = lub(A, B)
        !(A <:< C)

        Doesn't the definition of "lub" require that to be true? Both that ticket and this one lead to situations where it isn't.

        Show
        Paul Phillips added a comment - Eugene recently opened this as SI-5302 . See my comment there; this will be easy to fix one way or another, I only need to understand why/how this situation arises: type C = lub(A, B) !(A <:< C) Doesn't the definition of "lub" require that to be true? Both that ticket and this one lead to situations where it isn't.
        Hide
        Paul Phillips added a comment -

        I have pinpointed this; it's definitely NullaryMethodType not being handled in the isSubType2 typecase. I have a blunt instrument fix, but I'm investigating something more appealing.

        Show
        Paul Phillips added a comment - I have pinpointed this; it's definitely NullaryMethodType not being handled in the isSubType2 typecase. I have a blunt instrument fix, but I'm investigating something more appealing.
        Hide
        Paul Phillips added a comment -

        That's slipperier than I'd thought. Since there's a good chance that even if I come up with something it will not be quite what you want, I will table this for an opinion.

        The long and the short of it is that a subtype test is done with a nullary method type on the left and a refinement on the right. The nullary result type is a subtype of the refined type, but the nullary method itself is not. Poking around I tried letting those through; then the lub is correctly calculated and everything looks good for a while, until cleanup when it crashes here:

        
                  val t: Tree = ad.symbol.tpe match {
                    case MethodType(mparams, resType) =>
                      assert(params.length == mparams.length)
                      typedPos {
                        val sym = currentOwner.newValue(ad.pos, mkTerm("qual")) setInfo qual0.tpe
                        qual = safeREF(sym)
        
                        BLOCK(
                          VAL(sym) === qual0,
                          callAsReflective(mparams map (_.tpe), resType)
                        )
                      }
                  }
        

        With a match error because a TypeRef turns up. In case that's interesting.

        Show
        Paul Phillips added a comment - That's slipperier than I'd thought. Since there's a good chance that even if I come up with something it will not be quite what you want, I will table this for an opinion. The long and the short of it is that a subtype test is done with a nullary method type on the left and a refinement on the right. The nullary result type is a subtype of the refined type, but the nullary method itself is not. Poking around I tried letting those through; then the lub is correctly calculated and everything looks good for a while, until cleanup when it crashes here: val t: Tree = ad.symbol.tpe match { case MethodType(mparams, resType) => assert(params.length == mparams.length) typedPos { val sym = currentOwner.newValue(ad.pos, mkTerm("qual")) setInfo qual0.tpe qual = safeREF(sym) BLOCK( VAL(sym) === qual0, callAsReflective(mparams map (_.tpe), resType) ) } } With a match error because a TypeRef turns up. In case that's interesting.
        Hide
        Paul Phillips added a comment -

        This is fixed in f737e35ddf . The commit message explains what happened.

        Show
        Paul Phillips added a comment - This is fixed in f737e35ddf . The commit message explains what happened.

          People

          • Assignee:
            Paul Phillips
            Reporter:
            Martin Odersky
          • Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development