From 9616be10ce1339827eeb3d80deda86c0dd19b60a Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Tue, 2 Oct 2018 10:01:08 +0200 Subject: [PATCH] Fix #5107: disallow block as Apply.fun --- compiler/src/dotty/tools/dotc/Compiler.scala | 3 +- compiler/src/dotty/tools/dotc/ast/tpd.scala | 12 +- .../tools/dotc/transform/ElimByName.scala | 3 - .../dotty/tools/dotc/transform/Erasure.scala | 2 +- .../dotty/tools/dotc/transform/Splitter.scala | 137 ------------------ .../src/dotty/tools/dotc/typer/Typer.scala | 7 +- .../transform/PatmatExhaustivityTest.scala | 2 +- tests/pos/i5107.scala | 9 ++ 8 files changed, 27 insertions(+), 148 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/transform/Splitter.scala create mode 100644 tests/pos/i5107.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 17bf78e54a50..c500f2754cc7 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -69,8 +69,7 @@ class Compiler { new ExplicitOuter, // Add accessors to outer classes from nested ones. new ExplicitSelf, // Make references to non-trivial self types explicit as casts new StringInterpolatorOpt, // Optimizes raw and s string interpolators by rewriting them to string concatentations - new CrossCastAnd, // Normalize selections involving intersection types. - new Splitter) :: // Expand selections involving union types into conditionals + new CrossCastAnd) :: // Normalize selections involving intersection types. List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions new VCInlineMethods, // Inlines calls to value class methods new SeqLiterals, // Express vararg arguments as arrays diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index c7007294b7c8..e8c542362fa4 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -40,11 +40,15 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Super(qual: Tree, mixName: TypeName, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context): Super = Super(qual, if (mixName.isEmpty) untpd.EmptyTypeIdent else untpd.Ident(mixName), inConstrCall, mixinClass) - def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = + def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = { + assert(fn.isInstanceOf[RefTree] || fn.isInstanceOf[GenericApply[_]]) ta.assignType(untpd.Apply(fn, args), fn, args) + } - def TypeApply(fn: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = + def TypeApply(fn: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = { + assert(fn.isInstanceOf[RefTree] || fn.isInstanceOf[GenericApply[_]]) ta.assignType(untpd.TypeApply(fn, args), fn, args) + } def Literal(const: Constant)(implicit ctx: Context): Literal = ta.assignType(untpd.Literal(const)) @@ -181,8 +185,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Alternative(trees: List[Tree])(implicit ctx: Context): Alternative = ta.assignType(untpd.Alternative(trees), trees) - def UnApply(fun: Tree, implicits: List[Tree], patterns: List[Tree], proto: Type)(implicit ctx: Context): UnApply = + def UnApply(fun: Tree, implicits: List[Tree], patterns: List[Tree], proto: Type)(implicit ctx: Context): UnApply = { + assert(fun.isInstanceOf[RefTree] || fun.isInstanceOf[GenericApply[_]]) ta.assignType(untpd.UnApply(fun, implicits, patterns), proto) + } def ValDef(sym: TermSymbol, rhs: LazyTree = EmptyTree)(implicit ctx: Context): ValDef = ta.assignType(untpd.ValDef(sym.name, TypeTree(sym.info), rhs), sym) diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index b35704f02019..e5474439f8dd 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -36,9 +36,6 @@ class ElimByName extends TransformByNameApply with InfoTransformer { override def phaseName: String = ElimByName.name - override def runsAfterGroupsOf: Set[String] = Set(Splitter.name) - // I got errors running this phase in an earlier group, but I did not track them down. - override def changesParents: Boolean = true // Only true for by-names /** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */ diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 7bce3fa200b5..bafea790faba 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -34,7 +34,7 @@ class Erasure extends Phase with DenotTransformer { override def phaseName: String = Erasure.name /** List of names of phases that should precede this phase */ - override def runsAfter: Set[String] = Set(InterceptedMethods.name, Splitter.name, ElimRepeated.name) + override def runsAfter: Set[String] = Set(InterceptedMethods.name, ElimRepeated.name) override def changesMembers: Boolean = true // the phase adds bridges override def changesParents: Boolean = true // the phase drops Any diff --git a/compiler/src/dotty/tools/dotc/transform/Splitter.scala b/compiler/src/dotty/tools/dotc/transform/Splitter.scala deleted file mode 100644 index 2f969748229d..000000000000 --- a/compiler/src/dotty/tools/dotc/transform/Splitter.scala +++ /dev/null @@ -1,137 +0,0 @@ -package dotty.tools.dotc -package transform - -import MegaPhase._ -import ast.Trees._ -import core._ -import Contexts._, Types._ - -object Splitter { - val name: String = "splitter" -} - -/** Distribute applications into Block and If nodes - */ -class Splitter extends MiniPhase { - import ast.tpd._ - - override def phaseName: String = Splitter.name - - /** Distribute arguments among splitted branches */ - def distribute(tree: GenericApply[Type], rebuild: (Tree, List[Tree]) => Context => Tree)(implicit ctx: Context): Tree = { - def recur(fn: Tree): Tree = fn match { - case Block(stats, expr) => Block(stats, recur(expr)) - case If(cond, thenp, elsep) => If(cond, recur(thenp), recur(elsep)) - case _ => rebuild(fn, tree.args)(ctx) withPos tree.pos - } - recur(tree.fun) - } - - private def needsDistribution(fun: Tree) = fun match { - case _: Block | _: If => true - case _ => false - } - - override def transformTypeApply(tree: TypeApply)(implicit ctx: Context): Tree = - if (needsDistribution(tree.fun)) - distribute(tree, typeApply) - else - tree - - override def transformApply(tree: Apply)(implicit ctx: Context): Tree = - if (needsDistribution(tree.fun)) - distribute(tree, apply) - else - tree - - private val typeApply = (fn: Tree, args: List[Tree]) => (ctx: Context) => TypeApply(fn, args)(ctx) - private val apply = (fn: Tree, args: List[Tree]) => (ctx: Context) => Apply(fn, args)(ctx) - -/* The following is no longer necessary, since we select members on the join of an or type: - * - /** If we select a name, make sure the node has a symbol. - * If necessary, split the qualifier with type tests. - * Example: Assume: - * - * class A { def f(x: S): T } - * class B { def f(x: S): T } - * def p(): A | B - * - * Then p().f(a) translates to - * - * val ev$1 = p() - * if (ev$1.isInstanceOf[A]) ev$1.asInstanceOf[A].f(a) - * else ev$1.asInstanceOf[B].f(a) - */ - override def transformSelect(tree: Select)(implicit ctx: Context) = { - val Select(qual, name) = tree - - def memberDenot(tp: Type): SingleDenotation = { - val mbr = tp.member(name) - if (!mbr.isOverloaded) mbr.asSingleDenotation - else tree.tpe match { - case tref: TermRef if !tref.signature.isOverloaded => - mbr.atSignature(tref.sig).checkUnique - case _ => - def alts = mbr.alternatives.map(alt => i"$alt: ${alt.info}").mkString(", ") - ctx.error(s"cannot disambiguate overloaded members $alts", tree.pos) - NoDenotation - } - } - - def candidates(tp: Type): List[Symbol] = { - val mbr = memberDenot(tp) - if (mbr.symbol.exists) mbr.symbol :: Nil - else tp.widen match { - case tref: TypeRef => - tref.info match { - case TypeBounds(_, hi) => candidates(hi) - case _ => Nil - } - case OrType(tp1, tp2) => - candidates(tp1) | candidates(tp2) - case AndType(tp1, tp2) => - candidates(tp1) & candidates(tp2) - case tpw => - Nil - } - } - - def isStructuralSelect(tp: Type): Boolean = tp.stripTypeVar match { - case tp: RefinedType => tp.refinedName == name || isStructuralSelect(tp.parent) - case tp: TypeProxy => isStructuralSelect(tp.underlying) - case AndType(tp1, tp2) => isStructuralSelect(tp1) || isStructuralSelect(tp2) - case _ => false - } - - if (tree.symbol.exists) tree - else { - def choose(qual: Tree, syms: List[Symbol]): Tree = { - def testOrCast(which: Symbol, mbr: Symbol) = - qual.select(which).appliedToType(mbr.owner.appliedRef) - def select(sym: Symbol) = { - val qual1 = - if (qual.tpe derivesFrom sym.owner) qual - else testOrCast(defn.Any_asInstanceOf, sym) - qual1.select(sym).withPos(tree.pos) - } - syms match { - case Nil => - def msg = - if (isStructuralSelect(qual.tpe)) - s"cannot access member '$name' from structural type ${qual.tpe.widen.show}; use Dynamic instead" - else - s"no candidate symbols for ${tree.tpe.show} found in ${qual.tpe.show}" - ctx.error(msg, tree.pos) - tree - case sym :: Nil => - select(sym) - case sym :: syms1 => - If(testOrCast(defn.Any_isInstanceOf, sym), select(sym), choose(qual, syms1)) - } - } - evalOnce(qual)(qual => choose(qual, candidates(qual.tpe))) - } - } -*/ -} diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 13ee9697b3a4..5125a3545341 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2365,7 +2365,12 @@ class Typer extends Namer } } else issueErrors() } - else readaptSimplified(tpd.Apply(tree, args)) + else tree match { + case tree: Block => + readaptSimplified(tpd.Block(tree.stats, tpd.Apply(tree.expr, args))) + case _ => + readaptSimplified(tpd.Apply(tree, args)) + } } addImplicitArgs(argCtx(tree)) } diff --git a/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala b/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala index 8fb80462951f..4215b25c260d 100644 --- a/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala +++ b/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala @@ -17,7 +17,7 @@ import vulpix.TestConfiguration class PatmatExhaustivityTest { val testsDir = "tests/patmat" // stop-after: patmatexhaust-huge.scala crash compiler - val options = List("-color:never", "-Ystop-after:splitter", "-Ycheck-all-patmat", "-classpath", TestConfiguration.basicClasspath) + val options = List("-color:never", "-Ystop-after:crossCast", "-Ycheck-all-patmat", "-classpath", TestConfiguration.basicClasspath) private def compileFile(path: JPath) = { val stringBuffer = new StringWriter() diff --git a/tests/pos/i5107.scala b/tests/pos/i5107.scala new file mode 100644 index 000000000000..94daa2ae85f8 --- /dev/null +++ b/tests/pos/i5107.scala @@ -0,0 +1,9 @@ +class Test { + inline def foo(x: Int = 5)(implicit y: Int): Int = + if (x > 0) y * y + else y + y + + implicit val m: Int = 7 + + (new Test).foo() +}