summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan@lightbend.com>2016-10-04 12:48:30 -0500
committerAdriaan Moors <adriaan@lightbend.com>2016-10-04 15:48:33 -0500
commit20896646122fa82dc81f1405173b09eac37ae7cc (patch)
tree6422d91f1142c7c393eed6433700ae226d3a2157
parent1f6006d0d4f8edf4db04915702f8b7e3c8ca1f5e (diff)
downloadscala-20896646122fa82dc81f1405173b09eac37ae7cc.tar.gz
scala-20896646122fa82dc81f1405173b09eac37ae7cc.tar.bz2
scala-20896646122fa82dc81f1405173b09eac37ae7cc.zip
SI-9943 final/sealed class does not yield SAM type
Cannot subclass such a class. (Well, we could subclass a sealed class in the same compilation unit. We ignore this for simplicity.) This is a bit of a sneaky fix for this bug, but our hand is pretty much forced by other constraints, in this intersection of overload resolution involving built-in function types and SAMs, and type inference for higher-order function literals (#5307). Luckily, in this particular issue, the overloading clash seems accidental. The `sealed` `<:<` class is not a SAM type as it cannot be subclassed outside of `Predef`. For simplicity, we don't consider where the SAM conversion occurs and exclude all sealed classes from yielding SAM types. Thanks to Miles for pointing out that `final` was missing in my first iteration of this fix.
-rw-r--r--spec/06-expressions.md1
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala4
-rw-r--r--test/files/pos/t9943.scala9
3 files changed, 12 insertions, 2 deletions
diff --git a/spec/06-expressions.md b/spec/06-expressions.md
index 2b238d149a..468d9f5fef 100644
--- a/spec/06-expressions.md
+++ b/spec/06-expressions.md
@@ -1361,6 +1361,7 @@ Note that a function literal that targets a SAM is not necessarily compiled to t
It follows that:
- if class `C` defines a constructor, it must be accessible and must define exactly one, empty, argument list;
+ - class `C` cannot be `final` or `sealed` (for simplicity we ignore the possibility of SAM conversion in the same compilation unit as the sealed class);
- `m` cannot be polymorphic;
- it must be possible to derive a fully-defined type `U` from `S` by inferring any unknown type parameters of `C`.
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 0f7cf07f08..fc7e184918 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -840,14 +840,14 @@ trait Definitions extends api.StandardDefinitions {
*
* The method must be monomorphic and have exactly one parameter list.
* The class defining the method is a supertype of `tp` that
- * has a public no-arg primary constructor.
+ * has a public no-arg primary constructor and it can be subclassed (not final or sealed).
*/
def samOf(tp: Type): Symbol = if (!doSam) NoSymbol else if (!isNonRefinementClassType(unwrapToClass(tp))) NoSymbol else {
// look at erased type because we (only) care about what ends up in bytecode
// (e.g., an alias type is fine as long as is compiles to a single-abstract-method)
val tpSym: Symbol = erasure.javaErasure(tp).typeSymbol
- if (tpSym.exists && tpSym.isClass
+ if (tpSym.exists && tpSym.isClass && !(tpSym hasFlag (FINAL | SEALED))
// if tp has a constructor (its class is not a trait), it must be public and must not take any arguments
// (implementation restriction: implicit argument lists are excluded to simplify type inference in adaptToSAM)
&& { val ctor = tpSym.primaryConstructor
diff --git a/test/files/pos/t9943.scala b/test/files/pos/t9943.scala
new file mode 100644
index 0000000000..0d4717ccbb
--- /dev/null
+++ b/test/files/pos/t9943.scala
@@ -0,0 +1,9 @@
+class Foo[T] {
+ def toMap[K, V](implicit ev: Foo[T] <:< Foo[(K, V)]): Foo[Map[K, V]] = null
+ def toMap[K](keySelector: T => K): Foo[Map[K, T]] = null
+}
+
+object Foo {
+ (??? : Foo[Int]) toMap (_ % 2)
+ (??? : Foo[(Int, String)]).toMap
+}