From 528f6b0524c465a3e795aa5bb538c680956bf6d2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 17 Sep 2016 22:32:10 +0200 Subject: Fix #1503 - be careful where to insert an apply. `apply` nodes should not be inserted in the result parts of a block, if-then-else, match, or try. Instead they should be added to the surrounding statement. --- src/dotty/tools/dotc/ast/Desugar.scala | 2 +- src/dotty/tools/dotc/ast/untpd.scala | 10 ++++++++++ src/dotty/tools/dotc/core/Types.scala | 7 +++++-- src/dotty/tools/dotc/typer/Applications.scala | 2 +- src/dotty/tools/dotc/typer/ProtoTypes.scala | 4 ++++ src/dotty/tools/dotc/typer/Typer.scala | 16 ++++++++-------- 6 files changed, 29 insertions(+), 12 deletions(-) (limited to 'src/dotty') diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 70d8f2d5e..631b1bf8d 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -699,7 +699,7 @@ object desugar { Apply(Select(left, op), args) } else { val x = ctx.freshName().toTermName - Block( + new InfixOpBlock( ValDef(x, TypeTree(), left).withMods(synthetic), Apply(Select(right, op), Ident(x))) } diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index cef78c6e6..6bbb76b89 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -63,6 +63,16 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { class PolyTypeDef(name: TypeName, override val tparams: List[TypeDef], rhs: Tree) extends TypeDef(name, rhs) + /** A block arising from a right-associative infix operation, where, e.g. + * + * a +: b + * + * is expanded to + * + * { val x = a; b.+:(x) } + */ + class InfixOpBlock(leftOperand: Tree, rightOp: Tree) extends Block(leftOperand :: Nil, rightOp) + // ----- TypeTrees that refer to other tree's symbols ------------------- /** A type tree that gets its type from some other tree's symbol. Enters the diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index cb423e186..d788b7e69 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -927,7 +927,7 @@ object Types { def narrow(implicit ctx: Context): TermRef = TermRef(NoPrefix, ctx.newSkolem(this)) - /** Useful for diagnsotics: The underlying type if this type is a type proxy, + /** Useful for diagnostics: The underlying type if this type is a type proxy, * otherwise NoType */ def underlyingIfProxy(implicit ctx: Context) = this match { @@ -935,7 +935,10 @@ object Types { case _ => NoType } - // ----- Normalizing typerefs over refined types ---------------------------- + /** If this is a FunProto or PolyProto, WildcardType, otherwise this */ + def notApplied: Type = this + + // ----- Normalizing typerefs over refined types ---------------------------- /** If this normalizes* to a refinement type that has a refinement for `name` (which might be followed * by other refinements), and the refined info is a type alias, return the alias, diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index a9212e5d6..20850e21d 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -652,7 +652,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => /** Overridden in ReTyper to handle primitive operations that can be generated after erasure */ protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree = - throw new Error(s"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}") + throw new Error(i"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}") def typedNamedArgs(args: List[untpd.Tree])(implicit ctx: Context) = for (arg @ NamedArg(id, argtpt) <- args) yield { diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala index f209c99be..41628f0dc 100644 --- a/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -182,6 +182,8 @@ object ProtoTypes { if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this else new FunProto(args, resultType, typer) + override def notApplied = WildcardType + /** Forget the types of any arguments that have been typed producing a constraint in a * typer state that is not yet committed into the one of the current context `ctx`. * This is necessary to avoid "orphan" PolyParams that are referred to from @@ -319,6 +321,8 @@ object ProtoTypes { if ((targs eq this.targs) && (resType eq this.resType)) this else PolyProto(targs, resType) + override def notApplied = WildcardType + def map(tm: TypeMap)(implicit ctx: Context): PolyProto = derivedPolyProto(targs mapConserve tm, tm(resultType)) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 3f8c421d9..c59cd38c3 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -572,7 +572,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = track("typedBlock") { val exprCtx = index(tree.stats) val stats1 = typedStats(tree.stats, ctx.owner) - val expr1 = typedExpr(tree.expr, pt)(exprCtx) + val ept = if (tree.isInstanceOf[untpd.InfixOpBlock]) pt else pt.notApplied + val expr1 = typedExpr(tree.expr, ept)(exprCtx) ensureNoLocalRefs( assignType(cpy.Block(tree)(stats1, expr1), stats1, expr1), pt, localSyms(stats1)) } @@ -619,8 +620,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) = track("typedIf") { val cond1 = typed(tree.cond, defn.BooleanType) - val thenp1 = typed(tree.thenp, pt) - val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt) + val thenp1 = typed(tree.thenp, pt.notApplied) + val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied) val thenp2 :: elsep2 :: Nil = harmonize(thenp1 :: elsep1 :: Nil) assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2) } @@ -793,7 +794,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val selType = widenForMatchSelector( fullyDefinedType(sel1.tpe, "pattern selector", tree.pos)) - val cases1 = typedCases(tree.cases, selType, pt) + val cases1 = typedCases(tree.cases, selType, pt.notApplied) val cases2 = harmonize(cases1).asInstanceOf[List[CaseDef]] assignType(cpy.Match(tree)(sel1, cases2), cases2) } @@ -920,8 +921,8 @@ 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 cases1 = typedCases(tree.cases, defn.ThrowableType, pt) + val expr1 = typed(tree.expr, pt.notApplied) + val cases1 = typedCases(tree.cases, defn.ThrowableType, pt.notApplied) val finalizer1 = typed(tree.finalizer, defn.UnitType) val expr2 :: cases2x = harmonize(expr1 :: cases1) val cases2 = cases2x.asInstanceOf[List[CaseDef]] @@ -1535,8 +1536,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } /** If this tree is a select node `qual.name`, try to insert an implicit conversion - * `c` around `qual` so that `c(qual).name` conforms to `pt`. If that fails - * return `tree` itself. + * `c` around `qual` so that `c(qual).name` conforms to `pt`. */ def tryInsertImplicitOnQualifier(tree: Tree, pt: Type)(implicit ctx: Context): Option[Tree] = ctx.traceIndented(i"try insert impl on qualifier $tree $pt") { tree match { -- cgit v1.2.3