aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorodersky <odersky@gmail.com>2016-10-16 13:26:34 +0200
committerGitHub <noreply@github.com>2016-10-16 13:26:34 +0200
commit0fdd4e37ffca9b1da770ba348aece793c200f1ff (patch)
treecab76702a7a8e31618d39cacac017ae144344dc0
parent009398bf72d4ec3e10f4b6f56431927065c3b846 (diff)
parent5d531ec78173b9524acd3b58485f89099dbe6991 (diff)
downloaddotty-0fdd4e37ffca9b1da770ba348aece793c200f1ff.tar.gz
dotty-0fdd4e37ffca9b1da770ba348aece793c200f1ff.tar.bz2
dotty-0fdd4e37ffca9b1da770ba348aece793c200f1ff.zip
Merge pull request #1592 from dotty-staging/fix-#1590
Fix #1590: Eliminate wildcards when approximating a type
-rw-r--r--src/dotty/tools/dotc/core/ConstraintHandling.scala24
-rw-r--r--tests/pos/i1590.scala10
2 files changed, 33 insertions, 1 deletions
diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala
index 3835d553c..0e155b9e1 100644
--- a/src/dotty/tools/dotc/core/ConstraintHandling.scala
+++ b/src/dotty/tools/dotc/core/ConstraintHandling.scala
@@ -162,7 +162,8 @@ trait ConstraintHandling {
/** Solve constraint set for given type parameter `param`.
* If `fromBelow` is true the parameter is approximated by its lower bound,
* otherwise it is approximated by its upper bound. However, any occurrences
- * of the parameter in a refinement somewhere in the bound are removed.
+ * of the parameter in a refinement somewhere in the bound are removed. Also
+ * wildcard types in bounds are approximated by their upper or lower bounds.
* (Such occurrences can arise for F-bounded types).
* The constraint is left unchanged.
* @return the instantiating type
@@ -174,6 +175,27 @@ trait ConstraintHandling {
def apply(tp: Type) = mapOver {
tp match {
case tp: RefinedType if param occursIn tp.refinedInfo => tp.parent
+ case tp: WildcardType =>
+ val bounds = tp.optBounds.orElse(TypeBounds.empty).bounds
+ // Try to instantiate the wildcard to a type that is known to conform to it.
+ // This means:
+ // If fromBelow is true, we minimize the type overall
+ // Hence, if variance < 0, pick the maximal safe type: bounds.lo
+ // (i.e. the whole bounds range is over the type)
+ // if variance > 0, pick the minimal safe type: bounds.hi
+ // (i.e. the whole bounds range is under the type)
+ // if variance == 0, pick bounds.lo anyway (this is arbitrary but in line with
+ // the principle that we pick the smaller type when in doubt).
+ // If fromBelow is false, we maximize the type overall and reverse the bounds
+ // if variance != 0. For variance == 0, we still minimize.
+ // In summary we pick the bound given by this table:
+ //
+ // variance | -1 0 1
+ // ------------------------
+ // from below | lo lo hi
+ // from above | hi lo lo
+ //
+ if (variance == 0 || fromBelow == (variance < 0)) bounds.lo else bounds.hi
case _ => tp
}
}
diff --git a/tests/pos/i1590.scala b/tests/pos/i1590.scala
new file mode 100644
index 000000000..ab922c218
--- /dev/null
+++ b/tests/pos/i1590.scala
@@ -0,0 +1,10 @@
+case class W[T](seq: Option[Option[T]] = Option.empty)
+object W {
+ def apply[T] = new W[T]()
+}
+
+case class V[T](vv: W[W[T]] = W.apply)
+object Test {
+ W[Int]()
+ // V[Int]() fails in scalac and dotty: both instantiate the vv-default to W[Nothing]
+}