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

instantiate gets confused if tvar is inferred to be a refinement type whose parent is a refinement type

    Details

      Description

      trait Universe {
        type Tree
      
        type SymTree <: Tree
        type NameTree <: Tree
        type RefTree <: SymTree with NameTree
      
        type Ident <: RefTree
        type Select <: RefTree
      }
      
      object Test extends App {
        val universe: Universe = null
        import universe._
        def select: Select = ???
        def ident: Ident = ???
        List(select, ident)
      }
      
      C:\Projects\KeplerUnderRefactoring\sandbox @ topic/reflection>myke compile Test.scala
      Test.scala:17: error: no type parameters for method apply: (xs: A*)List[A] in object List exist so that it can be applied to arguments (Test.universe.Select, Test.universe.Ident)
       --- because ---
      undetermined type
        List(select, ident)
        ^
      

        Activity

        Hide
        Eugene Burmako added a comment - - edited

        As displayed in the debugger, `tvar` is a refinement type:

        Test.universe.RefTree with Test.universe.SymTree with Test.universe.NameTree
        

        Mapping over this type goes into:

        case rtp @ RefinedType(parents, decls) =>
          val parents1 = parents mapConserve this
          val decls1 = mapOver(decls)
          //if ((parents1 eq parents) && (decls1 eq decls)) tp
          //else refinementOfClass(tp.typeSymbol, parents1, decls1)
          copyRefinedType(rtp, parents1, decls1)
        

        `parents` for this refinement type are:

        Test.universe.RefTree // TypeRef(pre = Test.universe.type, sym = type RefTree /* AbstractTypeSymbol */, args = Nil)
        Test.universe.SymTree with Test.universe.NameTree // RefinementTypeRef (pre = NoType, sym = Universe /* RefinementClassSymbol */, args = Nil)
        

        `tvars map instantiate` in Infer.scala calls `instantiate`:

        object instantiate extends TypeMap {
          private var excludedVars = immutable.Set[TypeVar]()
          def apply(tp: Type): Type = tp match {
            case WildcardType | BoundedWildcardType(_) | NoType =>
              throw new NoInstance("undetermined type")
            case tv @ TypeVar(origin, constr) if !tv.untouchable =>
              if (constr.inst == NoType) {
                throw new DeferredNoInstance(() =>
                  "no unique instantiation of type variable " + origin + " could be found")
              } else if (excludedVars(tv)) {
                throw new NoInstance("cyclic instantiation")
              } else {
                excludedVars += tv
                val res = apply(constr.inst)
                excludedVars -= tv
                res
              }
            case _ =>
              mapOver(tp)
          }
        }
        

        When navigating the second parent, `instantiate` sees `NoType` in `pre` and bails with NoInstance.

        Show
        Eugene Burmako added a comment - - edited As displayed in the debugger, `tvar` is a refinement type: Test.universe.RefTree with Test.universe.SymTree with Test.universe.NameTree Mapping over this type goes into: case rtp @ RefinedType(parents, decls) => val parents1 = parents mapConserve this val decls1 = mapOver(decls) //if ((parents1 eq parents) && (decls1 eq decls)) tp //else refinementOfClass(tp.typeSymbol, parents1, decls1) copyRefinedType(rtp, parents1, decls1) `parents` for this refinement type are: Test.universe.RefTree // TypeRef(pre = Test.universe.type, sym = type RefTree /* AbstractTypeSymbol */, args = Nil) Test.universe.SymTree with Test.universe.NameTree // RefinementTypeRef (pre = NoType, sym = Universe /* RefinementClassSymbol */, args = Nil) `tvars map instantiate` in Infer.scala calls `instantiate`: object instantiate extends TypeMap { private var excludedVars = immutable.Set[TypeVar]() def apply(tp: Type): Type = tp match { case WildcardType | BoundedWildcardType(_) | NoType => throw new NoInstance("undetermined type") case tv @ TypeVar(origin, constr) if !tv.untouchable => if (constr.inst == NoType) { throw new DeferredNoInstance(() => "no unique instantiation of type variable " + origin + " could be found") } else if (excludedVars(tv)) { throw new NoInstance("cyclic instantiation") } else { excludedVars += tv val res = apply(constr.inst) excludedVars -= tv res } case _ => mapOver(tp) } } When navigating the second parent, `instantiate` sees `NoType` in `pre` and bails with NoInstance.
        Hide
        Eugene Burmako added a comment - - edited

        Also it's unclear why the lub is calculated as:

        List(Test.universe.RefTree, Test.universe.SymTree with Test.universe.NameTree)
        

        But not just to:

        List(Test.universe.RefTree)
        

        (the latter is how things work with concrete types)

        Show
        Eugene Burmako added a comment - - edited Also it's unclear why the lub is calculated as: List(Test.universe.RefTree, Test.universe.SymTree with Test.universe.NameTree) But not just to: List(Test.universe.RefTree) (the latter is how things work with concrete types)
        Hide
        Adriaan Moors added a comment -

        it looks like refinement typerefs lost their prefix in the Great Types Refactor of https://github.com/scala/scala/commit/5f5029d2ac and https://github.com/scala/scala/commit/f7535f7290 (specifically: https://github.com/scala/scala/commit/f7535f7290#L2R2260)

        This issue is an example of where the prefix is something interesting, and not just good old `NoType` (as assumed by https://github.com/scala/scala/commit/f7535f7290#L2R1880). `NoType` indicates type inference failure. I could imagine it being `NoPrefix` once in a while, but never `NoType`.

        fix in https://github.com/adriaanm/scala/tree/ticket/5829 pending it passing the test suite

        Show
        Adriaan Moors added a comment - it looks like refinement typerefs lost their prefix in the Great Types Refactor of https://github.com/scala/scala/commit/5f5029d2ac and https://github.com/scala/scala/commit/f7535f7290 (specifically: https://github.com/scala/scala/commit/f7535f7290#L2R2260 ) This issue is an example of where the prefix is something interesting, and not just good old `NoType` (as assumed by https://github.com/scala/scala/commit/f7535f7290#L2R1880 ). `NoType` indicates type inference failure. I could imagine it being `NoPrefix` once in a while, but never `NoType`. fix in https://github.com/adriaanm/scala/tree/ticket/5829 pending it passing the test suite
        Show
        Eugene Burmako added a comment - Fixed in https://github.com/scala/scala/commit/6bb5975289c5b11cb8c88dd4629286956b5d3d27
        Hide
        Paul Phillips added a comment -

        Hey, I never saw this ticket.

        For the record, and maybe you know this because you say "prefix is something interesting" which sounds like a reference to the comment, but I did this because it was documented by martin that "refinements always have non-interesting prefixes."

        https://github.com/scala/scala/commit/7ebc41cda2#L0R1453

        Show
        Paul Phillips added a comment - Hey, I never saw this ticket. For the record, and maybe you know this because you say "prefix is something interesting" which sounds like a reference to the comment, but I did this because it was documented by martin that "refinements always have non-interesting prefixes." https://github.com/scala/scala/commit/7ebc41cda2#L0R1453

          People

          • Assignee:
            Eugene Burmako
            Reporter:
            Eugene Burmako
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development