@@ -625,31 +625,36 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
625
625
block.tpe namedPartsWith (tp => locals.contains(tp.symbol))
626
626
}
627
627
628
- /** Check that expression's type can be expressed without references to locally defined
629
- * symbols. The following two remedies are tried before giving up:
630
- * 1. If the expected type of the expression is fully defined, pick it as the
631
- * type of the result expressed by adding a type ascription.
632
- * 2. If (1) fails, force all type variables so that the block's type is
633
- * fully defined and try again.
628
+ /** Ensure that an expression's type can be expressed without references to locally defined
629
+ * symbols. This is done by adding a type ascription of a widened type that does
630
+ * not refer to the locally defined symbols. The widened type is computed using
631
+ * `TyperAssigner#avoid`. However, if the expected type is fully defined and not
632
+ * a supertype of the widened type, we ascribe with the expected type instead.
633
+ *
634
+ * There's a special case having to do with anonymous classes. Sometimes the
635
+ * expected type of a block is the anonymous class defined inside it. In that
636
+ * case there's technically a leak which is not removed by the ascription.
634
637
*/
635
- protected def ensureNoLocalRefs (tree : Tree , pt : Type , localSyms : => List [Symbol ], forcedDefined : Boolean = false )(implicit ctx : Context ): Tree = {
638
+ protected def ensureNoLocalRefs (tree : Tree , pt : Type , localSyms : => List [Symbol ])(implicit ctx : Context ): Tree = {
636
639
def ascribeType (tree : Tree , pt : Type ): Tree = tree match {
637
640
case block @ Block (stats, expr) =>
638
641
val expr1 = ascribeType(expr, pt)
639
642
cpy.Block (block)(stats, expr1) withType expr1.tpe // no assignType here because avoid is redundant
640
643
case _ =>
641
644
Typed (tree, TypeTree (pt.simplified))
642
645
}
643
- val leaks = escapingRefs(tree, localSyms)
644
- if (leaks.isEmpty) tree
645
- else if (isFullyDefined(pt, ForceDegree .none)) ascribeType(tree, pt)
646
- else if (! forcedDefined) {
646
+ def noLeaks (t : Tree ): Boolean = escapingRefs(t, localSyms).isEmpty
647
+ if (noLeaks(tree)) tree
648
+ else {
647
649
fullyDefinedType(tree.tpe, " block" , tree.pos)
648
- val tree1 = ascribeType(tree, avoid(tree.tpe, localSyms))
649
- ensureNoLocalRefs(tree1, pt, localSyms, forcedDefined = true )
650
- } else
651
- errorTree(tree,
652
- em " local definition of ${leaks.head.name} escapes as part of expression's type ${tree.tpe}" /* ; full type: ${result.tpe.toString}"*/ )
650
+ var avoidingType = avoid(tree.tpe, localSyms)
651
+ val ptDefined = isFullyDefined(pt, ForceDegree .none)
652
+ if (ptDefined && ! (avoidingType <:< pt)) avoidingType = pt
653
+ val tree1 = ascribeType(tree, avoidingType)
654
+ assert(ptDefined || noLeaks(tree1), // `ptDefined` needed because of special case of anonymous classes
655
+ i " leak: ${escapingRefs(tree1, localSyms).toList}%, % in $tree1" )
656
+ tree1
657
+ }
653
658
}
654
659
655
660
def typedIf (tree : untpd.If , pt : Type )(implicit ctx : Context ): Tree = track(" typedIf" ) {
0 commit comments