Skip to content

Fix #5606: Handle closures in extension method calls #5619

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,25 @@ object Applications {

/** A wrapper indicating that its argument is an application of an extension method.
*/
case class ExtMethodApply(app: Tree) extends tpd.Tree {
class ExtMethodApply(val app: Tree) extends tpd.Tree {
override def pos = app.pos

def canEqual(that: Any): Boolean = app.canEqual(that)
def productArity: Int = app.productArity
def productElement(n: Int): Any = app.productElement(n)
}

/** The unapply method of this extractor also recognizes ExtMethodApplys in closure blocks.
* This is necessary to deal with closures as left arguments of extension method applications.
* A test case is i5606.scala
*/
object ExtMethodApply {
def apply(app: Tree) = new ExtMethodApply(app)
def unapply(tree: Tree)(implicit ctx: Context): Option[Tree] = tree match {
case tree: ExtMethodApply => Some(tree.app)
case Block(stats, ExtMethodApply(app)) => Some(tpd.cpy.Block(tree)(stats, app))
case _ => None
}
}
}

Expand Down
15 changes: 9 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import EtaExpansion.etaExpand
import util.Positions._
import util.common._
import util.Property
import Applications.{ExtMethodApply, wrapDefs, productSelectorTypes}

import collection.mutable
import annotation.tailrec
Expand Down Expand Up @@ -426,7 +427,7 @@ class Typer extends Namer

def typeSelectOnTerm(implicit ctx: Context): Tree =
typedExpr(tree.qualifier, selectionProto(tree.name, pt, this)) match {
case qual1 @ Applications.ExtMethodApply(app) =>
case qual1 @ ExtMethodApply(app) =>
pt.revealIgnored match {
case _: PolyProto => qual1 // keep the ExtMethodApply to strip at next typedTypeApply
case _ => app
Expand Down Expand Up @@ -926,7 +927,7 @@ class Typer extends Namer
def ptIsCorrectProduct(formal: Type) = {
isFullyDefined(formal, ForceDegree.noBottom) &&
(defn.isProductSubType(formal) || formal.derivesFrom(defn.PairClass)) &&
Applications.productSelectorTypes(formal).corresponds(params) {
productSelectorTypes(formal).corresponds(params) {
(argType, param) =>
param.tpt.isEmpty || argType <:< typedAheadType(param.tpt).tpe
}
Expand Down Expand Up @@ -1837,7 +1838,7 @@ class Typer extends Namer
case Block(stats, expr) =>
tpd.cpy.Block(app)(stats, lift(expr))
}
Applications.wrapDefs(defs, lift(app))
wrapDefs(defs, lift(app))
}
}

Expand Down Expand Up @@ -2640,7 +2641,9 @@ class Typer extends Namer
val app = tryExtension(nestedCtx)
if (!app.isEmpty && !nestedCtx.reporter.hasErrors) {
nestedCtx.typerState.commit()
return Applications.ExtMethodApply(app).withType(app.tpe)
return ExtMethodApply(app).withType(WildcardType)
// Use wildcard type in order not to prompt any further adaptations such as eta expansion
// before the method is fully applied.
}
case _ =>
}
Expand All @@ -2652,7 +2655,7 @@ class Typer extends Namer
else err.typeMismatch(tree, pt, failure)
if (ctx.mode.is(Mode.ImplicitsEnabled) && tree.typeOpt.isValueType)
inferView(tree, pt) match {
case SearchSuccess(inferred: Applications.ExtMethodApply, _, _) =>
case SearchSuccess(inferred: ExtMethodApply, _, _) =>
inferred // nothing to check or adapt for extension method applications
case SearchSuccess(inferred, _, _) =>
checkImplicitConversionUseOK(inferred.symbol, tree.pos)
Expand Down Expand Up @@ -2732,7 +2735,7 @@ class Typer extends Namer
adaptToArgs(wtp, pt)
case pt: PolyProto =>
tree match {
case _: Applications.ExtMethodApply => tree
case _: ExtMethodApply => tree
case _ => tryInsertApplyOrImplicit(tree, pt, locked)(tree) // error will be reported in typedTypeApply
}
case _ =>
Expand Down
14 changes: 14 additions & 0 deletions tests/run/i5606.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
object Test extends App {

def (f: A => B) $[A, B](a: A): B = f(a)

assert((((a: Int) => a.toString()) $ 10) == "10")

def g(x: Int): String = x.toString

assert((g $ 10) == "10")

val h: Int => String = _.toString

assert((h $ 10) == "10")
}