From ac868319ecf75bcffe6000a41379c5c12e92e62e Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Sun, 5 Feb 2017 12:47:05 +0100 Subject: Represent untyped operators as Ident instead of Name This has two advantages: - We can distinguish BackquotedIdent from Ident, allowing the user to use a defined "type `&`", see testcase. - We get better positions for the operators. This is useful in IDEs, for example to get the type at point. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 24 ++++++++++---------- compiler/src/dotty/tools/dotc/ast/Trees.scala | 5 +++++ compiler/src/dotty/tools/dotc/ast/untpd.scala | 20 ++++++++--------- .../src/dotty/tools/dotc/parsing/JavaParsers.scala | 2 +- .../src/dotty/tools/dotc/parsing/Parsers.scala | 26 ++++++++++------------ .../dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- .../tools/dotc/transform/PatternMatcher.scala | 2 -- .../src/dotty/tools/dotc/typer/EtaExpansion.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- 9 files changed, 44 insertions(+), 43 deletions(-) (limited to 'compiler/src/dotty/tools') diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 2e471e046..3835355e3 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -353,7 +353,7 @@ object desugar { for (i <- 0 until arity if nme.selectorName(i) `ne` caseParams(i).name) yield syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeIdent), caseParams(i).name)) def isRepeated(tree: Tree): Boolean = tree match { - case PostfixOp(_, nme.raw.STAR) => true + case PostfixOp(_, Ident(nme.raw.STAR)) => true case ByNameTypeTree(tree1) => isRepeated(tree1) case _ => false } @@ -739,23 +739,23 @@ object desugar { /** Translate infix operation expression left op right */ - def makeBinop(left: Tree, op: Name, right: Tree): Tree = { + def makeBinop(left: Tree, op: Ident, right: Tree): Tree = { def assignToNamedArg(arg: Tree) = arg match { case Assign(Ident(name), rhs) => cpy.NamedArg(arg)(name, rhs) case _ => arg } - if (isLeftAssoc(op)) { + if (isLeftAssoc(op.name)) { val args: List[Tree] = right match { case Parens(arg) => assignToNamedArg(arg) :: Nil case Tuple(args) => args mapConserve assignToNamedArg case _ => right :: Nil } - Apply(Select(left, op), args) + Apply(Select(left, op.name), args) } else { val x = ctx.freshName().toTermName new InfixOpBlock( ValDef(x, TypeTree(), left).withMods(synthetic), - Apply(Select(right, op), Ident(x))) + Apply(Select(right, op.name), Ident(x))) } } @@ -956,25 +956,25 @@ object desugar { Apply(Select(Apply(Ident(nme.StringContext), strs), id), elems) case InfixOp(l, op, r) => if (ctx.mode is Mode.Type) - if (op == tpnme.raw.AMP) AndTypeTree(l, r) // l & r - else if (op == tpnme.raw.BAR) OrTypeTree(l, r) // l | r - else AppliedTypeTree(Ident(op), l :: r :: Nil) // op[l, r] + if (!op.isBackquoted && op.name == tpnme.raw.AMP) AndTypeTree(l, r) // l & r + else if (!op.isBackquoted && op.name == tpnme.raw.BAR) OrTypeTree(l, r) // l | r + else AppliedTypeTree(op, l :: r :: Nil) // op[l, r] else if (ctx.mode is Mode.Pattern) - Apply(Ident(op), l :: r :: Nil) // op(l, r) + Apply(op, l :: r :: Nil) // op(l, r) else // l.op(r), or val x = r; l.op(x), plus handle named args specially makeBinop(l, op, r) case PostfixOp(t, op) => - if ((ctx.mode is Mode.Type) && op == nme.raw.STAR) { + if ((ctx.mode is Mode.Type) && !op.isBackquoted && op.name == nme.raw.STAR) { val seqType = if (ctx.compilationUnit.isJava) defn.ArrayType else defn.SeqType Annotated( AppliedTypeTree(ref(seqType), t), New(ref(defn.RepeatedAnnotType), Nil :: Nil)) } else { assert(ctx.mode.isExpr || ctx.reporter.hasErrors, ctx.mode) - Select(t, op) + Select(t, op.name) } case PrefixOp(op, t) => - Select(t, nme.UNARY_PREFIX ++ op) + Select(t, nme.UNARY_PREFIX ++ op.name) case Parens(t) => t case Tuple(ts) => diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 6dc2c9a13..7ab8de39f 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -334,10 +334,15 @@ object Trees { extends RefTree[T] { type ThisTree[-T >: Untyped] = Ident[T] def qualifier: Tree[T] = genericEmptyTree + + /** Is this a `BackquotedIdent` ? */ + def isBackquoted: Boolean = false } class BackquotedIdent[-T >: Untyped] private[ast] (name: Name) extends Ident[T](name) { + override def isBackquoted: Boolean = true + override def toString = s"BackquotedIdent($name)" } diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 37dc32c0e..99bc3240f 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -15,9 +15,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { // ----- Tree cases that exist in untyped form only ------------------ trait OpTree extends Tree { - def op: Name - override def isTerm = op.isTermName - override def isType = op.isTypeName + def op: Ident + override def isTerm = op.name.isTermName + override def isType = op.name.isTypeName } /** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice @@ -66,9 +66,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { */ class WildcardFunction(placeholderParams: List[ValDef], body: Tree) extends Function(placeholderParams, body) - case class InfixOp(left: Tree, op: Name, right: Tree) extends OpTree - case class PostfixOp(od: Tree, op: Name) extends OpTree - case class PrefixOp(op: Name, od: Tree) extends OpTree + case class InfixOp(left: Tree, op: Ident, right: Tree) extends OpTree + case class PostfixOp(od: Tree, op: Ident) extends OpTree + case class PrefixOp(op: Ident, od: Tree) extends OpTree case class Parens(t: Tree) extends ProxyTree { def forwardTo = t } @@ -357,7 +357,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { * parameter, the reference will be a repeated argument. */ def refOfDef(tree: MemberDef)(implicit ctx: Context) = tree match { - case ValDef(_, PostfixOp(_, nme.raw.STAR), _) => repeated(Ident(tree.name)) + case ValDef(_, PostfixOp(_, Ident(nme.raw.STAR)), _) => repeated(Ident(tree.name)) case _ => Ident(tree.name) } @@ -409,15 +409,15 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: Function if (args eq tree.args) && (body eq tree.body) => tree case _ => untpd.Function(args, body).withPos(tree.pos) } - def InfixOp(tree: Tree)(left: Tree, op: Name, right: Tree) = tree match { + def InfixOp(tree: Tree)(left: Tree, op: Ident, right: Tree) = tree match { case tree: InfixOp if (left eq tree.left) && (op eq tree.op) && (right eq tree.right) => tree case _ => untpd.InfixOp(left, op, right).withPos(tree.pos) } - def PostfixOp(tree: Tree)(od: Tree, op: Name) = tree match { + def PostfixOp(tree: Tree)(od: Tree, op: Ident) = tree match { case tree: PostfixOp if (od eq tree.od) && (op eq tree.op) => tree case _ => untpd.PostfixOp(od, op).withPos(tree.pos) } - def PrefixOp(tree: Tree)(op: Name, od: Tree) = tree match { + def PrefixOp(tree: Tree)(op: Ident, od: Tree) = tree match { case tree: PrefixOp if (op eq tree.op) && (od eq tree.od) => tree case _ => untpd.PrefixOp(op, od).withPos(tree.pos) } diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 92ab10db9..d35594823 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -448,7 +448,7 @@ object JavaParsers { if (in.token == DOTDOTDOT) { in.nextToken() t = atPos(t.pos.start) { - PostfixOp(t, nme.raw.STAR) + PostfixOp(t, Ident(nme.raw.STAR)) } } atPos(start, in.offset) { diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index f62093db0..5ed4fbdd7 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -30,7 +30,7 @@ object Parsers { import reporting.diagnostic.Message import reporting.diagnostic.messages._ - case class OpInfo(operand: Tree, operator: Name, offset: Offset) + case class OpInfo(operand: Tree, operator: Ident, offset: Offset) class ParensCounters { private var parCounts = new Array[Int](lastParen - firstParen) @@ -414,18 +414,17 @@ object Parsers { "left- and right-associative operators with same precedence may not be mixed", offset) def reduceStack(base: List[OpInfo], top: Tree, prec: Int, leftAssoc: Boolean): Tree = { - if (opStack != base && precedence(opStack.head.operator) == prec) - checkAssoc(opStack.head.offset, opStack.head.operator, leftAssoc) + if (opStack != base && precedence(opStack.head.operator.name) == prec) + checkAssoc(opStack.head.offset, opStack.head.operator.name, leftAssoc) def recur(top: Tree): Tree = { if (opStack == base) top else { val opInfo = opStack.head - val opPrec = precedence(opInfo.operator) + val opPrec = precedence(opInfo.operator.name) if (prec < opPrec || leftAssoc && prec == opPrec) { opStack = opStack.tail recur { - val opPos = Position(opInfo.offset, opInfo.offset + opInfo.operator.length, opInfo.offset) - atPos(opPos union opInfo.operand.pos union top.pos) { + atPos(opInfo.operator.pos union opInfo.operand.pos union top.pos) { InfixOp(opInfo.operand, opInfo.operator, top) } } @@ -449,10 +448,9 @@ object Parsers { val base = opStack var top = first while (isIdent && in.name != notAnOperator) { - val op = if (isType) in.name.toTypeName else in.name - top = reduceStack(base, top, precedence(op), isLeftAssoc(op)) + val op = if (isType) typeIdent() else termIdent() + top = reduceStack(base, top, precedence(op.name), isLeftAssoc(op.name)) opStack = OpInfo(top, op, in.offset) :: opStack - ident() newLineOptWhenFollowing(canStartOperand) if (maybePostfix && !canStartOperand(in.token)) { val topInfo = opStack.head @@ -870,7 +868,7 @@ object Parsers { val t = toplevelTyp() if (isIdent(nme.raw.STAR)) { in.nextToken() - atPos(startOffset(t)) { PostfixOp(t, nme.raw.STAR) } + atPos(startOffset(t)) { PostfixOp(t, Ident(nme.raw.STAR)) } } else t } @@ -1189,11 +1187,11 @@ object Parsers { val prefixExpr = () => if (isIdent && nme.raw.isUnary(in.name)) { val start = in.offset - val name = ident() - if (name == nme.raw.MINUS && isNumericLit) + val op = termIdent() + if (op.name == nme.raw.MINUS && isNumericLit) simpleExprRest(literal(start), canApply = true) else - atPos(start) { PrefixOp(name, simpleExpr()) } + atPos(start) { PrefixOp(op, simpleExpr()) } } else simpleExpr() @@ -1260,7 +1258,7 @@ object Parsers { val app = atPos(startOffset(t), in.offset) { Apply(t, argumentExprs()) } simpleExprRest(app, canApply = true) case USCORE => - atPos(startOffset(t), in.skipToken()) { PostfixOp(t, nme.WILDCARD) } + atPos(startOffset(t), in.skipToken()) { PostfixOp(t, Ident(nme.WILDCARD)) } case _ => t } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 7f124563d..5b75df6b2 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -516,7 +516,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { argsText ~ " => " ~ toText(body) } case InfixOp(l, op, r) => - val opPrec = parsing.precedence(op) + val opPrec = parsing.precedence(op.name) changePrec(opPrec) { toText(l) ~ " " ~ toText(op) ~ " " ~ toText(r) } case PostfixOp(l, op) => changePrec(InfixPrec) { toText(l) ~ " " ~ toText(op) } diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 181dfccd9..7c49e68dd 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -1077,8 +1077,6 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { trait MatchTranslator extends TreeMakers with ScalacPatternExpanders { - def isBackquoted(x: Ident) = x.isInstanceOf[BackquotedIdent] - def isVarPattern(pat: Tree): Boolean = pat match { case x: BackquotedIdent => false case x: Ident => x.name.isVariableName diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index c390ae808..e7e7ece78 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -144,7 +144,7 @@ object EtaExpansion { ids = ids.init :+ repeated(ids.last) var body: Tree = Apply(lifted, ids) mt.resultType match { - case rt: MethodType if !rt.isImplicit => body = PostfixOp(body, nme.WILDCARD) + case rt: MethodType if !rt.isImplicit => body = PostfixOp(body, Ident(nme.WILDCARD)) case _ => } val fn = untpd.Function(params, body) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 59df98a93..5ea36986f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1440,7 +1440,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedAsFunction(tree: untpd.PostfixOp, pt: Type)(implicit ctx: Context): Tree = { - val untpd.PostfixOp(qual, nme.WILDCARD) = tree + val untpd.PostfixOp(qual, Ident(nme.WILDCARD)) = tree val pt1 = if (defn.isFunctionType(pt)) pt else AnyFunctionProto var res = typed(qual, pt1) if (pt1.eq(AnyFunctionProto) && !defn.isFunctionClass(res.tpe.classSymbol)) { @@ -1541,7 +1541,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case tree: untpd.Annotated => typedAnnotated(tree, pt) case tree: untpd.TypedSplice => typedTypedSplice(tree) case tree: untpd.UnApply => typedUnApply(tree, pt) - case tree @ untpd.PostfixOp(qual, nme.WILDCARD) => typedAsFunction(tree, pt) + case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree case _ => typedUnadapted(desugar(tree), pt) } -- cgit v1.2.3 From b0576e977c26a86a400755ab9810b90af73b3b8d Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Sun, 5 Feb 2017 13:28:11 +0100 Subject: Better positions for infix term operations. Preserving the position of infix operators is useful for IDEs' type-at-point. We also preserve the position of the untyped lhs of right-associative operators, this is useful both for IDEs and for error messages, before: 4 |val x: List[Int] = "foo" :: List(1) | ^ | found: String($1$) | required: Int | After: scala> val x: List[Int] = "foo" :: List(1) -- [E007] Type Mismatch Error: --------------------------------------- 4 |val x: List[Int] = "foo" :: List(1) | ^^^^^ | found: String($1$) | required: Int | Note: It would be even nicer if we displayed "String" instead of "String($1$)" since $1$ is synthetic, this commit does not address this. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 6 ++++-- tests/repl/errmsgs.check | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'compiler/src/dotty/tools') diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 3835355e3..eda4a12dc 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -750,12 +750,14 @@ object desugar { case Tuple(args) => args mapConserve assignToNamedArg case _ => right :: Nil } - Apply(Select(left, op.name), args) + val selectPos = Position(left.pos.start, op.pos.end, op.pos.start) + Apply(Select(left, op.name).withPos(selectPos), args) } else { val x = ctx.freshName().toTermName + val selectPos = Position(op.pos.start, right.pos.end, op.pos.start) new InfixOpBlock( ValDef(x, TypeTree(), left).withMods(synthetic), - Apply(Select(right, op.name), Ident(x))) + Apply(Select(right, op.name).withPos(selectPos), Ident(x).withPos(left.pos))) } } diff --git a/tests/repl/errmsgs.check b/tests/repl/errmsgs.check index f0ccdf53f..0dc8e8ae5 100644 --- a/tests/repl/errmsgs.check +++ b/tests/repl/errmsgs.check @@ -78,4 +78,11 @@ scala> class Foo() { def bar: Int = 1 }; val foo = new Foo(); foo.barr 4 |class Foo() { def bar: Int = 1 }; val foo = new Foo(); foo.barr | ^^^^^^^^ | value `barr` is not a member of Foo(foo) - did you mean `foo.bar`? +scala> val x: List[Int] = "foo" :: List(1) +-- [E007] Type Mismatch Error: --------------------------------------- +4 |val x: List[Int] = "foo" :: List(1) + | ^^^^^ + | found: String($1$) + | required: Int + | scala> :quit -- cgit v1.2.3