From 1be3b2fd50487b20812b57ef485e8983dea7c289 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko 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 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 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 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 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 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 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 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 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 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 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 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, $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 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 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 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 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") {