diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Infer.scala | 6 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 36 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Definitions.scala | 2 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/tpe/TypeMaps.scala | 16 | ||||
-rw-r--r-- | src/reflect/scala/reflect/runtime/JavaUniverseForce.scala | 1 | ||||
-rw-r--r-- | test/files/pos/sammy_exist.flags | 1 | ||||
-rw-r--r-- | test/files/pos/sammy_exist.scala | 24 | ||||
-rw-r--r-- | test/files/pos/sammy_overload.flags | 1 | ||||
-rw-r--r-- | test/files/pos/sammy_overload.scala | 9 | ||||
-rw-r--r-- | test/files/pos/t8310.flags | 1 | ||||
-rw-r--r-- | test/files/pos/t8310.scala | 22 |
11 files changed, 108 insertions, 11 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index ee2775ee26..8979b26719 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -295,11 +295,17 @@ trait Infer extends Checkable { && !isByNameParamType(tp) && isCompatible(tp, dropByName(pt)) ) + def isCompatibleSam(tp: Type, pt: Type): Boolean = { + val samFun = typer.samToFunctionType(pt) + (samFun ne NoType) && isCompatible(tp, samFun) + } + val tp1 = normalize(tp) ( (tp1 weak_<:< pt) || isCoercible(tp1, pt) || isCompatibleByName(tp, pt) + || isCompatibleSam(tp, pt) ) } def isCompatibleArgs(tps: List[Type], pts: List[Type]) = (tps corresponds pts)(isCompatible) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 70acb03584..c41b81aaf5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -741,6 +741,26 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => } + /** + * Convert a SAM type to the corresponding FunctionType, + * extrapolating BoundedWildcardTypes in the process + * (no type precision is lost by the extrapolation, + * but this facilitates dealing with the types arising from Java's use-site variance). + */ + def samToFunctionType(tp: Type, sam: Symbol = NoSymbol): Type = { + val samSym = sam orElse samOf(tp) + + def correspondingFunctionSymbol = { + val numVparams = samSym.info.params.length + if (numVparams > definitions.MaxFunctionArity) NoSymbol + else FunctionClass(numVparams) + } + + if (samSym.exists && samSym.owner != correspondingFunctionSymbol) // don't treat Functions as SAMs + wildcardExtrapolation(normalize(tp memberInfo samSym)) + else NoType + } + /** Perform the following adaptations of expression, pattern or type `tree` wrt to * given mode `mode` and given prototype `pt`: * (-1) For expressions with annotated types, let AnnotationCheckers decide what to do @@ -824,7 +844,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case Block(_, tree1) => tree1.symbol case _ => tree.symbol } - if (!meth.isConstructor && isFunctionType(pt)) { // (4.2) + if (!meth.isConstructor && (isFunctionType(pt) || samOf(pt).exists)) { // (4.2) debuglog(s"eta-expanding $tree: ${tree.tpe} to $pt") checkParamsConvertible(tree, tree.tpe) val tree0 = etaExpand(context.unit, tree, this) @@ -2838,7 +2858,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * as `(a => a): Int => Int` should not (yet) get the sam treatment. */ val sam = - if (!settings.Xexperimental || pt.typeSymbol == FunctionSymbol) NoSymbol + if (pt.typeSymbol == FunctionSymbol) NoSymbol else samOf(pt) /* The SAM case comes first so that this works: @@ -2848,15 +2868,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * Note that the arity of the sam must correspond to the arity of the function. */ val samViable = sam.exists && sameLength(sam.info.params, fun.vparams) + val ptNorm = if (samViable) samToFunctionType(pt, sam) else pt val (argpts, respt) = - if (samViable) { - val samInfo = pt memberInfo sam - (samInfo.paramTypes, samInfo.resultType) - } else { - pt baseType FunctionSymbol match { - case TypeRef(_, FunctionSymbol, args :+ res) => (args, res) - case _ => (fun.vparams map (_ => if (pt == ErrorType) ErrorType else NoType), WildcardType) - } + ptNorm baseType FunctionSymbol match { + case TypeRef(_, FunctionSymbol, args :+ res) => (args, res) + case _ => (fun.vparams map (_ => if (pt == ErrorType) ErrorType else NoType), WildcardType) } if (!FunctionSymbol.exists) diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index e2ee6a9076..85b080f29b 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -790,7 +790,7 @@ trait Definitions extends api.StandardDefinitions { * The class defining the method is a supertype of `tp` that * has a public no-arg primary constructor. */ - def samOf(tp: Type): Symbol = { + def samOf(tp: Type): Symbol = if (!settings.Xexperimental) NoSymbol else { // if tp has a constructor, it must be public and must not take any arguments // (not even an implicit argument list -- to keep it simple for now) val tpSym = tp.typeSymbol diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index f06420de96..662306d5ab 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -422,6 +422,22 @@ private[internal] trait TypeMaps { } } + /** + * Get rid of BoundedWildcardType where variance allows us to do so. + * Invariant: `wildcardExtrapolation(tp) =:= tp` + * + * For example, the MethodType given by `def bla(x: (_ >: String)): (_ <: Int)` + * is both a subtype and a supertype of `def bla(x: String): Int`. + */ + object wildcardExtrapolation extends TypeMap(trackVariance = true) { + def apply(tp: Type): Type = + tp match { + case BoundedWildcardType(TypeBounds(lo, AnyTpe)) if variance.isContravariant =>lo + case BoundedWildcardType(TypeBounds(NothingTpe, hi)) if variance.isCovariant => hi + case tp => mapOver(tp) + } + } + /** Might the given symbol be important when calculating the prefix * of a type? When tp.asSeenFrom(pre, clazz) is called on `tp`, * the result will be `tp` unchanged if `pre` is trivial and `clazz` diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 18a3c5d63f..c87b810bdd 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -170,6 +170,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.dropSingletonType this.abstractTypesToBounds this.dropIllegalStarTypes + this.wildcardExtrapolation this.IsDependentCollector this.ApproximateDependentMap this.wildcardToTypeVarMap diff --git a/test/files/pos/sammy_exist.flags b/test/files/pos/sammy_exist.flags new file mode 100644 index 0000000000..48fd867160 --- /dev/null +++ b/test/files/pos/sammy_exist.flags @@ -0,0 +1 @@ +-Xexperimental diff --git a/test/files/pos/sammy_exist.scala b/test/files/pos/sammy_exist.scala new file mode 100644 index 0000000000..95c7c7b68e --- /dev/null +++ b/test/files/pos/sammy_exist.scala @@ -0,0 +1,24 @@ +// scala> typeOf[java.util.stream.Stream[_]].nonPrivateMember(TermName("map")).info +// [R](x$1: java.util.function.Function[_ >: T, _ <: R])java.util.stream.Stream[R] + +// java.util.function.Function +trait Fun[A, B] { def apply(x: A): B } + +// java.util.stream.Stream +class S[T](x: T) { def map[R](f: Fun[_ >: T, _ <: R]): R = f(x) } + +class Bla { def foo: Bla = this } + +object T { + val aBlaSAM = (new S(new Bla)).map(_.foo) // : Bla should be inferred, when running under -Xexperimental [TODO] + val fun: Fun[Bla, Bla] = (x: Bla) => x + val aBlaSAMX = (new S(new Bla)).map(fun) // : Bla should be inferred, when running under -Xexperimental [TODO] +} +// +// // or, maybe by variance-cast? +// import annotation.unchecked.{uncheckedVariance => uv} +// type SFun[-A, +B] = Fun[_ >: A, _ <: B @uv] +// +// def jf[T, R](f: T => R): SFun[T, R] = (x: T) => f(x): R +// +// val aBlaSAM = (new S(new Bla)).map(jf(_.foo)) // : Bla should be inferred [type checks, but existential inferred]
\ No newline at end of file diff --git a/test/files/pos/sammy_overload.flags b/test/files/pos/sammy_overload.flags new file mode 100644 index 0000000000..48fd867160 --- /dev/null +++ b/test/files/pos/sammy_overload.flags @@ -0,0 +1 @@ +-Xexperimental diff --git a/test/files/pos/sammy_overload.scala b/test/files/pos/sammy_overload.scala new file mode 100644 index 0000000000..5472248f4d --- /dev/null +++ b/test/files/pos/sammy_overload.scala @@ -0,0 +1,9 @@ +trait Consumer[T] { + def consume(x: T): Unit +} + +object Test { + def foo(x: String): Unit = ??? + def foo(): Unit = ??? + val f: Consumer[_ >: String] = foo +}
\ No newline at end of file diff --git a/test/files/pos/t8310.flags b/test/files/pos/t8310.flags new file mode 100644 index 0000000000..48fd867160 --- /dev/null +++ b/test/files/pos/t8310.flags @@ -0,0 +1 @@ +-Xexperimental diff --git a/test/files/pos/t8310.scala b/test/files/pos/t8310.scala new file mode 100644 index 0000000000..874caf4d3b --- /dev/null +++ b/test/files/pos/t8310.scala @@ -0,0 +1,22 @@ +trait Comparinator[T] { def compare(a: T, b: T): Int } + +object TestOkay { + def sort(x: Comparinator[_ >: String]) = () + sort((a: String, b: String) => a.compareToIgnoreCase(b)) +} + +object TestOkay2 { + def sort[T](x: Comparinator[_ >: T]) = () + sort((a: String, b: String) => a.compareToIgnoreCase(b)) +} + +object TestOkay3 { + def sort[T](xs: Option[T], x: Comparinator[_ >: T]) = () + sort(Some(""), (a: String, b: String) => a.compareToIgnoreCase(b)) +} + +object TestKoOverloaded { + def sort[T](xs: Option[T]) = () + def sort[T](xs: Option[T], x: Comparinator[_ >: T]) = () + sort(Some(""), (a: String, b: String) => a.compareToIgnoreCase(b)) +} |