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

Infinite loop in Typers#adapt with overloaded types, erroneous trees

    Details

      Description

      I haven't minimized this one yet.

      The loop appears:

        last tree to typer: Ident(QuotableKey)
                    symbol: class <error: <none>> (flags: <synthetic> <is_error>)
         symbol definition: class <error: <none>> extends 
                       tpe: <error>
             symbol owners: class <error: <none>> -> method indexedPathCube -> trait FullPathLocator -> package mc
            context owners: method indexedPathCube -> trait FullPathLocator -> package mc
      
      
       at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1069)
      
        at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1070)
      
        at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1070)
      
        at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1070)
      
        at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1070)
      
        at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1070)
      
        at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1070)
      
        at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1070)
      
        at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1070)
      
        at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1070)
      
        at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1070)
      
              case OverloadedType(pre, alts) if !inFunMode(mode) => // (1)
                inferExprAlternative(tree, pt)
                adapt(tree, mode, pt, original)
      

        Issue Links

          Activity

          Hide
          James Iry added a comment -

          Is there any reproducer for this, minimized or not?

          Show
          James Iry added a comment - Is there any reproducer for this, minimized or not?
          Hide
          Jason Zaugg added a comment -

          Sadly, no. It slipped through my fingers as I was porting a customer's codebase do 2.10. Maybe I'll be able to reverse engineer a reproduction, or add a sanity check to prevent the loop.

          Show
          Jason Zaugg added a comment - Sadly, no. It slipped through my fingers as I was porting a customer's codebase do 2.10. Maybe I'll be able to reverse engineer a reproduction, or add a sanity check to prevent the loop.
          Hide
          Jason Zaugg added a comment - - edited

          So inspecting the code in inferExprAlternative, it would appear that it could fall through if best != NoSymbol && !competing.isEmpty && !noAlternatives && pt.isErroneous.

                  if (best == NoSymbol) {
                    // ...
                    NoBestExprAlternativeError(tree, pt, isSecondTry)
                  } else if (!competing.isEmpty) {
                    if (noAlternatives) NoBestExprAlternativeError(tree, pt, isSecondTry)
                    else if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt, isSecondTry)
                  } else {
                    tree.setSymbol(best).setType(pre.memberType(best))
                  }
          

          This commit seems relevant:

          https://github.com/scala/scala/commit/b10b58

          +        // since inferMethodAlternative modifies the state of the tree 
          +        // we have to set the type of tree to ErrorType only in the very last
          +        // fallback action that is done in the inference (tracking it manually is error prone).
          +        // This avoids entering infinite loop in doTypeApply.
          +        // TODO: maybe we should do the same thing with inferExprAlternative.
          +        if (implicitly[Context].reportErrors) setError(tree)
          

          It was refined later in:

          https://github.com/scala/scala/commit/1ddc93

          See also:

                // side-effect on the tree, break the overloaded type cycle in infer
                private def setErrorOnLastTry(lastTry: Boolean, tree: Tree) = if (lastTry) setError(tree)
          

          Actually, digging back a bit further:

          https://github.com/scala/scala/commit/c800d1

                   } else if (!competing.isEmpty) {
          -          if (secondTry) {
          -            typeErrorTree(tree, tree.symbol.tpe, pt)
          -          } else {
          -            if (!pt.isErroneous)
          -              context.ambiguousError(tree.pos, pre, best, competing.head, "expected type " + pt)
          -            setError(tree)
          -          }
          +          if (secondTry) NoBestExprAlternativeError(tree, pt)
          +          else { if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt) }
                   } else {
          

          Before that change, in the condition mentioned above, setError(tree) was called, which would have broken the loop.

          Show
          Jason Zaugg added a comment - - edited So inspecting the code in inferExprAlternative , it would appear that it could fall through if best != NoSymbol && !competing.isEmpty && !noAlternatives && pt.isErroneous . if (best == NoSymbol) { // ... NoBestExprAlternativeError(tree, pt, isSecondTry) } else if (!competing.isEmpty) { if (noAlternatives) NoBestExprAlternativeError(tree, pt, isSecondTry) else if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt, isSecondTry) } else { tree.setSymbol(best).setType(pre.memberType(best)) } This commit seems relevant: https://github.com/scala/scala/commit/b10b58 + // since inferMethodAlternative modifies the state of the tree + // we have to set the type of tree to ErrorType only in the very last + // fallback action that is done in the inference (tracking it manually is error prone). + // This avoids entering infinite loop in doTypeApply. + // TODO: maybe we should do the same thing with inferExprAlternative. + if (implicitly[Context].reportErrors) setError(tree) It was refined later in: https://github.com/scala/scala/commit/1ddc93 See also: // side-effect on the tree, break the overloaded type cycle in infer private def setErrorOnLastTry(lastTry: Boolean, tree: Tree) = if (lastTry) setError(tree) Actually, digging back a bit further: https://github.com/scala/scala/commit/c800d1 } else if (!competing.isEmpty) { - if (secondTry) { - typeErrorTree(tree, tree.symbol.tpe, pt) - } else { - if (!pt.isErroneous) - context.ambiguousError(tree.pos, pre, best, competing.head, "expected type " + pt) - setError(tree) - } + if (secondTry) NoBestExprAlternativeError(tree, pt) + else { if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt) } } else { Before that change, in the condition mentioned above, setError(tree) was called, which would have broken the loop.
          Show
          Jason Zaugg added a comment - https://github.com/scala/scala/pull/1859
          Hide
          Paul Phillips added a comment -

          5d65772762

          Show
          Paul Phillips added a comment - 5d65772762

            People

            • Assignee:
              Jason Zaugg
              Reporter:
              Jason Zaugg
            • Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development