From 1be3b2fd50487b20812b57ef485e8983dea7c289 Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Mon, 27 Oct 2014 14:03:10 +0100
Subject: [PATCH 01/16] Extracting TypedCases to be reused for typedTry

---
 src/dotty/tools/dotc/typer/Typer.scala | 74 ++++++++++++++------------
 1 file changed, 39 insertions(+), 35 deletions(-)

diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 1afa5f9f33f9..42b973eed5bd 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -590,47 +590,51 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
         val selType = widenForMatchSelector(
             fullyDefinedType(sel1.tpe, "pattern selector", tree.pos))
 
-        /** gadtSyms = "all type parameters of enclosing methods that appear
-         *              non-variantly in the selector type" todo: should typevars
-         *              which appear with variances +1 and -1 (in different
-         *              places) be considered as well?
-         */
-        val gadtSyms: Set[Symbol] = ctx.traceIndented(i"GADT syms of $selType", gadts) {
-          val accu = new TypeAccumulator[Set[Symbol]] {
-            def apply(tsyms: Set[Symbol], t: Type): Set[Symbol] = {
-              val tsyms1 = t match {
-                case tr: TypeRef if (tr.symbol is TypeParam) && tr.symbol.owner.isTerm && variance == 0 =>
-                  tsyms + tr.symbol
-                case _ =>
-                  tsyms
-              }
-              foldOver(tsyms1, t)
-            }
-          }
-          accu(Set.empty, selType)
-        }
-
-        val cases1 = tree.cases mapconserve (typedCase(_, pt, selType, gadtSyms))
+        val cases1 = typedCases(tree.cases, selType, pt)
         assignType(cpy.Match(tree)(sel1, cases1), cases1)
     }
   }
 
-  def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = track("typedCase") {
-    def caseRest(pat: Tree)(implicit ctx: Context) = {
-      gadtSyms foreach (_.resetGADTFlexType)
-      pat foreachSubTree {
-        case b: Bind =>
-          if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
-          else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos)
-        case _ =>
+  def typedCases(cases: List[untpd.CaseDef], selType: Type, pt: Type)(implicit ctx: Context) = {
+
+    /** gadtSyms = "all type parameters of enclosing methods that appear
+      *              non-variantly in the selector type" todo: should typevars
+      *              which appear with variances +1 and -1 (in different
+      *              places) be considered as well?
+      */
+    val gadtSyms: Set[Symbol] = ctx.traceIndented(i"GADT syms of $selType", gadts) {
+      val accu = new TypeAccumulator[Set[Symbol]] {
+        def apply(tsyms: Set[Symbol], t: Type): Set[Symbol] = {
+          val tsyms1 = t match {
+            case tr: TypeRef if (tr.symbol is TypeParam) && tr.symbol.owner.isTerm && variance == 0 =>
+              tsyms + tr.symbol
+            case _ =>
+              tsyms
+          }
+          foldOver(tsyms1, t)
+        }
+      }
+      accu(Set.empty, selType)
+    }
+
+    def typedCase(tree: untpd.CaseDef): CaseDef = track("typedCase") {
+      def caseRest(pat: Tree)(implicit ctx: Context) = {
+        gadtSyms foreach (_.resetGADTFlexType)
+        pat foreachSubTree {
+          case b: Bind =>
+            if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
+            else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos)
+          case _ =>
+        }
+        val guard1 = typedExpr(tree.guard, defn.BooleanType)
+        val body1 = typedExpr(tree.body, pt)
+        assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1)
       }
-      val guard1 = typedExpr(tree.guard, defn.BooleanType)
-      val body1 = typedExpr(tree.body, pt)
-      assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1)
+      val doCase: () => CaseDef =
+        () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope)
+      (doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))()
     }
-    val doCase: () => CaseDef =
-      () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope)
-    (doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))()
+    cases mapconserve typedCase
   }
 
   def typedReturn(tree: untpd.Return)(implicit ctx: Context): Return = track("typedReturn") {

From e44e4aa7e818d7eb90316880cd0758c65298f2a8 Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Mon, 27 Oct 2014 14:59:07 +0100
Subject: [PATCH 02/16] TailRec now handles Try with Ident-handler

---
 src/dotty/tools/dotc/transform/TailRec.scala |  1 +
 tests/pos/tryTyping.scala                    | 14 ++++++++++++++
 2 files changed, 15 insertions(+)
 create mode 100644 tests/pos/tryTyping.scala

diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala
index 71eedadab782..c3c1ed9d3f63 100644
--- a/src/dotty/tools/dotc/transform/TailRec.scala
+++ b/src/dotty/tools/dotc/transform/TailRec.scala
@@ -237,6 +237,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
             case Block(List((d: DefDef)), cl@Closure(Nil, _, EmptyTree)) =>
               val newDef = cpy.DefDef(d)(rhs = transform(d.rhs))
               Block(List(newDef), cl)
+            case t: Ident => t // handler is an external function
             case _ => assert(false, s"failed to deconstruct try handler ${t.show}"); ???
           }
         }
diff --git a/tests/pos/tryTyping.scala b/tests/pos/tryTyping.scala
new file mode 100644
index 000000000000..35180bee9aeb
--- /dev/null
+++ b/tests/pos/tryTyping.scala
@@ -0,0 +1,14 @@
+object tryTyping{
+  def foo: Int = {
+    try{???; 1}
+    catch {
+      case e: Exception => 2
+    }
+  }
+
+  def foo2: Int = {
+    val a: (Throwable => Int) = _ match {case _ => 2}
+    try{???; 1}
+    catch a
+  }
+}
\ No newline at end of file

From 09c5ad4f92aab61053e70f2e6e863271c777dd14 Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Tue, 28 Oct 2014 11:56:37 +0100
Subject: [PATCH 03/16] New kind of EmptyTree for indicating exception selector
 in Try blocks.

Simplifies a lot handling of Try blocks in patmat and in tailrec.
---
 src/dotty/tools/dotc/ast/Trees.scala | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala
index f1ccfdb75e3e..d7dcff9923c2 100644
--- a/src/dotty/tools/dotc/ast/Trees.scala
+++ b/src/dotty/tools/dotc/ast/Trees.scala
@@ -804,7 +804,7 @@ object Trees {
     type ThisTree[-T >: Untyped] = Thicket[T]
     override def isEmpty: Boolean = trees.isEmpty
     override def toList: List[Tree[T]] = flatten(trees)
-    override def toString = if (isEmpty) "EmptyTree" else "Thicket(" + trees.mkString(", ") + ")"
+    override def toString = if(this eq theExceptionHandlerSel) "ExceptionSel" else if (isEmpty) "EmptyTree" else "Thicket(" + trees.mkString(", ") + ")"
     override def withPos(pos: Position): this.type = {
       val newTrees = trees.map(_.withPos(pos))
       new Thicket[T](newTrees).asInstanceOf[this.type]
@@ -818,9 +818,11 @@ object Trees {
 
   val theEmptyTree: Thicket[Type] = Thicket(Nil)
   val theEmptyValDef = new EmptyValDef[Type]
+  val theExceptionHandlerSel: Thicket[Type] = Thicket(Nil)
 
   def genericEmptyValDef[T >: Untyped]: ValDef[T] = theEmptyValDef.asInstanceOf[ValDef[T]]
   def genericEmptyTree[T >: Untyped]: Thicket[T] = theEmptyTree.asInstanceOf[Thicket[T]]
+  def genericExceptionHandlerSel[T >: Untyped]: Thicket[T] = theExceptionHandlerSel.asInstanceOf[Thicket[T]]
 
   def flatten[T >: Untyped](trees: List[Tree[T]]): List[Tree[T]] = {
     var buf: ListBuffer[Tree[T]] = null
@@ -911,6 +913,7 @@ object Trees {
     type Thicket = Trees.Thicket[T]
 
     val EmptyTree: Thicket = genericEmptyTree
+    val ExceptionHandlerSel: Thicket = genericExceptionHandlerSel // selector used in exception hanlder of Try nodes
     val EmptyValDef: ValDef = genericEmptyValDef
 
     // ----- Auxiliary creation methods ------------------

From 5613295e61f4328f2caaa20dbc21c3a0ce4937ac Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Tue, 28 Oct 2014 11:57:36 +0100
Subject: [PATCH 04/16] Typer should leave inline exception handlers inline.

---
 src/dotty/tools/dotc/typer/Typer.scala |  8 +++++++-
 tests/pos/tryTyping.scala              | 10 ++++++++--
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 42b973eed5bd..e7b6f45d4d6a 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -663,7 +663,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
 
   def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = track("typedTry") {
     val expr1 = typed(tree.expr, pt)
-    val handler1 = typed(tree.handler, defn.FunctionType(defn.ThrowableType :: Nil, pt))
+    val handler1: Tree = tree.handler match {
+      case h: untpd.Match if ((h.selector eq EmptyTree)                   // comes from parser
+                               || (h.selector eq ExceptionHandlerSel)) => // during retyping
+        val cases1 = typedCases(h.cases, defn.ThrowableType, pt)
+        assignType(untpd.Match(ExceptionHandlerSel, cases1), cases1)
+      case _ => typed(tree.handler, defn.FunctionType(defn.ThrowableType :: Nil, pt))
+    }
     val finalizer1 = typed(tree.finalizer, defn.UnitType)
     assignType(cpy.Try(tree)(expr1, handler1, finalizer1), expr1, handler1)
   }
diff --git a/tests/pos/tryTyping.scala b/tests/pos/tryTyping.scala
index 35180bee9aeb..a2aeb17c8edc 100644
--- a/tests/pos/tryTyping.scala
+++ b/tests/pos/tryTyping.scala
@@ -7,8 +7,14 @@ object tryTyping{
   }
 
   def foo2: Int = {
-    val a: (Throwable => Int) = _ match {case _ => 2}
+    val a2: (Throwable => Int) = _ match {case _ => 2}
     try{???; 1}
-    catch a
+    catch a2
+  }
+
+  def foo3: Int = {
+    val a3: (Int => Throwable => Int) = (b: Int) => _ match {case _ => b}
+    try{???; 1}
+    catch a3(3)
   }
 }
\ No newline at end of file

From f2ea8dd31caadfe5a7f3d14cea7c3bc3f0c56110 Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Tue, 28 Oct 2014 11:58:33 +0100
Subject: [PATCH 05/16] Fix TailRec to handle inline exception handlers and
 non-trivial not-inlined

---
 src/dotty/tools/dotc/transform/TailRec.scala | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala
index c3c1ed9d3f63..26b3cc376c74 100644
--- a/src/dotty/tools/dotc/transform/TailRec.scala
+++ b/src/dotty/tools/dotc/transform/TailRec.scala
@@ -237,7 +237,9 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
             case Block(List((d: DefDef)), cl@Closure(Nil, _, EmptyTree)) =>
               val newDef = cpy.DefDef(d)(rhs = transform(d.rhs))
               Block(List(newDef), cl)
-            case t: Ident => t // handler is an external function
+            case Match(ExceptionHandlerSel, _) =>
+              transform(t)
+            case _: Ident|_: Apply| _: TypeApply => t // handler is an external function
             case _ => assert(false, s"failed to deconstruct try handler ${t.show}"); ???
           }
         }

From 6fe3a2d87e220b28ffff808c476313c456b743cb Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Tue, 28 Oct 2014 14:32:16 +0100
Subject: [PATCH 06/16] Fix Erasure.Boxing isBox and isUnbox

Box& unbox methods are defined in companion objects.
---
 src/dotty/tools/dotc/transform/Erasure.scala | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala
index 31397e08a723..7937f1dcffec 100644
--- a/src/dotty/tools/dotc/transform/Erasure.scala
+++ b/src/dotty/tools/dotc/transform/Erasure.scala
@@ -111,10 +111,10 @@ object Erasure extends TypeTestsCasts{
   object Boxing {
 
     def isUnbox(sym: Symbol)(implicit ctx: Context) =
-      sym.name == nme.unbox && (defn.ScalaBoxedClasses contains sym.owner)
+      sym.name == nme.unbox && (defn.ScalaBoxedClasses contains sym.owner.linkedClass)
 
     def isBox(sym: Symbol)(implicit ctx: Context) =
-      sym.name == nme.box && (defn.ScalaValueClasses contains sym.owner)
+      sym.name == nme.box && (defn.ScalaValueClasses contains sym.owner.linkedClass)
 
     def boxMethod(cls: ClassSymbol)(implicit ctx: Context) =
       cls.linkedClass.info.member(nme.box).symbol

From 049bcb5f22f1d663f9c5ad0ab38594a22e7f6f2a Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Tue, 28 Oct 2014 14:32:52 +0100
Subject: [PATCH 07/16] Add Patmat to Context

---
 src/dotty/tools/dotc/core/Phases.scala | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala
index 7b589fec1e0e..476ce6e553f5 100644
--- a/src/dotty/tools/dotc/core/Phases.scala
+++ b/src/dotty/tools/dotc/core/Phases.scala
@@ -9,7 +9,7 @@ import Denotations._
 import config.Printers._
 import scala.collection.mutable.{ListBuffer, ArrayBuffer}
 import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform}
-import dotty.tools.dotc.transform.{TreeTransforms, ExplicitOuter, Erasure, Flatten, GettersSetters}
+import dotty.tools.dotc.transform._
 import Periods._
 import typer.{FrontEnd, RefChecks}
 import ast.tpd
@@ -167,6 +167,7 @@ object Phases {
     private val typerCache = new PhaseCache(classOf[FrontEnd])
     private val refChecksCache = new PhaseCache(classOf[RefChecks])
     private val erasureCache = new PhaseCache(classOf[Erasure])
+    private val patmatCache = new PhaseCache(classOf[PatternMatcher])
     private val flattenCache = new PhaseCache(classOf[Flatten])
     private val explicitOuterCache = new PhaseCache(classOf[ExplicitOuter])
     private val gettersSettersCache = new PhaseCache(classOf[GettersSetters])
@@ -174,6 +175,7 @@ object Phases {
     def typerPhase = typerCache.phase
     def refchecksPhase = refChecksCache.phase
     def erasurePhase = erasureCache.phase
+    def patmatPhase = patmatCache.phase
     def flattenPhase = flattenCache.phase
     def explicitOuterPhase = explicitOuterCache.phase
     def gettersSettersPhase = gettersSettersCache.phase

From ba4fee76dd5d93e52672e223633539e932ccf0b6 Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Tue, 28 Oct 2014 14:34:34 +0100
Subject: [PATCH 08/16] Stop patmat from using selector pos for Try.

It doesn't exist.
Also use symbol with 'ex' name as selector for exceptions
---
 .../tools/dotc/transform/PatternMatcher.scala  | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala
index 8af1b4a214d0..6e8b58f56cec 100644
--- a/src/dotty/tools/dotc/transform/PatternMatcher.scala
+++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala
@@ -50,7 +50,8 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
 
   override def transformMatch(tree: Match)(implicit ctx: Context, info: TransformerInfo): Tree = {
     val translated = new Translator()(ctx).translator.translateMatch(tree)
-    translated.ensureConforms(tree.tpe)
+
+    Typed(translated.ensureConforms(tree.tpe), TypeTree(tree.tpe))
   }
 
   class Translator(implicit ctx: Context) {
@@ -1136,7 +1137,13 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
       *   this could probably optimized... (but note that the matchStrategy must be solved for each nested patternmatch)
       */
     def translateMatch(match_ : Match): Tree = {
-      val Match(selector, cases) = match_
+      val Match(sel, cases) = match_
+
+      val selectorTp = elimAnonymousClass(sel.tpe.widen/*withoutAnnotations*/)
+
+      val selectorSym =
+        if (sel ne ExceptionHandlerSel) freshSym(sel.pos, selectorTp, "selector")
+        else freshSym(match_.pos, defn.ThrowableType, "ex")
 
       val (nonSyntheticCases, defaultOverride) = cases match {
         case init :+ last if isSyntheticDefaultCase(last) => (init, Some(((scrut: Symbol) => last.body)))
@@ -1155,8 +1162,6 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
 
       //val start = if (Statistics.canEnable) Statistics.startTimer(patmatNanos) else null
 
-      val selectorTp = elimAnonymousClass(selector.tpe.widen/*withoutAnnotations*/)
-
       // when one of the internal cps-type-state annotations is present, strip all CPS annotations
       ///val origPt  = removeCPSFromPt(match_.tpe)
       // relevant test cases: pos/existentials-harmful.scala, pos/gadt-gilles.scala, pos/t2683.scala, pos/virtpatmat_exist4.scala
@@ -1164,14 +1169,13 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
       val pt = match_.tpe.widen //repeatedToSeq(origPt)
 
       // val packedPt = repeatedToSeq(typer.packedType(match_, context.owner))
-      val selectorSym = freshSym(selector.pos, selectorTp, "selector")
       selectorSym.setFlag(Flags.SyntheticCase)
 
       // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala  with -Xexperimental
-      val combined = combineCases(selector, selectorSym, nonSyntheticCases map translateCase(selectorSym, pt), pt, ctx.owner, defaultOverride)
+      val combined = combineCases(sel, selectorSym, nonSyntheticCases map translateCase(selectorSym, pt), pt, ctx.owner, defaultOverride)
 
       // if (Statistics.canEnable) Statistics.stopTimer(patmatNanos, start)
-      Block(List(ValDef(selectorSym,selector)), combined)
+      Block(List(ValDef(selectorSym, sel)), combined)
     }
 
     // return list of typed CaseDefs that are supported by the backend (typed/bind/wildcard)

From f60f81fd7b6ed5f6fb692069ebbc46f2b8098894 Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Tue, 28 Oct 2014 16:04:00 +0100
Subject: [PATCH 09/16] Flag dotty deviation for return type which is part of
 cake.

---
 src/dotty/tools/dotc/transform/PatternMatcher.scala | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala
index 6e8b58f56cec..9ba8d54c79a5 100644
--- a/src/dotty/tools/dotc/transform/PatternMatcher.scala
+++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala
@@ -779,10 +779,11 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
       // is this purely a type test, e.g. no outer check, no equality tests (used in switch emission)
       //def isPureTypeTest = renderCondition(pureTypeTestChecker)
 
-      def impliesBinderNonNull(binder: Symbol):Boolean =
+      def impliesBinderNonNull(binder: Symbol): Boolean =
       // @odersky: scalac is able to infer in this method that nonNullImpliedByTestChecker.Result,
       // dotty instead infers type projection TreeMakers.this.TypeTestTreeMaker.TypeTestCondStrategy#Result
       // which in turn doesn't typecheck in this method. Can you please explain why?
+      // dotty deviation
         renderCondition(nonNullImpliedByTestChecker(binder)).asInstanceOf[Boolean]
 
       override def toString = "TT"+((expectedTp, testedBinder.name, nextBinderTp))

From d923d70e2ec381b22d7ce44d5bd58d6d45dab6bc Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Tue, 28 Oct 2014 16:04:41 +0100
Subject: [PATCH 10/16] Make typing of Try nodes idempotent

---
 src/dotty/tools/dotc/transform/TailRec.scala |  2 +-
 src/dotty/tools/dotc/typer/Typer.scala       | 10 +++++++++-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala
index 26b3cc376c74..b3f63bcaf43d 100644
--- a/src/dotty/tools/dotc/transform/TailRec.scala
+++ b/src/dotty/tools/dotc/transform/TailRec.scala
@@ -237,7 +237,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
             case Block(List((d: DefDef)), cl@Closure(Nil, _, EmptyTree)) =>
               val newDef = cpy.DefDef(d)(rhs = transform(d.rhs))
               Block(List(newDef), cl)
-            case Match(ExceptionHandlerSel, _) =>
+            case Match(Typed(ExceptionHandlerSel, _), _) =>
               transform(t)
             case _: Ident|_: Apply| _: TypeApply => t // handler is an external function
             case _ => assert(false, s"failed to deconstruct try handler ${t.show}"); ???
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index e7b6f45d4d6a..355b9f263ad3 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -21,6 +21,7 @@ import Flags._
 import Decorators._
 import ErrorReporting._
 import EtaExpansion.etaExpand
+import dotty.tools.dotc.transform.Erasure.Boxing
 import util.Positions._
 import util.common._
 import util.SourcePosition
@@ -344,6 +345,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
       assignType(cpy.Typed(tree)(expr1, tpt1), tpt1)
     }
     tree.expr match {
+      case ExceptionHandlerSel if (tree.tpt.tpe == defn.ThrowableType) =>
+        tree withType defn.ThrowableType
       case id: untpd.Ident if (ctx.mode is Mode.Pattern) && isVarPattern(id) =>
         if (id.name == nme.WILDCARD) regularTyped(isWildcard = true)
         else {
@@ -667,8 +670,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
       case h: untpd.Match if ((h.selector eq EmptyTree)                   // comes from parser
                                || (h.selector eq ExceptionHandlerSel)) => // during retyping
         val cases1 = typedCases(h.cases, defn.ThrowableType, pt)
-        assignType(untpd.Match(ExceptionHandlerSel, cases1), cases1)
+        assignType(untpd.Match(Typed(ExceptionHandlerSel, TypeTree(defn.ThrowableType)), cases1), cases1)
+      case Typed(handler, tpe) if ctx.phaseId > ctx.patmatPhase.id =>  // we are retyping an expanded pattern
+        typed(tree.handler, pt)
+      case Apply(bx, List(Typed(handler, tpe))) if ctx.erasedTypes && Boxing.isBox(bx.symbol) =>
+        typed(tree.handler, pt)
       case _ => typed(tree.handler, defn.FunctionType(defn.ThrowableType :: Nil, pt))
+
     }
     val finalizer1 = typed(tree.finalizer, defn.UnitType)
     assignType(cpy.Try(tree)(expr1, handler1, finalizer1), expr1, handler1)

From 6dcd16ab2040a5a337e78bc77e40228dc9944662 Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Tue, 28 Oct 2014 16:06:43 +0100
Subject: [PATCH 11/16] Rewrite assignType for Try

After erasure was always wrong(didn't include the type of handler).
Now it's able to use both Closures and desugared Math nodes.
---
 src/dotty/tools/dotc/typer/TypeAssigner.scala | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala
index cb6fefab1472..193af8f0e48f 100644
--- a/src/dotty/tools/dotc/typer/TypeAssigner.scala
+++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala
@@ -314,8 +314,11 @@ trait TypeAssigner {
     tree.withType(defn.NothingType)
 
   def assignType(tree: untpd.Try, expr: Tree, handler: Tree)(implicit ctx: Context) = {
-    val handlerTypeArgs = handler.tpe.baseArgTypesHi(defn.FunctionClass(1))
-    tree.withType(if (handlerTypeArgs.nonEmpty) expr.tpe | handlerTypeArgs(1) else expr.tpe)
+    if(handler.isEmpty) tree.withType(expr.tpe)
+    else if(handler.tpe.derivesFrom(defn.FunctionClass(1))) {
+      val handlerTypeArgs = handler.tpe.baseArgTypesHi(defn.FunctionClass(1))
+      tree.withType(if (handlerTypeArgs.nonEmpty) expr.tpe | handlerTypeArgs(1) else expr.tpe /*| Object, as function returns boxed value ??? */)
+    } else tree.withType(expr.tpe | handler.tpe)
   }
 
   def assignType(tree: untpd.Throw)(implicit ctx: Context) =

From e07e9a3986ec59cab1f0ec2b9b4458fd6a64d8c8 Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Tue, 28 Oct 2014 21:02:42 +0100
Subject: [PATCH 12/16] Restructure Try node.

Now Try node always has cases as handlers.
In case handler is an Ident of type Throwable => T
than it's desugared to a CaseDef during parsing.
---
 src/dotty/tools/dotc/ast/Trees.scala             | 16 ++++++++--------
 src/dotty/tools/dotc/ast/tpd.scala               | 16 ++++++++--------
 src/dotty/tools/dotc/ast/untpd.scala             |  2 +-
 src/dotty/tools/dotc/core/StdNames.scala         |  1 +
 .../tools/dotc/core/pickling/UnPickler.scala     |  2 +-
 src/dotty/tools/dotc/parsing/Parsers.scala       | 10 +++++++++-
 .../tools/dotc/printing/RefinedPrinter.scala     | 13 +++++++++----
 src/dotty/tools/dotc/transform/LazyVals.scala    | 15 ++++++---------
 .../tools/dotc/transform/PatternMatcher.scala    |  2 +-
 src/dotty/tools/dotc/transform/TailRec.scala     |  9 ++++-----
 .../tools/dotc/transform/TreeTransform.scala     |  4 ++--
 src/dotty/tools/dotc/typer/TypeAssigner.scala    |  9 +++------
 src/dotty/tools/dotc/typer/Typer.scala           | 15 ++-------------
 13 files changed, 55 insertions(+), 59 deletions(-)

diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala
index d7dcff9923c2..4cd41cc2e925 100644
--- a/src/dotty/tools/dotc/ast/Trees.scala
+++ b/src/dotty/tools/dotc/ast/Trees.scala
@@ -611,7 +611,7 @@ object Trees {
    *
    *    Match(EmptyTree, <case x: Throwable => $anonfun(x)>)
    */
-  case class Try[-T >: Untyped] private[ast] (expr: Tree[T], handler: Tree[T], finalizer: Tree[T])
+  case class Try[-T >: Untyped] private[ast] (expr: Tree[T], cases: List[CaseDef[T]], finalizer: Tree[T])
     extends TermTree[T] {
     type ThisTree[-T >: Untyped] = Try[T]
   }
@@ -1027,9 +1027,9 @@ object Trees {
         case tree: Return if (expr eq tree.expr) && (from eq tree.from) => tree
         case _ => finalize(tree, untpd.Return(expr, from))
       }
-      def Try(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): Try = tree match {
-        case tree: Try if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree
-        case _ => finalize(tree, untpd.Try(expr, handler, finalizer))
+      def Try(tree: Tree)(expr: Tree, cases: List[CaseDef], finalizer: Tree)(implicit ctx: Context): Try = tree match {
+        case tree: Try if (expr eq tree.expr) && (cases eq tree.cases) && (finalizer eq tree.finalizer) => tree
+        case _ => finalize(tree, untpd.Try(expr, cases, finalizer))
       }
       def Throw(tree: Tree)(expr: Tree)(implicit ctx: Context): Throw = tree match {
         case tree: Throw if (expr eq tree.expr) => tree
@@ -1131,8 +1131,8 @@ object Trees {
         Closure(tree: Tree)(env, meth, tpt)
       def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef =
         CaseDef(tree: Tree)(pat, guard, body)
-      def Try(tree: Try)(expr: Tree = tree.expr, handler: Tree = tree.handler, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try =
-        Try(tree: Tree)(expr, handler, finalizer)
+      def Try(tree: Try)(expr: Tree = tree.expr, cases: List[CaseDef] = tree.cases, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try =
+        Try(tree: Tree)(expr, cases, finalizer)
       def UnApply(tree: UnApply)(fun: Tree = tree.fun, implicits: List[Tree] = tree.implicits, patterns: List[Tree] = tree.patterns): UnApply =
         UnApply(tree: Tree)(fun, implicits, patterns)
       def ValDef(tree: ValDef)(mods: Modifiers = tree.mods, name: TermName = tree.name, tpt: Tree = tree.tpt, rhs: Tree = tree.rhs): ValDef =
@@ -1184,8 +1184,8 @@ object Trees {
           cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body))
         case Return(expr, from) =>
           cpy.Return(tree)(transform(expr), transformSub(from))
-        case Try(block, handler, finalizer) =>
-          cpy.Try(tree)(transform(block), transform(handler), transform(finalizer))
+        case Try(block, cases, finalizer) =>
+          cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer))
         case Throw(expr) =>
           cpy.Throw(tree)(transform(expr))
         case SeqLiteral(elems) =>
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala
index 4c21fcf49229..d0f64f5a7b24 100644
--- a/src/dotty/tools/dotc/ast/tpd.scala
+++ b/src/dotty/tools/dotc/ast/tpd.scala
@@ -113,8 +113,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
   def Return(expr: Tree, from: Tree)(implicit ctx: Context): Return =
     ta.assignType(untpd.Return(expr, from))
 
-  def Try(block: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): Try =
-    ta.assignType(untpd.Try(block, handler, finalizer), block, handler)
+  def Try(block: Tree, cases: List[CaseDef], finalizer: Tree)(implicit ctx: Context): Try =
+    ta.assignType(untpd.Try(block, cases, finalizer), block, cases)
 
   def Throw(expr: Tree)(implicit ctx: Context): Throw =
     ta.assignType(untpd.Throw(expr))
@@ -457,11 +457,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
     override def Return(tree: Tree)(expr: Tree, from: Tree)(implicit ctx: Context): Return =
       ta.assignType(untpd.cpy.Return(tree)(expr, from))
 
-    override def Try(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): Try = {
-      val tree1 = untpd.cpy.Try(tree)(expr, handler, finalizer)
+    override def Try(tree: Tree)(expr: Tree, cases: List[CaseDef], finalizer: Tree)(implicit ctx: Context): Try = {
+      val tree1 = untpd.cpy.Try(tree)(expr, cases, finalizer)
       tree match {
-        case tree: Try if (expr.tpe eq tree.expr.tpe) && (handler.tpe eq tree.handler.tpe) => tree1.withTypeUnchecked(tree.tpe)
-        case _ => ta.assignType(tree1, expr, handler)
+        case tree: Try if (expr.tpe eq tree.expr.tpe) && (sameTypes(cases, tree.cases)) => tree1.withTypeUnchecked(tree.tpe)
+        case _ => ta.assignType(tree1, expr, cases)
       }
     }
 
@@ -490,8 +490,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
       Closure(tree: Tree)(env, meth, tpt)
     override def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef =
       CaseDef(tree: Tree)(pat, guard, body)
-    override def Try(tree: Try)(expr: Tree = tree.expr, handler: Tree = tree.handler, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try =
-      Try(tree: Tree)(expr, handler, finalizer)
+    override def Try(tree: Try)(expr: Tree = tree.expr, cases: List[CaseDef] = tree.cases, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try =
+      Try(tree: Tree)(expr, cases, finalizer)
   }
 
   implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal {
diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala
index 5a6c9fa89012..a5072bc96cb7 100644
--- a/src/dotty/tools/dotc/ast/untpd.scala
+++ b/src/dotty/tools/dotc/ast/untpd.scala
@@ -123,7 +123,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
   def Match(selector: Tree, cases: List[CaseDef]): Match = new Match(selector, cases)
   def CaseDef(pat: Tree, guard: Tree, body: Tree): CaseDef = new CaseDef(pat, guard, body)
   def Return(expr: Tree, from: Tree): Return = new Return(expr, from)
-  def Try(expr: Tree, handler: Tree, finalizer: Tree): Try = new Try(expr, handler, finalizer)
+  def Try(expr: Tree, cases: List[CaseDef], finalizer: Tree): Try = new Try(expr, cases, finalizer)
   def Throw(expr: Tree): Throw = new Throw(expr)
   def SeqLiteral(elems: List[Tree]): SeqLiteral = new SeqLiteral(elems)
   def JavaSeqLiteral(elems: List[Tree]): JavaSeqLiteral = new JavaSeqLiteral(elems)
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index f7354a8b423d..99290f0848d0 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -121,6 +121,7 @@ object StdNames {
     val SUPER_PREFIX: N               = "super$"
     val TRAIT_SETTER_PREFIX: N        = "_setter_$"
     val WHILE_PREFIX: N               = "while$"
+    val DEFAULT_EXCEPTION_NAME: N = "ex$"
 
     // value types (and AnyRef) are all used as terms as well
     // as (at least) arguments to the @specialize annotation.
diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala
index 3510462cc329..60000441c22c 100644
--- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala
+++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala
@@ -1035,7 +1035,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot:
         val block = readTreeRef()
         val finalizer = readTreeRef()
         val catches = until(end, readCaseDefRef)
-        Try(block, Match(EmptyTree, catches), finalizer)
+        Try(block, catches, finalizer)
 
       case THROWtree =>
         Throw(readTreeRef())
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index 1efc2d31ca78..a787f9712107 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -914,7 +914,15 @@ object Parsers {
           val finalizer =
             if (handler.isEmpty || in.token == FINALLY) { accept(FINALLY); expr() }
             else EmptyTree
-          Try(body, handler, finalizer)
+          handler match {
+            case Match(sel, cases) => Try(body, cases, finalizer)
+            case EmptyTree => Try(body, Nil, finalizer)
+            case _ =>
+              Try(body,
+                List(CaseDef(Ident(nme.DEFAULT_EXCEPTION_NAME), EmptyTree, Apply(handler, Ident(nme.DEFAULT_EXCEPTION_NAME)))),
+              finalizer)
+          }
+
         }
       case THROW =>
         atPos(in.skipToken()) { Throw(expr()) }
diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index d9e248e405a2..f0d55882403b 100644
--- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -141,6 +141,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
     super.toText(tp)
   }
 
+  def blockText[T >: Untyped](trees: List[Tree[T]]): Text =
+    "{" ~ toText(trees, "\n") ~ "}"
+
   override def toText[T >: Untyped](tree: Tree[T]): Text = controlled {
 
     def optDotPrefix(name: Name) = optText(name)(_ ~ ".")
@@ -155,8 +158,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
     def addVparamssText(txt: Text, vparamss: List[List[ValDef[T]]]): Text =
       (txt /: vparamss)((txt, vparams) => txt ~ "(" ~ toText(vparams, ", ") ~ ")")
 
-    def blockText(trees: List[Tree[T]]): Text =
-      "{" ~ toText(trees, "\n") ~ "}"
+
 
     def caseBlockText(tree: Tree[T]): Text = tree match {
       case Block(stats, expr) => toText(stats :+ expr, "\n")
@@ -261,9 +263,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
         "case " ~ toText(pat) ~ optText(guard)(" if " ~ _) ~ " => " ~ caseBlockText(body)
       case Return(expr, from) =>
         changePrec(GlobalPrec) { "return" ~ optText(expr)(" " ~ _) }
-      case Try(expr, handler, finalizer) =>
+      case Try(expr, cases, finalizer) =>
         changePrec(GlobalPrec) {
-          "try " ~ toText(expr) ~ optText(handler)(" catch " ~ _) ~ optText(finalizer)(" finally " ~ _)
+          "try " ~ toText(expr) ~ optText(cases)(" catch " ~ _) ~ optText(finalizer)(" finally " ~ _)
         }
       case Throw(expr) =>
         changePrec(GlobalPrec) {
@@ -461,6 +463,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
   def optText[T >: Untyped](tree: Tree[T])(encl: Text => Text): Text =
     if (tree.isEmpty) "" else encl(toText(tree))
 
+  def optText[T >: Untyped](tree: List[Tree[T]])(encl: Text => Text): Text =
+    if (tree.exists(!_.isEmpty)) "" else encl(blockText(tree))
+
   override protected def polyParamName(name: TypeName): TypeName =
     name.unexpandedName()
 
diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala
index cc9aac3662ce..9c47ce6283f1 100644
--- a/src/dotty/tools/dotc/transform/LazyVals.scala
+++ b/src/dotty/tools/dotc/transform/LazyVals.scala
@@ -217,18 +217,15 @@ class LazyValsTransform extends MiniPhaseTransform with IdentityDenotTransformer
       val compute = {
         val handlerSymbol = ctx.newSymbol(methodSymbol, "$anonfun".toTermName, Flags.Synthetic,
           MethodType(List("x$1".toTermName), List(defn.ThrowableType), defn.IntType))
-
-        val handler = Closure(handlerSymbol, {
-          args =>
-            val exception = args.head.head
-            val complete = setFlagState.appliedTo(thiz, offset, initState, Literal(Constant(ord)))
-            Block(List(complete), Throw(exception))
-        })
+        val caseSymbol = ctx.newSymbol(methodSymbol, nme.DEFAULT_EXCEPTION_NAME, Flags.Synthetic, defn.ThrowableType)
+        val complete = setFlagState.appliedTo(thiz, offset, initState, Literal(Constant(ord)))
+        val handler = CaseDef(Bind(caseSymbol, ref(caseSymbol)), EmptyTree,
+          Block(List(complete), Throw(ref(caseSymbol))
+        ))
 
         val compute = Assign(ref(resultSymbol), rhs)
-        val tr = Try(compute, handler, EmptyTree)
+        val tr = Try(compute, List(handler), EmptyTree)
         val assign = Assign(ref(target), ref(resultSymbol))
-        val complete = setFlagState.appliedTo(thiz, offset, computedState, Literal(Constant(ord)))
         val noRetry = Assign(ref(retrySymbol), Literal(Constants.Constant(false)))
         val body = If(casFlag.appliedTo(thiz, offset, ref(flagSymbol), computeState, Literal(Constant(ord))),
           Block(tr :: assign :: complete :: noRetry :: Nil, Literal(Constant(()))),
diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala
index 9ba8d54c79a5..842582592aee 100644
--- a/src/dotty/tools/dotc/transform/PatternMatcher.scala
+++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala
@@ -51,7 +51,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
   override def transformMatch(tree: Match)(implicit ctx: Context, info: TransformerInfo): Tree = {
     val translated = new Translator()(ctx).translator.translateMatch(tree)
 
-    Typed(translated.ensureConforms(tree.tpe), TypeTree(tree.tpe))
+    translated.ensureConforms(tree.tpe)
   }
 
   class Translator(implicit ctx: Context) {
diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala
index b3f63bcaf43d..46028e0fc552 100644
--- a/src/dotty/tools/dotc/transform/TailRec.scala
+++ b/src/dotty/tools/dotc/transform/TailRec.scala
@@ -156,9 +156,8 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
     def noTailTransform(tree: Tree)(implicit c: Context): Tree =
       transform(tree, noTailContext)
 
-
-    def noTailTransforms(trees: List[Tree])(implicit c: Context) =
-      trees map (noTailTransform)
+    def noTailTransforms[Tr <: Tree](trees: List[Tr])(implicit c: Context): List[Tr] =
+      trees.map(noTailTransform).asInstanceOf[List[Tr]]
 
     override def transform(tree: Tree)(implicit c: Context): Tree = {
       /* A possibly polymorphic apply to be considered for tail call transformation. */
@@ -247,14 +246,14 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
           // SI-1672 Catches are in tail position when there is no finalizer
           tpd.cpy.Try(tree)(
             noTailTransform(tree.expr),
-            transformHandlers(tree.handler),
+            transformSub(tree.cases),
             EmptyTree
           )
         }
         else {
           tpd.cpy.Try(tree)(
             noTailTransform(tree.expr),
-            noTailTransform(tree.handler),
+            noTailTransforms(tree.cases),
             noTailTransform(tree.finalizer)
           )
         }
diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala
index 9e04d03b9acf..588a13fc93a1 100644
--- a/src/dotty/tools/dotc/transform/TreeTransform.scala
+++ b/src/dotty/tools/dotc/transform/TreeTransform.scala
@@ -1104,9 +1104,9 @@ object TreeTransforms {
           if (mutatedInfo eq null) tree
           else {
             val block = transform(tree.expr, mutatedInfo, cur)
-            val handler = transform(tree.handler, mutatedInfo, cur)
+            val cases1 = tree.cases.mapConserve(transform(_, mutatedInfo, cur)).asInstanceOf[List[CaseDef]]
             val finalizer = transform(tree.finalizer, mutatedInfo, cur)
-            goTry(cpy.Try(tree)(block, handler, finalizer), mutatedInfo.nx.nxTransTry(cur))
+            goTry(cpy.Try(tree)(block, cases1, finalizer), mutatedInfo.nx.nxTransTry(cur))
           }
         case tree: Throw =>
           implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForThrow, info.nx.nxPrepThrow, tree, cur)
diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala
index 193af8f0e48f..bb488bdc5c82 100644
--- a/src/dotty/tools/dotc/typer/TypeAssigner.scala
+++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala
@@ -313,12 +313,9 @@ trait TypeAssigner {
   def assignType(tree: untpd.Return)(implicit ctx: Context) =
     tree.withType(defn.NothingType)
 
-  def assignType(tree: untpd.Try, expr: Tree, handler: Tree)(implicit ctx: Context) = {
-    if(handler.isEmpty) tree.withType(expr.tpe)
-    else if(handler.tpe.derivesFrom(defn.FunctionClass(1))) {
-      val handlerTypeArgs = handler.tpe.baseArgTypesHi(defn.FunctionClass(1))
-      tree.withType(if (handlerTypeArgs.nonEmpty) expr.tpe | handlerTypeArgs(1) else expr.tpe /*| Object, as function returns boxed value ??? */)
-    } else tree.withType(expr.tpe | handler.tpe)
+  def assignType(tree: untpd.Try, expr: Tree, cases: List[CaseDef])(implicit ctx: Context) = {
+    if (cases.isEmpty) tree.withType(expr.tpe)
+    else tree.withType(ctx.typeComparer.lub(expr.tpe :: cases.tpes))
   }
 
   def assignType(tree: untpd.Throw)(implicit ctx: Context) =
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 355b9f263ad3..4b49d266994c 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -666,20 +666,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
 
   def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = track("typedTry") {
     val expr1 = typed(tree.expr, pt)
-    val handler1: Tree = tree.handler match {
-      case h: untpd.Match if ((h.selector eq EmptyTree)                   // comes from parser
-                               || (h.selector eq ExceptionHandlerSel)) => // during retyping
-        val cases1 = typedCases(h.cases, defn.ThrowableType, pt)
-        assignType(untpd.Match(Typed(ExceptionHandlerSel, TypeTree(defn.ThrowableType)), cases1), cases1)
-      case Typed(handler, tpe) if ctx.phaseId > ctx.patmatPhase.id =>  // we are retyping an expanded pattern
-        typed(tree.handler, pt)
-      case Apply(bx, List(Typed(handler, tpe))) if ctx.erasedTypes && Boxing.isBox(bx.symbol) =>
-        typed(tree.handler, pt)
-      case _ => typed(tree.handler, defn.FunctionType(defn.ThrowableType :: Nil, pt))
-
-    }
+    val cases1 = typedCases(tree.cases, defn.ThrowableType, pt)
     val finalizer1 = typed(tree.finalizer, defn.UnitType)
-    assignType(cpy.Try(tree)(expr1, handler1, finalizer1), expr1, handler1)
+    assignType(cpy.Try(tree)(expr1, cases1, finalizer1), expr1, cases1)
   }
 
   def typedThrow(tree: untpd.Throw)(implicit ctx: Context): Throw = track("typedThrow") {

From 0c967f06e9bf51e02903d1bb2c79fa1c4febf98d Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Tue, 28 Oct 2014 21:13:47 +0100
Subject: [PATCH 13/16] Get rid of ExceptionHandlerSel.

It's not used anymore as superseded by previous commit.
---
 src/dotty/tools/dotc/ast/Trees.scala                |  5 +----
 src/dotty/tools/dotc/transform/PatternMatcher.scala |  4 +---
 src/dotty/tools/dotc/transform/TailRec.scala        | 11 -----------
 src/dotty/tools/dotc/typer/Typer.scala              |  2 --
 4 files changed, 2 insertions(+), 20 deletions(-)

diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala
index 4cd41cc2e925..71026a4499f3 100644
--- a/src/dotty/tools/dotc/ast/Trees.scala
+++ b/src/dotty/tools/dotc/ast/Trees.scala
@@ -804,7 +804,7 @@ object Trees {
     type ThisTree[-T >: Untyped] = Thicket[T]
     override def isEmpty: Boolean = trees.isEmpty
     override def toList: List[Tree[T]] = flatten(trees)
-    override def toString = if(this eq theExceptionHandlerSel) "ExceptionSel" else if (isEmpty) "EmptyTree" else "Thicket(" + trees.mkString(", ") + ")"
+    override def toString = if (isEmpty) "EmptyTree" else "Thicket(" + trees.mkString(", ") + ")"
     override def withPos(pos: Position): this.type = {
       val newTrees = trees.map(_.withPos(pos))
       new Thicket[T](newTrees).asInstanceOf[this.type]
@@ -818,11 +818,9 @@ object Trees {
 
   val theEmptyTree: Thicket[Type] = Thicket(Nil)
   val theEmptyValDef = new EmptyValDef[Type]
-  val theExceptionHandlerSel: Thicket[Type] = Thicket(Nil)
 
   def genericEmptyValDef[T >: Untyped]: ValDef[T] = theEmptyValDef.asInstanceOf[ValDef[T]]
   def genericEmptyTree[T >: Untyped]: Thicket[T] = theEmptyTree.asInstanceOf[Thicket[T]]
-  def genericExceptionHandlerSel[T >: Untyped]: Thicket[T] = theExceptionHandlerSel.asInstanceOf[Thicket[T]]
 
   def flatten[T >: Untyped](trees: List[Tree[T]]): List[Tree[T]] = {
     var buf: ListBuffer[Tree[T]] = null
@@ -913,7 +911,6 @@ object Trees {
     type Thicket = Trees.Thicket[T]
 
     val EmptyTree: Thicket = genericEmptyTree
-    val ExceptionHandlerSel: Thicket = genericExceptionHandlerSel // selector used in exception hanlder of Try nodes
     val EmptyValDef: ValDef = genericEmptyValDef
 
     // ----- Auxiliary creation methods ------------------
diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala
index 842582592aee..7631c99c8613 100644
--- a/src/dotty/tools/dotc/transform/PatternMatcher.scala
+++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala
@@ -1142,9 +1142,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
 
       val selectorTp = elimAnonymousClass(sel.tpe.widen/*withoutAnnotations*/)
 
-      val selectorSym =
-        if (sel ne ExceptionHandlerSel) freshSym(sel.pos, selectorTp, "selector")
-        else freshSym(match_.pos, defn.ThrowableType, "ex")
+      val selectorSym = freshSym(sel.pos, selectorTp, "selector")
 
       val (nonSyntheticCases, defaultOverride) = cases match {
         case init :+ last if isSyntheticDefaultCase(last) => (init, Some(((scrut: Symbol) => last.body)))
diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala
index 46028e0fc552..376a41646e2a 100644
--- a/src/dotty/tools/dotc/transform/TailRec.scala
+++ b/src/dotty/tools/dotc/transform/TailRec.scala
@@ -231,17 +231,6 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
       }
 
       def rewriteTry(tree: Try): Try = {
-        def transformHandlers(t: Tree): Tree = {
-          t match {
-            case Block(List((d: DefDef)), cl@Closure(Nil, _, EmptyTree)) =>
-              val newDef = cpy.DefDef(d)(rhs = transform(d.rhs))
-              Block(List(newDef), cl)
-            case Match(Typed(ExceptionHandlerSel, _), _) =>
-              transform(t)
-            case _: Ident|_: Apply| _: TypeApply => t // handler is an external function
-            case _ => assert(false, s"failed to deconstruct try handler ${t.show}"); ???
-          }
-        }
         if (tree.finalizer eq EmptyTree) {
           // SI-1672 Catches are in tail position when there is no finalizer
           tpd.cpy.Try(tree)(
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 4b49d266994c..89edd29ca96e 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -345,8 +345,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
       assignType(cpy.Typed(tree)(expr1, tpt1), tpt1)
     }
     tree.expr match {
-      case ExceptionHandlerSel if (tree.tpt.tpe == defn.ThrowableType) =>
-        tree withType defn.ThrowableType
       case id: untpd.Ident if (ctx.mode is Mode.Pattern) && isVarPattern(id) =>
         if (id.name == nme.WILDCARD) regularTyped(isWildcard = true)
         else {

From 7ebc502c1a6eee7362a5c00a1561c97be10f4845 Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Wed, 29 Oct 2014 12:49:05 +0100
Subject: [PATCH 14/16] Postponing desugaring of Try nodes.

Instead of desugaring in parser desugaring is now done during desugaring.
---
 src/dotty/tools/dotc/ast/Desugar.scala     | 10 ++++++++++
 src/dotty/tools/dotc/ast/untpd.scala       |  2 ++
 src/dotty/tools/dotc/parsing/Parsers.scala | 10 +---------
 3 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index dba3872cb127..3d217f38f5e6 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -817,6 +817,16 @@ object desugar {
       case PatDef(mods, pats, tpt, rhs) =>
         val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
         flatTree(pats1 map (makePatDef(mods, _, rhs)))
+      case ParsedTry(body, handler, finalizer) =>
+        handler match {
+          case Match(EmptyTree, cases) => Try(body, cases, finalizer)
+          case EmptyTree => Try(body, Nil, finalizer)
+          case _ =>
+            Try(body,
+              List(CaseDef(Ident(nme.DEFAULT_EXCEPTION_NAME), EmptyTree, Apply(handler, Ident(nme.DEFAULT_EXCEPTION_NAME)))),
+              finalizer)
+        }
+
     }
   }.withPos(tree.pos)
 
diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala
index a5072bc96cb7..9e6ce44503b6 100644
--- a/src/dotty/tools/dotc/ast/untpd.scala
+++ b/src/dotty/tools/dotc/ast/untpd.scala
@@ -32,6 +32,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
     def withName(name: Name)(implicit ctx: Context) = cpy.ModuleDef(this)(mods, name.toTermName, impl)
   }
 
+  case class ParsedTry(expr: Tree, handler: Tree, finalizer: Tree) extends TermTree
+
   case class SymbolLit(str: String) extends TermTree
   case class InterpolatedString(id: TermName, strings: List[Literal], elems: List[Tree]) extends TermTree
   case class Function(args: List[Tree], body: Tree) extends Tree {
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index a787f9712107..2651b75149fd 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -914,15 +914,7 @@ object Parsers {
           val finalizer =
             if (handler.isEmpty || in.token == FINALLY) { accept(FINALLY); expr() }
             else EmptyTree
-          handler match {
-            case Match(sel, cases) => Try(body, cases, finalizer)
-            case EmptyTree => Try(body, Nil, finalizer)
-            case _ =>
-              Try(body,
-                List(CaseDef(Ident(nme.DEFAULT_EXCEPTION_NAME), EmptyTree, Apply(handler, Ident(nme.DEFAULT_EXCEPTION_NAME)))),
-              finalizer)
-          }
-
+          ParsedTry(body, handler, finalizer)
         }
       case THROW =>
         atPos(in.skipToken()) { Throw(expr()) }

From 9d1b4bfd96d351f3c05aeb258a708e7486f5f390 Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Wed, 29 Oct 2014 13:31:56 +0100
Subject: [PATCH 15/16] Print whole TreeTransform name in Ycheck and Xprint

---
 src/dotty/tools/dotc/Run.scala                   | 6 +++++-
 src/dotty/tools/dotc/core/Contexts.scala         | 4 ++++
 src/dotty/tools/dotc/transform/TreeChecker.scala | 4 +++-
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala
index 2fbfc3f9b1f2..a9948c54a312 100644
--- a/src/dotty/tools/dotc/Run.scala
+++ b/src/dotty/tools/dotc/Run.scala
@@ -3,6 +3,7 @@ package dotc
 
 import core._
 import Contexts._, Periods._, Symbols._, Phases._, Decorators._
+import dotty.tools.dotc.transform.TreeTransforms.TreeTransformer
 import io.PlainFile
 import util.{SourceFile, NoSource, Stats, SimpleMap}
 import reporting.Reporter
@@ -60,7 +61,10 @@ class Run(comp: Compiler)(implicit ctx: Context) {
 
   private def printTree(ctx: Context) = {
     val unit = ctx.compilationUnit
-    println(s"result of $unit after ${ctx.phase.prev}:")
+    val prevPhase = ctx.phase.prev // can be a mini-phase
+    val squahsedPhase = ctx.squashed(prevPhase)
+
+    println(s"result of $unit after ${squahsedPhase}:")
     println(unit.tpdTree.show(ctx))
   }
 
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala
index 3b14872b7a7e..de6b0cabfde7 100644
--- a/src/dotty/tools/dotc/core/Contexts.scala
+++ b/src/dotty/tools/dotc/core/Contexts.scala
@@ -472,6 +472,10 @@ object Contexts {
 
     /** The standard definitions */
     val definitions = new Definitions
+
+    def squashed(p: Phase): Phase = {
+      squashedPhases.find(_.period.containsPhaseId(p.id)).getOrElse(NoPhase)
+    }
   }
 
   /** The essential mutable state of a context base, collected into a common class */
diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala
index 4a7d280e5344..7315b4da1347 100644
--- a/src/dotty/tools/dotc/transform/TreeChecker.scala
+++ b/src/dotty/tools/dotc/transform/TreeChecker.scala
@@ -50,7 +50,9 @@ class TreeChecker {
   }
 
   def check(phasesToRun: Seq[Phase], ctx: Context) = {
-    println(s"checking ${ctx.compilationUnit} after phase ${ctx.phase.prev}")
+    val prevPhase = ctx.phase.prev // can be a mini-phase
+    val squahsedPhase = ctx.squashed(prevPhase)
+    println(s"checking ${ctx.compilationUnit} after phase ${squahsedPhase}")
     val checkingCtx = ctx.fresh
       .setTyperState(ctx.typerState.withReporter(new ThrowingReporter(ctx.typerState.reporter)))
     val checker = new Checker(previousPhases(phasesToRun.toList)(ctx))

From 5540f1330862de8daeeb9f0b15b0bb0ba6f153c5 Mon Sep 17 00:00:00 2001
From: Dmitry Petrashko <dmitry.petrashko@gmail.com>
Date: Thu, 30 Oct 2014 17:45:43 +0100
Subject: [PATCH 16/16] merge fixes: extract typedCase to be reused in
 TreeChecker.

---
 src/dotty/tools/dotc/typer/Typer.scala | 33 +++++++++++++-------------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 89edd29ca96e..3c36a1f256ef 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -618,24 +618,25 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
       accu(Set.empty, selType)
     }
 
-    def typedCase(tree: untpd.CaseDef): CaseDef = track("typedCase") {
-      def caseRest(pat: Tree)(implicit ctx: Context) = {
-        gadtSyms foreach (_.resetGADTFlexType)
-        pat foreachSubTree {
-          case b: Bind =>
-            if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
-            else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos)
-          case _ =>
-        }
-        val guard1 = typedExpr(tree.guard, defn.BooleanType)
-        val body1 = typedExpr(tree.body, pt)
-        assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1)
+    cases mapconserve (typedCase(_, pt, selType, gadtSyms))
+  }
+
+  def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = track("typedCase") {
+    def caseRest(pat: Tree)(implicit ctx: Context) = {
+      gadtSyms foreach (_.resetGADTFlexType)
+      pat foreachSubTree {
+        case b: Bind =>
+          if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
+          else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos)
+        case _ =>
       }
-      val doCase: () => CaseDef =
-        () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope)
-      (doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))()
+      val guard1 = typedExpr(tree.guard, defn.BooleanType)
+      val body1 = typedExpr(tree.body, pt)
+      assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1)
     }
-    cases mapconserve typedCase
+    val doCase: () => CaseDef =
+      () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope)
+    (doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))()
   }
 
   def typedReturn(tree: untpd.Return)(implicit ctx: Context): Return = track("typedReturn") {