diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-11-06 15:21:19 +0100 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-11-07 12:00:48 +0100 |
commit | a34be74e8f2630f1862a34e538cbe1162d279e5e (patch) | |
tree | a404c5fb955bf1fd1f192895cee32adb805544df /src/compiler/scala/tools | |
parent | 9c36a76171d497fe277030d2f682f927ae2657f4 (diff) | |
download | scala-a34be74e8f2630f1862a34e538cbe1162d279e5e.tar.gz scala-a34be74e8f2630f1862a34e538cbe1162d279e5e.tar.bz2 scala-a34be74e8f2630f1862a34e538cbe1162d279e5e.zip |
[sammy] use correct type for method to override
Don't naively derive types for the single method's signature
from the provided function's type, as it may be a subtype
of the method's MethodType.
Instead, once the sam class type is fully defined, determine
the sam's info as seen from the class's type, and use those
to generate the correct override.
```
scala> Arrays.stream(Array(1, 2, 3)).map(n => 2 * n + 1).average.ifPresent(println)
5.0
scala> IntStream.range(1, 4).forEach(println)
1
2
3
```
Also, minimal error reporting
Can't figure out how to do it properly, but some reporting
is better than crashing. Right? Test case that illustrates
necessity of the clumsy stop gap `if (block exists (_.isErroneous))`
enclosed as `sammy_error_exist_no_crash`
added TODO for repeated and by-name params
Diffstat (limited to 'src/compiler/scala/tools')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 51 |
1 files changed, 48 insertions, 3 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index c41b81aaf5..5eee60031e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2700,7 +2700,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * `{ * def apply$body(p1: T1, ..., pN: TN): T = body * new S { - * def apply(p1: T1, ..., pN: TN): T = apply$body(p1,..., pN) + * def apply(p1: T1', ..., pN: TN'): T' = apply$body(p1,..., pN) * } * }` * @@ -2710,6 +2710,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * * The `apply` method is identified by the argument `sam`; `S` corresponds to the argument `samClassTp`, * and `resPt` is derived from `samClassTp` -- it may be fully defined, or not... + * If it is not fully defined, we derive `samClassTpFullyDefined` by inferring any unknown type parameters. + * + * The types T1' ... TN' and T' are derived from the method signature of the sam method, + * as seen from the fully defined `samClassTpFullyDefined`. * * The function's body is put in a method outside of the class definition to enforce scoping. * S's members should not be in scope in `body`. @@ -2721,6 +2725,35 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * However T must be fully defined before we type the instantiation, as it'll end up as a parent type, * which must be fully defined. Would be nice to have some kind of mechanism to insert type vars in a block of code, * and have the instantiation of the first occurrence propagate to the rest of the block. + * + * TODO: repeated and by-name params + * scala> trait LazySink { def accept(a: => Any): Unit } + * defined trait LazySink + * + * scala> val f: LazySink = (a) => (a, a) + * f: LazySink = $anonfun$1@1fb26910 + * + * scala> f(println("!")) + * <console>:10: error: LazySink does not take parameters + * f(println("!")) + * ^ + * + * scala> f.accept(println("!")) + * ! + * ! + * This looks like a bug: + * + * scala> trait RepeatedSink { def accept(a: Any*): Unit } + * defined trait RepeatedSink + * + * scala> val f: RepeatedSink = (a) => println(a) + * f: RepeatedSink = $anonfun$1@4799abc2 + * + * scala> f.accept(1) + * WrappedArray(WrappedArray(1)) + * + * scala> f.accept(1, 2) + * WrappedArray(WrappedArray(1, 2)) */ def synthesizeSAMFunction(sam: Symbol, fun: Function, resPt: Type, samClassTp: Type, mode: Mode): Tree = { // assert(fun.vparams forall (vp => isFullyDefined(vp.tpt.tpe))) -- by construction, as we take them from sam's info @@ -2801,13 +2834,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper samClassTp } - // `final override def ${sam.name}($p1: $T1, ..., $pN: $TN): $resPt = ${sam.name}\$body'($p1, ..., $pN)` + // what's the signature of the method that we should actually be overriding? + val samMethTp = samClassTpFullyDefined memberInfo sam + // Before the mutation, `tp <:< vpar.tpt.tpe` should hold. + // TODO: error message when this is not the case, as the expansion won't type check + // - Ti' <:< Ti and T <: T' must hold for the samDef body to type check + val funArgTps = foreach2(samMethTp.paramTypes, fun.vparams)((tp, vpar) => vpar.tpt setType tp) + + // `final override def ${sam.name}($p1: $T1', ..., $pN: $TN'): ${samMethTp.finalResultType} = ${sam.name}\$body'($p1, ..., $pN)` val samDef = DefDef(Modifiers(FINAL | OVERRIDE | SYNTHETIC), sam.name.toTermName, Nil, List(fun.vparams), - TypeTree(samBodyDef.tpt.tpe) setPos sampos.focus, + TypeTree(samMethTp.finalResultType) setPos sampos.focus, Apply(Ident(bodyName), fun.vparams map (p => Ident(p.name))) ) @@ -2838,6 +2878,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ) } + // TODO: improve error reporting -- when we're in silent mode (from `silent(_.doTypedApply(tree, fun, args, mode, pt)) orElse onError`) + // the errors in the function don't get out... + if (block exists (_.isErroneous)) + context.error(fun.pos, s"Could not derive subclass of $samClassTp\n (with SAM `def $sam$samMethTp`)\n based on: $fun.") + classDef.symbol addAnnotation SerialVersionUIDAnnotation block } |