diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-02-04 22:29:52 -0800 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-02-05 12:47:30 -0800 |
commit | 4525e3392b42b47147479087d961d328c3b717bb (patch) | |
tree | 9c9f01862b43567936c0bd6a971aeecfb92a8cb2 /src/reflect | |
parent | eba3cc6a9e4bb091db3cc7d68dc64abb803f52c7 (diff) | |
download | scala-4525e3392b42b47147479087d961d328c3b717bb.tar.gz scala-4525e3392b42b47147479087d961d328c3b717bb.tar.bz2 scala-4525e3392b42b47147479087d961d328c3b717bb.zip |
SI-6169 Refine java wildcard bounds using corresponding tparam
Also fixes part of SI-8197. Necessary complement to SI-1786 (#2518),
because we now infer tighter bounds for RHSs to conform to.
When opening an existential, Java puts constraints in the typing environment
that are derived from the bounds on the type parameters of the existentially
quantified type, so let's do the same for existentials over java-defined
classes in skolemizeExistential...
Example from test case:
```
public class Exist<T extends String> {
// java helpfully re-interprets Exist<?> as Exist<? extends String>
public Exist<?> foo() { throw new RuntimeException(); }
}
```
In Scala syntax, given a java-defined `class C[T <: String]`, the
existential type `C[_]` is improved to `C[_ <: String]` before skolemization,
which models what Java does (track the bounds as type constraints in the typing environment)
(Also tried doing this once during class file parsing or
when creating the existential type, but that causes cyclic errors
because it happens too early.)
Diffstat (limited to 'src/reflect')
-rw-r--r-- | src/reflect/scala/reflect/internal/Types.scala | 45 |
1 files changed, 43 insertions, 2 deletions
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 2acf901d0e..a49ecea57f 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -899,7 +899,7 @@ trait Types -1 } - /** If this is a poly- or methodtype, a copy with cloned type / value parameters + /** If this is a ExistentialType, PolyType or MethodType, a copy with cloned type / value parameters * owned by `owner`. Identity for all other types. */ def cloneInfo(owner: Symbol) = this @@ -2656,8 +2656,49 @@ trait Types override def baseTypeSeq = underlying.baseTypeSeq map maybeRewrap override def isHigherKinded = false - override def skolemizeExistential(owner: Symbol, origin: AnyRef) = + /** [SI-6169, SI-8197 -- companion to SI-1786] + * + * Approximation to improve the bounds of a Java-defined existential type, + * based on the bounds of the type parameters of the quantified type + * In Scala syntax, given a java-defined class C[T <: String], the existential type C[_] + * is improved to C[_ <: String] before skolemization, which captures (get it?) what Java does: + * enter the type paramers' bounds into the context when checking subtyping/type equality of existential types + * + * (Also tried doing this once during class file parsing or when creating the existential type, + * but that causes cyclic errors because it happens too early.) + */ + private def sharpenQuantifierBounds(): Unit = { + if (underlying.typeSymbol.isJavaDefined && quantified == underlying.typeArgs.map(_.typeSymbol)) { + val tpars = underlying.typeSymbol.typeParams + debuglog(s"sharpen bounds: $this | ${underlying.typeArgs.map(_.typeSymbol)} <-- ${tpars.map(_.info)}") + + foreach2(quantified, tpars) { (quant, tparam) => + // TODO: check `tparam.info.substSym(tpars, quantified) <:< quant.info` instead (for some weird reason not working for test/t6169/ExistF) + // for now, crude approximation for the common case + if (quant.info.bounds.isEmptyBounds && !tparam.info.bounds.isEmptyBounds) { + // avoid creating cycles [pos/t2940] that consist of an existential quantifier's + // bounded by an existential type that unhygienically has that quantifier as its own quantifier + // (TODO: clone latter existential with fresh quantifiers -- not covering this case for now) + if ((existentialsInType(tparam.info) intersect quantified).isEmpty) + quant setInfo tparam.info.substSym(tpars, quantified) + } + } + } + + _sharpenQuantifierBounds = false + } + private[this] var _sharpenQuantifierBounds = true + + override def skolemizeExistential(owner: Symbol, origin: AnyRef) = { + // do this here because it's quite close to what Java does: + // when checking subtyping/type equality, enter constraints + // derived from the existentially quantified type into the typing environment + // (aka \Gamma, which tracks types for variables and constraints/kinds for types) + // as a nice bonus, delaying this until we need it avoids cyclic errors + if (_sharpenQuantifierBounds) sharpenQuantifierBounds + deriveType(quantified, tparam => (owner orElse tparam.owner).newExistentialSkolem(tparam, origin))(underlying) + } private def wildcardArgsString(qset: Set[Symbol], args: List[Type]): List[String] = args map { case TypeRef(_, sym, _) if (qset contains sym) => |