From aa972dc100544179beecde48b52dfdb847162001 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 23 Mar 2016 17:53:19 -0700 Subject: SAM conversion precedes implicit view application (as in dotty). This reflects the majority vote on the PR. DSLs that need their implicit conversions to kick in instead of SAM conversion, will have to make their target types not be SAM types (e.g., by adding a second abstract method to them). --- spec/06-expressions.md | 22 +++++++++++----------- .../scala/tools/nsc/typechecker/Typers.scala | 16 ++++++++-------- test/files/run/sammy_after_implicit_view.scala | 20 ++++++++++---------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/spec/06-expressions.md b/spec/06-expressions.md index bf1a6acf9a..30ad73a3cd 100644 --- a/spec/06-expressions.md +++ b/spec/06-expressions.md @@ -1345,17 +1345,6 @@ If $e$ has some value type and the expected type is `Unit`, $e$ is converted to the expected type by embedding it in the term `{ $e$; () }`. -###### View Application -If none of the previous conversions applies, and $e$'s type -does not conform to the expected type $\mathit{pt}$, it is attempted to convert -$e$ to the expected type with a [view](07-implicits.html#views). - -###### Selection on `Dynamic` -If none of the previous conversions applies, and $e$ is a prefix -of a selection $e.x$, and $e$'s type conforms to class `scala.Dynamic`, -then the selection is rewritten according to the rules for -[dynamic member selection](#dynamic-member-selection). - ###### SAM conversion An expression `(p1, ..., pN) => body` of function type `(T1, ..., TN) => T` is sam-convertible to the expected type `S` if the following holds: - the class `C` of `S` declares an abstract method `m` with signature `(p1: A1, ..., pN: AN): R`; @@ -1379,6 +1368,17 @@ Finally, we impose some implementation restrictions (these may be lifted in futu - `C` must not declare a self type (this simplifies type inference); - `C` must not be `@specialized`. +###### View Application +If none of the previous conversions applies, and $e$'s type +does not conform to the expected type $\mathit{pt}$, it is attempted to convert +$e$ to the expected type with a [view](07-implicits.html#views). + +###### Selection on `Dynamic` +If none of the previous conversions applies, and $e$ is a prefix +of a selection $e.x$, and $e$'s type conforms to class `scala.Dynamic`, +then the selection is rewritten according to the rules for +[dynamic member selection](#dynamic-member-selection). + ### Method Conversions The following four implicit conversions can be applied to methods diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index a9d5b69e2e..52242c10b3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1053,6 +1053,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (hasUndets) return instantiate(tree, mode, pt) + // we know `!(tree.tpe <:< pt)`; try to remedy if there's a sam for pt + val sam = samMatchingFunction(tree, pt) // this implies tree.isInstanceOf[Function] + if (sam.exists && !tree.tpe.isErroneous) { + val samTree = adaptToSAM(sam, tree.asInstanceOf[Function], pt, mode) + if (samTree ne EmptyTree) + return samTree.updateAttachment(SAMFunction(pt, sam)) + } + if (context.implicitsEnabled && !pt.isError && !tree.isErrorTyped) { // (14); the condition prevents chains of views inferView(tree, tree.tpe, pt) match { @@ -1071,14 +1079,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } } - - // we know `!(tree.tpe <:< pt)`; try to remedy if there's a sam for pt - val sam = samMatchingFunction(tree, pt) // this implies tree.isInstanceOf[Function] - if (sam.exists && !tree.tpe.isErroneous) { - val samTree = adaptToSAM(sam, tree.asInstanceOf[Function], pt, mode) - if (samTree ne EmptyTree) - return samTree.updateAttachment(SAMFunction(pt, sam)) - } } debuglog("error tree = " + tree) diff --git a/test/files/run/sammy_after_implicit_view.scala b/test/files/run/sammy_after_implicit_view.scala index 30e3babc75..a13a71e562 100644 --- a/test/files/run/sammy_after_implicit_view.scala +++ b/test/files/run/sammy_after_implicit_view.scala @@ -5,24 +5,24 @@ object Test extends App { final val AnonFunClass = "$anon$" final val LMFClass = "$$Lambda$" // LambdaMetaFactory names classes like this - // if there's an implicit conversion, it takes precedence - def statusQuo() = { + // if there's an implicit conversion, it does not takes precedence (because that's what dotty does) + def implicitSam() = { import language.implicitConversions - var ok = false - implicit def fun2sam(fun: Int => String): MySam = { ok = true; new MySam { def apply(x: Int) = fun(x) } } + var ok = true + implicit def fun2sam(fun: Int => String): MySam = { ok = false; new MySam { def apply(x: Int) = fun(x) } } val className = (((x: Int) => x.toString): MySam).getClass.toString assert(ok, "implicit conversion not called") - assert(className contains AnonFunClass, className) - assert(!(className contains LMFClass), className) + assert(!(className contains AnonFunClass), className) + assert(className contains LMFClass, className) } // indirectly check that this sam type instance was created from a class spun up by LambdaMetaFactory - def statusIndy() = { + def justSammy() = { val className = (((x: Int) => x.toString): MySam).getClass.toString assert(!(className contains AnonFunClass), className) assert(className contains LMFClass, className) } - statusQuo() - statusIndy() -} \ No newline at end of file + implicitSam() + justSammy() +} -- cgit v1.2.3