diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2016-01-19 18:47:10 -0800 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2016-03-26 09:31:57 -0700 |
commit | 236d0e0db8b4c53837bd580e2b2022d86afde456 (patch) | |
tree | 897e8694338eb40cc1e54c3b0f0a1ccafd331866 /test | |
parent | 4f4fb45d7bc41564c3ba0483c5a663172e063994 (diff) | |
download | scala-236d0e0db8b4c53837bd580e2b2022d86afde456.tar.gz scala-236d0e0db8b4c53837bd580e2b2022d86afde456.tar.bz2 scala-236d0e0db8b4c53837bd580e2b2022d86afde456.zip |
Refactor typedFunction, rework synthesizeSAMFunction for sammy
`typedFunction` uniformly recognizes Single Abstract Method types
and built-in `FunctionN` types, type checking literals regardless
of expected type.
`adapt` synthesizes an anonymous subclass of the SAM type, if
needed to meet the expected (non-`FunctionN`) type.
(Later, we may want to carry `Function` AST nodes with SAM types
through the whole pipeline until the back-end, and treat them
uniformly with built-in function types there too, emitting the
corresponding `invokedynamic` & `LambdaMetaFactory` bytecode.
Would be faster to avoid synthesizing all this code during type
checking...)
Refactor `typedFunction` for performance and clarity to avoid
non-local returns. A nice perk is that the error message for missing
argument types now indicates with `<error>` where they are missing
(see updated check file).
Allow pattern matching function literals when SAM type is expected
(SI-8429).
Support `return` in function body of SAM target type, by making the
synthetic `sam$body` method transparent to the `enclMethod` chain, so
that the `return` is interpreted in its original context.
A cleaner approach to inferring unknown type params of the SAM
method. Now that `synthesizeSAMFunction` operates on typed `Function`
nodes, we can take the types of the parameters and the body and
compare them against the function type that corresponds to the SAM
method's signature. Since we are reusing the typed body, we do need
to change owners for the symbols, and substitute the new method
argument symbols for the function's vparam syms.
Impl Notes:
- The shift from typing as a regular Function for SAM types was
triggered by limitation of the old approach, which deferred type
checking the body until it was in the synthetic SAM type
subclass, which would break if the expression was subsequently
retypechecked for implicit search. Other problems related to SAM
expansion in ctor args also are dodged now.
- Using `<:<`, not `=:=`, in comparing `pt`, as `=:=` causes
`NoInstance` exceptions when `WildcardType`s are encountered.
- Can't use method type subtyping: method arguments are in
invariant pos.
- Can't use STATIC yet, results in illegal bytecode. It would be a
better encoding, since the function body should not see members
of SAM class.
- This is all battle tested by running `synthesizeSAMFunction` on
all `Function` nodes while bootstrapping, including those where a
regular function type is expected. The only thing that didn't
work was regarding Function0 and the CBN transform, which breaks
outer path creation in lambdalift.
Diffstat (limited to 'test')
-rw-r--r-- | test/files/neg/names-defaults-neg.check | 6 | ||||
-rw-r--r-- | test/files/neg/sammy_error_exist_no_crash.check | 4 | ||||
-rw-r--r-- | test/files/pos/sam_ctor_arg.scala | 4 | ||||
-rw-r--r-- | test/files/pos/sam_infer_argtype_subtypes.scala | 6 | ||||
-rw-r--r-- | test/files/pos/sam_inferargs.scala | 6 | ||||
-rw-r--r-- | test/files/pos/sammy_implicit.scala | 10 | ||||
-rw-r--r-- | test/files/pos/t8429.scala | 7 | ||||
-rw-r--r-- | test/files/run/sam_return.scala | 14 |
8 files changed, 51 insertions, 6 deletions
diff --git a/test/files/neg/names-defaults-neg.check b/test/files/neg/names-defaults-neg.check index 8a6aafd67a..875bc2ade0 100644 --- a/test/files/neg/names-defaults-neg.check +++ b/test/files/neg/names-defaults-neg.check @@ -130,7 +130,7 @@ names-defaults-neg.scala:102: error: unknown parameter name: m names-defaults-neg.scala:135: error: reference to var2 is ambiguous; it is both a method parameter and a variable in scope. delay(var2 = 40) ^ -names-defaults-neg.scala:138: error: missing parameter type for expanded function ((x$1) => a = x$1) +names-defaults-neg.scala:138: error: missing parameter type for expanded function ((x$1: <error>) => a = x$1) val taf2: Int => Unit = testAnnFun(a = _, b = get("+")) ^ names-defaults-neg.scala:138: error: not found: value a @@ -142,10 +142,10 @@ names-defaults-neg.scala:138: error: not found: value get names-defaults-neg.scala:139: error: parameter 'a' is already specified at parameter position 1 val taf3 = testAnnFun(b = _: String, a = get(8)) ^ -names-defaults-neg.scala:140: error: missing parameter type for expanded function ((x$3) => testAnnFun(x$3, ((x$4) => b = x$4))) +names-defaults-neg.scala:140: error: missing parameter type for expanded function ((x$3: <error>) => testAnnFun(x$3, ((x$4) => b = x$4))) val taf4: (Int, String) => Unit = testAnnFun(_, b = _) ^ -names-defaults-neg.scala:140: error: missing parameter type for expanded function ((x$4) => b = x$4) +names-defaults-neg.scala:140: error: missing parameter type for expanded function ((x$4: <error>) => b = x$4) val taf4: (Int, String) => Unit = testAnnFun(_, b = _) ^ names-defaults-neg.scala:140: error: not found: value b diff --git a/test/files/neg/sammy_error_exist_no_crash.check b/test/files/neg/sammy_error_exist_no_crash.check index a0d2237ce0..944b6471fd 100644 --- a/test/files/neg/sammy_error_exist_no_crash.check +++ b/test/files/neg/sammy_error_exist_no_crash.check @@ -1,6 +1,4 @@ -sammy_error_exist_no_crash.scala:5: error: Could not derive subclass of F[? >: String] - (with SAM `def method apply(s: String)Int`) - based on: ((x$1: String) => x$1.<parseInt: error>). +sammy_error_exist_no_crash.scala:5: error: value parseInt is not a member of String bar(_.parseInt) ^ one error found diff --git a/test/files/pos/sam_ctor_arg.scala b/test/files/pos/sam_ctor_arg.scala new file mode 100644 index 0000000000..3c556d59f0 --- /dev/null +++ b/test/files/pos/sam_ctor_arg.scala @@ -0,0 +1,4 @@ +trait Fun[A, B] { def apply(a: A): B } +// can't do sam expansion until the sam body def is a static method in the sam class, and not a local method in a block' +class C(f: Fun[Int, String]) +class Test extends C(s => "a")
\ No newline at end of file diff --git a/test/files/pos/sam_infer_argtype_subtypes.scala b/test/files/pos/sam_infer_argtype_subtypes.scala new file mode 100644 index 0000000000..63966f879e --- /dev/null +++ b/test/files/pos/sam_infer_argtype_subtypes.scala @@ -0,0 +1,6 @@ +trait Fun[A, B] { def apply(a: A): B } + +class SamInferResult { + def foreach[U](f: Fun[String, U]): U = ??? + def foo = foreach(println) +}
\ No newline at end of file diff --git a/test/files/pos/sam_inferargs.scala b/test/files/pos/sam_inferargs.scala new file mode 100644 index 0000000000..10d9b4f0dd --- /dev/null +++ b/test/files/pos/sam_inferargs.scala @@ -0,0 +1,6 @@ +trait Proc { def apply(): Unit } +class Test { + val initCode = List[Proc]() + initCode foreach { proc => proc() } + +} diff --git a/test/files/pos/sammy_implicit.scala b/test/files/pos/sammy_implicit.scala new file mode 100644 index 0000000000..c9c2519bab --- /dev/null +++ b/test/files/pos/sammy_implicit.scala @@ -0,0 +1,10 @@ +abstract class SamImplicitConvert { + trait Fun[A, B] { def apply(a: A): B } + class Lst[T] + abstract class Str { def getBytes: Array[Int] } + def flatMap[B](f: Fun[Str, Lst[B]]): List[B] = ??? + + implicit def conv(xs: Array[Int]): Lst[Int] + + val encoded = flatMap (_.getBytes) +} diff --git a/test/files/pos/t8429.scala b/test/files/pos/t8429.scala new file mode 100644 index 0000000000..a2d32637e1 --- /dev/null +++ b/test/files/pos/t8429.scala @@ -0,0 +1,7 @@ +trait Must { def musta(str: String, i: Int): Unit } + +object Mustare { + def takesM(m: Must) = ??? + takesM{ (a, b) => println } // ok + takesM{ case (a: String, b: Int) => println("") } // should also be accepted +} diff --git a/test/files/run/sam_return.scala b/test/files/run/sam_return.scala new file mode 100644 index 0000000000..e959619dd1 --- /dev/null +++ b/test/files/run/sam_return.scala @@ -0,0 +1,14 @@ +trait Fun[A, B] { def apply(a: A): B } +class PF[A, B] { def runWith[U](action: Fun[B, U]): Fun[A, Boolean] = a => {action(a.asInstanceOf[B]); true} } + +class TO[A](x: A) { + def foreach[U](f: Fun[A, U]): U = f(x) + def collectFirst[B](pf: PF[A, B]): Option[B] = { + foreach(pf.runWith(b => return Some(b))) + None + } +} + +object Test extends App { + assert(new TO("a").collectFirst(new PF[String, String]).get == "a") +}
\ No newline at end of file |