diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-02-09 17:24:01 -0800 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-02-09 17:24:01 -0800 |
commit | 9038fe66959446787f92dc4e5dfaf9127bdab8c5 (patch) | |
tree | c52d7e5e68581c2610684b39f858b2ca78968b20 | |
parent | 01d53a0aea8a62b93f9b6dc04d0565d1a59419ce (diff) | |
parent | 38162b564b8e0b9b500fdc0443cb9e1a423a3ba3 (diff) | |
download | scala-9038fe66959446787f92dc4e5dfaf9127bdab8c5.tar.gz scala-9038fe66959446787f92dc4e5dfaf9127bdab8c5.tar.bz2 scala-9038fe66959446787f92dc4e5dfaf9127bdab8c5.zip |
Merge pull request #3471 from adriaanm/t6169
SI-6169 Refine java wildcard bounds using corresponding tparam
-rw-r--r-- | src/reflect/scala/reflect/internal/Types.scala | 51 | ||||
-rw-r--r-- | test/files/pos/t6169/Exist.java | 4 | ||||
-rw-r--r-- | test/files/pos/t6169/ExistF.java | 4 | ||||
-rw-r--r-- | test/files/pos/t6169/ExistIndir.java | 4 | ||||
-rw-r--r-- | test/files/pos/t6169/OP.java | 1 | ||||
-rw-r--r-- | test/files/pos/t6169/Skin.java | 1 | ||||
-rw-r--r-- | test/files/pos/t6169/Skinnable.java | 3 | ||||
-rw-r--r-- | test/files/pos/t6169/skinnable.scala | 14 | ||||
-rw-r--r-- | test/files/pos/t6169/t6169.scala | 7 |
9 files changed, 87 insertions, 2 deletions
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 2d4e0b3b34..4fbac235f4 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 @@ -2683,8 +2683,55 @@ 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 = { + /* Check that we're looking at rawToExistential's handiwork + * (`existentialAbstraction(eparams, typeRef(apply(pre), sym, eparams map (_.tpe)))`). + * We can't do this sharpening there because we'll run into cycles. + */ + def rawToExistentialCreatedMe = (quantified corresponds underlying.typeArgs){ (q, a) => q.tpe =:= a } + + if (underlying.typeSymbol.isJavaDefined && rawToExistentialCreatedMe) { + val tpars = underlying.typeSymbol.initialize.typeParams // TODO: is initialize needed? + 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) => diff --git a/test/files/pos/t6169/Exist.java b/test/files/pos/t6169/Exist.java new file mode 100644 index 0000000000..dfc6b36b33 --- /dev/null +++ b/test/files/pos/t6169/Exist.java @@ -0,0 +1,4 @@ +public class Exist<T extends String> { + // java helpfully re-interprets Exist<?> as Exist<? extends String> + public Exist<?> foo() { throw new RuntimeException(); } +}
\ No newline at end of file diff --git a/test/files/pos/t6169/ExistF.java b/test/files/pos/t6169/ExistF.java new file mode 100644 index 0000000000..70fabd74cf --- /dev/null +++ b/test/files/pos/t6169/ExistF.java @@ -0,0 +1,4 @@ +public class ExistF<T extends ExistF<T>> { + // java helpfully re-interprets ExistF<?> as ExistF<?0 extends ExistF<?0>> + public ExistF<?> foo() { throw new RuntimeException(); } +}
\ No newline at end of file diff --git a/test/files/pos/t6169/ExistIndir.java b/test/files/pos/t6169/ExistIndir.java new file mode 100644 index 0000000000..e66d1698c4 --- /dev/null +++ b/test/files/pos/t6169/ExistIndir.java @@ -0,0 +1,4 @@ +public class ExistIndir<T extends String, U extends T> { + // java helpfully re-interprets ExistIndir<?> as ExistIndir<? extends String> + public ExistIndir<?, ?> foo() { throw new RuntimeException(); } +} diff --git a/test/files/pos/t6169/OP.java b/test/files/pos/t6169/OP.java new file mode 100644 index 0000000000..15e4c5640f --- /dev/null +++ b/test/files/pos/t6169/OP.java @@ -0,0 +1 @@ +public abstract class OP<T> { } diff --git a/test/files/pos/t6169/Skin.java b/test/files/pos/t6169/Skin.java new file mode 100644 index 0000000000..780de1ee09 --- /dev/null +++ b/test/files/pos/t6169/Skin.java @@ -0,0 +1 @@ +public interface Skin<C extends Skinnable> { } diff --git a/test/files/pos/t6169/Skinnable.java b/test/files/pos/t6169/Skinnable.java new file mode 100644 index 0000000000..f91eaa30d8 --- /dev/null +++ b/test/files/pos/t6169/Skinnable.java @@ -0,0 +1,3 @@ +public interface Skinnable { + OP<Skin<?>> skinProperty(); +} diff --git a/test/files/pos/t6169/skinnable.scala b/test/files/pos/t6169/skinnable.scala new file mode 100644 index 0000000000..3ba2734526 --- /dev/null +++ b/test/files/pos/t6169/skinnable.scala @@ -0,0 +1,14 @@ +object ObjectProperty { + implicit def jfxObjectProperty2sfx[T](p: OP[T]) = new ObjectProperty[T](p) +} + +class ObjectProperty[T](val delegate: OP[T]) + +trait TestWildcardBoundInference { + def delegate: Skinnable + def skin: ObjectProperty[Skin[_ /* inferred: <: Skinnable */]] = ObjectProperty.jfxObjectProperty2sfx(delegate.skinProperty) + skin: ObjectProperty[Skin[_ <: Skinnable]] + + def skinCheckInference = delegate.skinProperty + skinCheckInference: ObjectProperty[Skin[_ <: Skinnable]] +}
\ No newline at end of file diff --git a/test/files/pos/t6169/t6169.scala b/test/files/pos/t6169/t6169.scala new file mode 100644 index 0000000000..37f42619ca --- /dev/null +++ b/test/files/pos/t6169/t6169.scala @@ -0,0 +1,7 @@ +class Test { + class MyExist extends ExistF[MyExist] + // SI-8197, SI-6169: java infers the bounds of existentials, so we have to as well now that SI-1786 is fixed... + def stringy: Exist[_ <: String] = (new Exist[String]).foo + def fbounded: (ExistF[t] forSome {type t <: ExistF[t] }) = (new MyExist).foo + def indir: ExistIndir[_ <: String, _ <: String] = (new ExistIndir[String, String]).foo +}
\ No newline at end of file |