From bddeaecb8f69032c5117e926ef67afc0deedd0dd Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 1 Dec 2013 18:42:45 +0100 Subject: Tweaks to applications, operator assignments, and variable definitions. --- src/dotty/tools/dotc/ast/Desugar.scala | 3 +- src/dotty/tools/dotc/ast/untpd.scala | 4 +- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 6 +- src/dotty/tools/dotc/typer/Applications.scala | 180 ++++++++++----------- src/dotty/tools/dotc/typer/EtaExpansion.scala | 24 ++- src/dotty/tools/dotc/typer/Typer.scala | 9 +- test/dotc/tests.scala | 1 + tests/pos/opassign.scala | 28 ++++ 8 files changed, 154 insertions(+), 101 deletions(-) create mode 100644 tests/pos/opassign.scala diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index d489aec44..1b7f4edc8 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -25,9 +25,10 @@ object desugar { if (!ctx.owner.isClass || (mods is Private) || !(mods is Mutable)) vdef else { val setterParam = makeSyntheticParameter(tpt = TypeTree(vdef)) + val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral val setter = cpy.DefDef(vdef, mods | Accessor, name.setterName, Nil, (setterParam :: Nil) :: Nil, - EmptyTree, refOfDef(setterParam)) + TypeTree(defn.UnitType), setterRhs) // rhs gets filled in later, when field is generated and getter has parameters Thicket(vdef, setter) } } diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index 774f24394..ab9de706d 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -144,8 +144,8 @@ object untpd extends Trees.Instance[Untyped] with TreeInfo[Untyped] { def makeParameter(pname: TermName, tpe: Tree, mods: Modifiers = Modifiers())(implicit ctx: Context): ValDef = ValDef(mods | Param, pname, tpe, EmptyTree) - def makeSyntheticParameter(n: Int = 1, tpt: Tree = EmptyTree)(implicit ctx: Context): ValDef = - ValDef(Modifiers(SyntheticTermParam), nme.syntheticParamName(n), TypeTree(), EmptyTree) + def makeSyntheticParameter(n: Int = 1, tpt: Tree = TypeTree())(implicit ctx: Context): ValDef = + ValDef(Modifiers(SyntheticTermParam), nme.syntheticParamName(n), tpt, EmptyTree) def refOfDef(tree: NameTree)(implicit ctx: Context) = Ident(tree.name) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 546a3e027..abea42c1d 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -212,7 +212,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case SeqLiteral(elems) => "[" ~ toTextGlobal(elems, ",") ~ "]" case TypeTree(orig) => - if (tree.hasType) toText(tree.typeOpt) else toText(orig) + if (tree.hasType) toText(tree.typeOpt) + else orig match { + case orig: ValDef => "like(" ~ toText(orig) ~ ")" + case _ => toText(orig) + } case SingletonTypeTree(ref) => toTextLocal(ref) ~ ".type" case SelectFromTypeTree(qual, name) => diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 648266a7a..d483163e7 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -26,9 +26,6 @@ import collection.mutable import language.implicitConversions object Applications { - - import tpd.{ cpy => _, _ } - private val isNamedArg = (arg: Any) => arg.isInstanceOf[Trees.NamedArg[_]] def hasNamedArg(args: List[Any]) = args exists isNamedArg } @@ -41,8 +38,6 @@ trait Applications extends Compatibility { self: Typer => import tpd.{ cpy => _, _ } import untpd.cpy - private def state(implicit ctx: Context) = ctx.typerState - /** @param Arg the type of arguments, could be tpd.Tree, untpd.Tree, or Type * @param methRef the reference to the method of the application * @param funType the type of the function part of the application @@ -93,16 +88,16 @@ trait Applications extends Compatibility { self: Typer => */ protected def liftFun(): Unit = () - /** A flag signalling that the application was so far succesful */ + /** A flag signalling that the typechecking the application was so far succesful */ protected var ok = true /** The function's type after widening and instantiating polytypes - * with polyparams or typevars in constraint set + * with polyparams in constraint set */ val methType = funType.widen match { case funType: MethodType => funType case funType: PolyType => constrained(funType).resultType - case _ => funType + case tp => tp //was: funType } /** The arguments re-ordered so that each named argument matches the @@ -130,8 +125,6 @@ trait Applications extends Compatibility { self: Typer => /** The application was succesful */ def success = ok - private def state = ctx.typerState - protected def methodType = methType.asInstanceOf[MethodType] private def methString: String = s"method ${methRef.name}: ${methType.show}" @@ -140,7 +133,7 @@ trait Applications extends Compatibility { self: Typer => var namedToArg: Map[Name, Trees.Tree[T]] = (for (NamedArg(name, arg1) <- args) yield (name, arg1)).toMap - def badNamedArg(arg: Trees.Tree[_ >: Untyped]): Unit = { + def badNamedArg(arg: untpd.Tree): Unit = { val NamedArg(name, _) = arg def msg = if (methodType.paramNames contains name) @@ -153,25 +146,25 @@ trait Applications extends Compatibility { self: Typer => def recur(pnames: List[Name], args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = pnames match { case pname :: pnames1 => namedToArg get pname match { - case Some(arg) => + case Some(arg) => // there is a named argument for this parameter; pick it namedToArg -= pname arg :: recur(pnames1, args) case None => args match { case (arg @ NamedArg(aname, _)) :: args1 => - if (namedToArg contains aname) + if (namedToArg contains aname) // argument is missing, pass an empty tree genericEmptyTree :: recur(pnames1, args) - else { + else { // name not (or no longer) available for named arg badNamedArg(arg) recur(pnames1, args1) } case arg :: args1 => - arg :: recur(pnames1, args1) - case Nil => + arg :: recur(pnames1, args1) // unnamed argument; pick it + case Nil => // no more args, continue to pick up any preceding named args recur(pnames1, args) } } - case nil => + case nil => // supernumerary arguments, can only be default args. if (hasNamedArg(args)) { val (namedArgs, otherArgs) = args partition isNamedArg namedArgs foreach badNamedArg @@ -214,6 +207,7 @@ trait Applications extends Compatibility { self: Typer => val cls = meth.owner val pre = if (meth.isClassConstructor) { + // default getters for class constructors are found in the companion object mpre.baseType(cls) match { case TypeRef(clspre, _) => ref(clspre, cls.companionModule) case _ => NoType @@ -285,15 +279,15 @@ trait Applications extends Compatibility { self: Typer => def constrainResult(mt: Type, pt: Type): Boolean = pt match { case FunProto(_, result, _) => mt match { - case mt: MethodType if !mt.isDependent => - constrainResult(mt.resultType, pt.resultType) + case mt: MethodType => + mt.isDependent || constrainResult(mt.resultType, pt.resultType) case _ => true } case pt: ValueType => mt match { - case mt: ImplicitMethodType if !mt.isDependent => - constrainResult(mt.resultType, pt) + case mt: ImplicitMethodType => + mt.isDependent || constrainResult(mt.resultType, pt) case _ => isCompatible(mt, pt) } @@ -329,9 +323,9 @@ trait Applications extends Compatibility { self: Typer => /** Subclass of Application for applicability tests with trees as arguments. */ class ApplicableToTrees(methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context) extends TestApplication(methRef, methRef, args, resultType) { - def isVarArg(arg: Tree): Boolean = tpd.isWildcardStarArg(arg) def argType(arg: Tree): Type = normalize(arg.tpe) def treeToArg(arg: Tree): Tree = arg + def isVarArg(arg: Tree): Boolean = tpd.isWildcardStarArg(arg) } /** Subclass of Application for applicability tests with types as arguments. */ @@ -401,9 +395,9 @@ trait Applications extends Compatibility { self: Typer => case nil => -1 } } - def sameSeq[T <: Trees.Tree[_]](xs: List[T], ys: List[T]): Boolean = firstDiff(xs, ys) < 0 + private def sameSeq[T <: Trees.Tree[_]](xs: List[T], ys: List[T]): Boolean = firstDiff(xs, ys) < 0 - val result ={ + val result = { var typedArgs = typedArgBuf.toList val ownType = ctx.traceIndented(i"apply $methRef to $typedArgs%, %", show = true) { if (!success) ErrorType @@ -421,9 +415,7 @@ trait Applications extends Compatibility { self: Typer => methodType.instantiate(typedArgs.tpes) } } - val app1 = cpy.Apply(app, normalizedFun, typedArgs).withType(ownType) - if (liftedDefs != null && liftedDefs.nonEmpty) Block(liftedDefs.toList, app1) - else app1 + wrapDefs(liftedDefs, cpy.Apply(app, normalizedFun, typedArgs).withType(ownType)) } } @@ -441,75 +433,74 @@ trait Applications extends Compatibility { self: Typer => def treeToArg(arg: Tree): Tree = arg } - def typedApply(app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context): Tree = track("typedApply") { - new ApplyToTyped(app, fun, methRef, args, resultType).result - } - - def typedApply(fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context): Tree = - typedApply(untpd.Apply(untpd.TypedSplice(fun), args), fun, methRef, args, resultType) - def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { - if (ctx.mode is Mode.Pattern) - typedUnApply(tree, pt) - else { - - def realApply(implicit ctx: Context): Tree = track("realApply") { - val proto = new FunProto(tree.args, pt, this) - val fun1 = typedExpr(tree.fun, proto) - methPart(fun1).tpe match { - case funRef: TermRef => - tryEither { implicit ctx => - val app = - if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) - else new ApplyToUntyped(tree, fun1, funRef, proto, pt) - val result = app.result - ConstFold(result) orElse result - } { failed => fun1 match { - case Select(qual, name) => - tryEither { implicit ctx => - val qual1 = adaptInterpolated(qual, new SelectionProto(name, proto)) - if (qual1.tpe.isError || (qual1 eq qual)) qual1 - else - typedApply( - cpy.Apply(tree, - cpy.Select(fun1, untpd.TypedSplice(qual1), name), - proto.typedArgs map untpd.TypedSplice), - pt) - } { _ => failed.commit() - } - case _ => - failed.commit() - } - } - case _ => - fun1.tpe match { - case ErrorType => - tree.withType(ErrorType) + + def realApply(implicit ctx: Context): Tree = track("realApply") { + val proto = new FunProto(tree.args, pt, this) + val fun1 = typedExpr(tree.fun, proto) + methPart(fun1).tpe match { + case funRef: TermRef => + tryEither { implicit ctx => + val app = + if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) + else new ApplyToUntyped(tree, fun1, funRef, proto, pt) + val result = app.result + ConstFold(result) orElse result + } { failed => fun1 match { + case Select(qual, name) => + // try with prototype `[].name(args)`, this might succeed by inserting an + // implicit conversion around []. (an example is Int + BigInt). + tryEither { implicit ctx => + val qual1 = adaptInterpolated(qual, new SelectionProto(name, proto)) + if (qual1.tpe.isError || (qual1 eq qual)) qual1 + else + typedApply( + cpy.Apply(tree, + cpy.Select(fun1, untpd.TypedSplice(qual1), name), + proto.typedArgs map untpd.TypedSplice), + pt) + } { _ => failed.commit() + } + case _ => + failed.commit() } - } + } + case _ => + fun1.tpe match { + case ErrorType => + tree.withType(ErrorType) + } } + } - def typedOpAssign: Tree = track("typedOpAssign") { - val Apply(Select(lhs, name), rhss) = tree - val lhs1 = typedExpr(lhs) - val lifted = new mutable.ListBuffer[Tree] - val lhs2 = untpd.TypedSplice(liftApp(lifted, lhs1)) - val assign = untpd.Assign(lhs2, untpd.Apply(untpd.Select(lhs2, name.init), rhss)) - typed(assign) - } + /** Convert expression like + * + * e += (args) + * + * where the lifted-for-assignment version of e is { val xs = es; e' } to + * + * { val xs = es; e' = e' + args } + */ + def typedOpAssign: Tree = track("typedOpAssign") { + val Apply(Select(lhs, name), rhss) = tree + val lhs1 = typedExpr(lhs) + val liftedDefs = new mutable.ListBuffer[Tree] + val lhs2 = untpd.TypedSplice(liftAssigned(liftedDefs, lhs1)) + val assign = untpd.Assign(lhs2, untpd.Apply(untpd.Select(lhs2, name.init), rhss)) + wrapDefs(liftedDefs, typed(assign)) + } - if (untpd.isOpAssign(tree)) + if (untpd.isOpAssign(tree)) + tryEither { + implicit ctx => realApply + } { failed => tryEither { - implicit ctx => realApply - } { failed => - tryEither { - implicit ctx => typedOpAssign - } { _ => - failed.commit() - } + implicit ctx => typedOpAssign + } { _ => + failed.commit() } - else realApply - } + } + else realApply } def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context): Tree = track("typedTypeApply") { @@ -846,4 +837,13 @@ trait Applications extends Compatibility { self: Typer => if (isDetermined(candidates)) candidates else narrowMostSpecific(candidates)(ctx.retractMode(ImplicitsEnabled)) } -} \ No newline at end of file +} + +/* + def typedApply(app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context): Tree = track("typedApply") { + new ApplyToTyped(app, fun, methRef, args, resultType).result + } + + def typedApply(fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context): Tree = + typedApply(untpd.Apply(untpd.TypedSplice(fun), args), fun, methRef, args, resultType) +*/ \ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala index 51d55221d..2eb7486eb 100644 --- a/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -29,6 +29,19 @@ object EtaExpansion { Ident(sym.valRef) } + /** Lift out common part of tree taking part in an operator assignment such as + * + * tree += expr + */ + def liftAssigned(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match { + case Apply(fn, args) => + cpy.Apply(tree, lift(defs, fn), liftArgs(defs, fn.tpe, args)) + case Select(pre, name) => + cpy.Select(tree, lift(defs, pre), name) + case _ => + tree + } + /** Lift arguments that are not-idempotent into ValDefs in buffer `defs` * and replace by the idents of so created ValDefs. */ @@ -40,7 +53,7 @@ object EtaExpansion { else lift(defs, arg, if (name contains '$') "" else name.toString) } case _ => - args map (lift(defs, _, "")) + args map (lift(defs, _)) } /** Lift out function prefix and all arguments from application @@ -63,12 +76,10 @@ object EtaExpansion { cpy.TypeApply(tree, liftApp(defs, fn), targs) case Select(pre, name) if tpd.isIdempotentRef(tree) => cpy.Select(tree, lift(defs, pre), name) - case tree: RefTree => - lift(defs, tree) case Block(stats, expr) => liftApp(defs ++= stats, expr) case _ => - tree + lift(defs, tree) } /** Eta-expanding a tree means converting a method reference to a function value. @@ -94,8 +105,11 @@ object EtaExpansion { } val defs = new mutable.ListBuffer[Tree] val lifted = liftApp(defs, tree) - Block(defs.toList, expand(lifted)) + wrapDefs(defs, expand(lifted)) } + + def wrapDefs(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = + if (defs != null && defs.nonEmpty) tpd.Block(defs.toList, tree) else tree } /**

not needed diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index f0d4d7493..c39007a9e 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -420,6 +420,10 @@ class Typer extends Namer with Applications with Implicits { tree.lhs match { case lhs @ Apply(fn, args) => typed(cpy.Apply(lhs, untpd.Select(fn, nme.update), args :+ tree.rhs), pt) + case untpd.TypedSplice(Apply(Select(lhs, app), args)) if app == nme.apply => + typed(cpy.Apply(lhs, + untpd.Select(untpd.TypedSplice(lhs), nme.update), + (args map untpd.TypedSplice) :+ tree.rhs), pt) case lhs => val lhs1 = typed(lhs) def reassignmentToVal = @@ -620,7 +624,7 @@ class Typer extends Namer with Applications with Implicits { assert(isFullyDefined(pt, ForceDegree.none)) (EmptyTree, pt) case original: ValDef => - val meth = symbolOfTree(original) + val meth = original.symbol // ??? was: symbolOfTree(original) TODO: come back to this assert(meth.exists, meth) (EmptyTree, meth.info) case original => @@ -819,7 +823,8 @@ class Typer extends Namer with Applications with Implicits { } def typedUnnamed(tree: untpd.Tree): Tree = tree match { - case tree: untpd.Apply => typedApply(tree, pt) + case tree: untpd.Apply => + if (ctx.mode is Mode.Pattern) typedUnApply(tree, pt) else typedApply(tree, pt) case tree: untpd.This => typedThis(tree) case tree: untpd.Literal => typedLiteral(tree) case tree: untpd.New => typedNew(tree, pt) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index b830471e5..247acaf5a 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -21,6 +21,7 @@ class tests extends CompilerTest { @Test def pos_Patterns() = compileFile(posDir, "Patterns") @Test def pos_selftypes() = compileFile(posDir, "selftypes") @Test def pos_varargs() = compileFile(posDir, "varargs") + @Test def pos_opassign() = compileFile(posDir, "opassign") @Test def neg_blockescapes() = compileFile(negDir, "blockescapesNeg", xerrors = 2) diff --git a/tests/pos/opassign.scala b/tests/pos/opassign.scala new file mode 100644 index 000000000..7b8fec652 --- /dev/null +++ b/tests/pos/opassign.scala @@ -0,0 +1,28 @@ +object opassign { + + var count: Int = 0 + + def next = { count += 1; count } + + var x: Int = 0 + x += 1 + + { var x: Int = 0 + x += 1 + } + + class Ref { + var x: Int + } + val r = new Ref + r.x += 1 + + val arr = new Array[Int](10) + arr(0) += 1 + + def f(x: Int): Ref = new Ref + f(next).x += 1 + + val buf = new collection.mutable.ListBuffer[Int] + buf += 1 +} \ No newline at end of file -- cgit v1.2.3