Skip to content

Fix #2367: Allow SAM conversion for overloaded functions #2894

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
Jul 25, 2017
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
41 changes: 22 additions & 19 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -474,21 +474,24 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
type TypedArg = Arg
type Result = Unit

protected def argOK(arg: TypedArg, formal: Type) = argType(arg, formal) match {
case ref: TermRef if ref.denot.isOverloaded =>
// in this case we could not resolve overloading because no alternative
// matches expected type
false
case argtpe =>
def SAMargOK = formal match {
case SAMType(meth) => argtpe <:< meth.info.toFunctionType()
case _ => false
}
isCompatible(argtpe, formal) || ctx.mode.is(Mode.ImplicitsEnabled) && SAMargOK
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add some note that implicit search and SAM conversion are tried at the same time to justify checking ImplicitsEnabled here?

}

/** The type of the given argument */
protected def argType(arg: Arg, formal: Type): Type

def typedArg(arg: Arg, formal: Type): Arg = arg
def addArg(arg: TypedArg, formal: Type) =
ok = ok & {
argType(arg, formal) match {
case ref: TermRef if ref.denot.isOverloaded =>
// in this case we could not resolve overloading because no alternative
// matches expected type
false
case argtpe =>
isCompatible(argtpe, formal)
}
}
final def addArg(arg: TypedArg, formal: Type) = ok = ok & argOK(arg, formal)
def makeVarArg(n: Int, elemFormal: Type) = {}
def fail(msg: => Message, arg: Arg) =
ok = false
Expand All @@ -512,11 +515,10 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
}

/** Subclass of Application for applicability tests with type arguments and value
* argument trees.
*/
* argument trees.
*/
class ApplicableToTreesDirectly(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context) extends ApplicableToTrees(methRef, targs, args, resultType)(ctx) {
override def addArg(arg: TypedArg, formal: Type) =
ok = ok & (argType(arg, formal) <:< formal)
override def argOK(arg: TypedArg, formal: Type) = argType(arg, formal) <:< formal
}

/** Subclass of Application for applicability tests with value argument types. */
Expand Down Expand Up @@ -1197,7 +1199,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
/** Resolve overloaded alternative `alts`, given expected type `pt` and
* possibly also type argument `targs` that need to be applied to each alternative
* to form the method type.
* todo: use techniques like for implicits to pick candidates quickly?
* Two trials: First, without implicits or SAM conversions enabled. Then,
* if the fist finds no eligible candidates, with implicits and SAM conversions enabled.
*/
def resolveOverloaded(alts: List[TermRef], pt: Type)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") {

Expand All @@ -1222,7 +1225,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
* fallback to `chosen`.
*
* Note this order of events is done for speed. One might be tempted to
* preselect alternatives by result type. But is slower, because it discriminates
* preselect alternatives by result type. But this is slower, because it discriminates
* less. The idea is when searching for a best solution, as is the case in overloading
* resolution, we should first try criteria which are cheap and which have a high
* probability of pruning the search. result type comparisons are neither cheap nor
Expand Down Expand Up @@ -1258,7 +1261,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
/** This private version of `resolveOverloaded` does the bulk of the work of
* overloading resolution, but does not do result adaptation. It might be
* called twice from the public `resolveOverloaded` method, once with
* implicits enabled, and once without.
* implicits and SAM conversions enabled, and once without.
*/
private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type])(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") {

Expand Down Expand Up @@ -1338,7 +1341,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
if (isDetermined(alts1)) alts1
else {
val alts2 = narrowByShapes(alts1)
//ctx.log(i"narrowed by shape: ${alts1.map(_.symbol.showDcl)}%, %")
//ctx.log(i"narrowed by shape: ${alts2.map(_.symbol.showDcl)}%, %")
if (isDetermined(alts2)) alts2
else {
pretypeArgs(alts2, pt)
Expand Down
9 changes: 7 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2161,6 +2161,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}

def toSAM(tree: Tree): Tree = tree match {
case tree: Block => tpd.cpy.Block(tree)(tree.stats, toSAM(tree.expr))
case tree: Closure => cpy.Closure(tree)(tpt = TypeTree(pt)).withType(pt)
}

def adaptToSubType(wtp: Type): Tree = {
// try converting a constant to the target type
val folded = ConstFold(tree, pt)
Expand All @@ -2172,7 +2177,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
return tpd.Block(adapt(tree, WildcardType) :: Nil, Literal(Constant(())))
// convert function literal to SAM closure
tree match {
case Closure(Nil, id @ Ident(nme.ANON_FUN), _)
case closure(Nil, id @ Ident(nme.ANON_FUN), _)
if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) =>
pt match {
case SAMType(meth)
Expand All @@ -2181,7 +2186,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
// but this prevents case blocks from implementing polymorphic partial functions,
// since we do not know the result parameter a priori. Have to wait until the
// body is typechecked.
return cpy.Closure(tree)(Nil, id, TypeTree(pt)).withType(pt)
return toSAM(tree)
case _ =>
}
case _ =>
Expand Down
8 changes: 8 additions & 0 deletions tests/pos/i2367.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
object Test {
new Thread(() => println("hi"))

def foo(x: Int => Int, y: Int): Int = 1
def foo(x: Int, y: Int): Int = 2
foo(1, 2)
foo(x => x, 2)
}