aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/dotty/tools/dotc/core')
-rw-r--r--src/dotty/tools/dotc/core/ConstraintHandling.scala24
-rw-r--r--src/dotty/tools/dotc/core/TypeComparer.scala43
-rw-r--r--src/dotty/tools/dotc/core/TypeErasure.scala2
-rw-r--r--src/dotty/tools/dotc/core/Types.scala23
-rw-r--r--src/dotty/tools/dotc/core/tasty/NameBuffer.scala18
5 files changed, 74 insertions, 36 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/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala
index b495f00d0..1980fe50d 100644
--- a/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -965,28 +965,29 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
* Test that the resulting bounds are still satisfiable.
*/
private def narrowGADTBounds(tr: NamedType, bound: Type, isUpper: Boolean): Boolean =
- ctx.mode.is(Mode.GADTflexible) && {
- val tparam = tr.symbol
- typr.println(i"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.isRef(tparam)}")
- if (bound.isRef(tparam)) false
- else bound match {
- case bound: TypeRef
- if bound.symbol.is(BindDefinedType) && ctx.gadt.bounds.contains(bound.symbol) &&
- !tr.symbol.is(BindDefinedType) =>
- // Avoid having pattern-bound types in gadt bounds,
- // as these might be eliminated once the pattern is typechecked.
- // Pattern-bound type symbols should be narrowed first, only if that fails
- // should symbols in the environment be constrained.
- narrowGADTBounds(bound, tr, !isUpper)
- case _ =>
- val oldBounds = ctx.gadt.bounds(tparam)
- val newBounds =
- if (isUpper) TypeBounds(oldBounds.lo, oldBounds.hi & bound)
- else TypeBounds(oldBounds.lo | bound, oldBounds.hi)
- isSubType(newBounds.lo, newBounds.hi) &&
- { ctx.gadt.setBounds(tparam, newBounds); true }
+ ctx.mode.is(Mode.GADTflexible) && !frozenConstraint && {
+ val tparam = tr.symbol
+ typr.println(i"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.isRef(tparam)}")
+ if (bound.isRef(tparam)) false
+ else bound match {
+ case bound: TypeRef
+ if bound.symbol.is(BindDefinedType) &&
+ ctx.gadt.bounds.contains(bound.symbol) &&
+ !tr.symbol.is(BindDefinedType) =>
+ // Avoid having pattern-bound types in gadt bounds,
+ // as these might be eliminated once the pattern is typechecked.
+ // Pattern-bound type symbols should be narrowed first, only if that fails
+ // should symbols in the environment be constrained.
+ narrowGADTBounds(bound, tr, !isUpper)
+ case _ =>
+ val oldBounds = ctx.gadt.bounds(tparam)
+ val newBounds =
+ if (isUpper) TypeBounds(oldBounds.lo, oldBounds.hi & bound)
+ else TypeBounds(oldBounds.lo | bound, oldBounds.hi)
+ isSubType(newBounds.lo, newBounds.hi) &&
+ { ctx.gadt.setBounds(tparam, newBounds); true }
+ }
}
- }
// Tests around `matches`
diff --git a/src/dotty/tools/dotc/core/TypeErasure.scala b/src/dotty/tools/dotc/core/TypeErasure.scala
index fd5fcb921..254ea3277 100644
--- a/src/dotty/tools/dotc/core/TypeErasure.scala
+++ b/src/dotty/tools/dotc/core/TypeErasure.scala
@@ -417,7 +417,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
// See doc comment for ElimByName for speculation how we could improve this.
else MethodType(Nil, Nil, eraseResult(rt))
case tp: PolyType =>
- this(tp.resultType) match {
+ eraseResult(tp.resultType) match {
case rt: MethodType => rt
case rt => MethodType(Nil, Nil, rt)
}
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index d242843e5..38913a7d0 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -239,9 +239,14 @@ object Types {
/** The parts of this type which are type or term refs and which
* satisfy predicate `p`.
+ *
+ * @param p The predicate to satisfy
+ * @param excludeLowerBounds If set to true, the lower bounds of abstract
+ * types will be ignored.
*/
- def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] =
- new NamedPartsAccumulator(p).apply(mutable.LinkedHashSet(), this)
+ def namedPartsWith(p: NamedType => Boolean, excludeLowerBounds: Boolean = false)
+ (implicit ctx: Context): collection.Set[NamedType] =
+ new NamedPartsAccumulator(p, excludeLowerBounds).apply(mutable.LinkedHashSet(), this)
/** Map function `f` over elements of an AndType, rebuilding with function `g` */
def mapReduceAnd[T](f: Type => T)(g: (T, T) => T)(implicit ctx: Context): T = stripTypeVar match {
@@ -615,13 +620,13 @@ object Types {
/** The set of abstract term members of this type. */
final def abstractTermMembers(implicit ctx: Context): Seq[SingleDenotation] = track("abstractTermMembers") {
memberDenots(abstractTermNameFilter,
- (name, buf) => buf ++= member(name).altsWith(_ is Deferred))
+ (name, buf) => buf ++= nonPrivateMember(name).altsWith(_ is Deferred))
}
/** The set of abstract type members of this type. */
final def abstractTypeMembers(implicit ctx: Context): Seq[SingleDenotation] = track("abstractTypeMembers") {
memberDenots(abstractTypeNameFilter,
- (name, buf) => buf += member(name).asSingleDenotation)
+ (name, buf) => buf += nonPrivateMember(name).asSingleDenotation)
}
/** The set of abstract type members of this type. */
@@ -3710,7 +3715,8 @@ object Types {
def apply(x: Boolean, tp: Type) = x || tp.isUnsafeNonvariant || foldOver(x, tp)
}
- class NamedPartsAccumulator(p: NamedType => Boolean)(implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] {
+ class NamedPartsAccumulator(p: NamedType => Boolean, excludeLowerBounds: Boolean = false)
+ (implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] {
override def stopAtStatic = false
def maybeAdd(x: mutable.Set[NamedType], tp: NamedType) = if (p(tp)) x += tp else x
val seen: mutable.Set[Type] = mutable.Set()
@@ -3723,7 +3729,8 @@ object Types {
apply(foldOver(maybeAdd(x, tp), tp), tp.underlying)
case tp: TypeRef =>
foldOver(maybeAdd(x, tp), tp)
- case TypeBounds(_, hi) =>
+ case TypeBounds(lo, hi) =>
+ if (!excludeLowerBounds) apply(x, lo)
apply(x, hi)
case tp: ThisType =>
apply(x, tp.tref)
@@ -3756,7 +3763,7 @@ object Types {
object abstractTypeNameFilter extends NameFilter {
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =
name.isTypeName && {
- val mbr = pre.member(name)
+ val mbr = pre.nonPrivateMember(name)
(mbr.symbol is Deferred) && mbr.info.isInstanceOf[RealTypeBounds]
}
}
@@ -3773,7 +3780,7 @@ object Types {
/** A filter for names of deferred term definitions of a given type */
object abstractTermNameFilter extends NameFilter {
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =
- name.isTermName && (pre member name).hasAltWith(_.symbol is Deferred)
+ name.isTermName && pre.nonPrivateMember(name).hasAltWith(_.symbol is Deferred)
}
object typeNameFilter extends NameFilter {
diff --git a/src/dotty/tools/dotc/core/tasty/NameBuffer.scala b/src/dotty/tools/dotc/core/tasty/NameBuffer.scala
index 69fd63805..3ff7298ce 100644
--- a/src/dotty/tools/dotc/core/tasty/NameBuffer.scala
+++ b/src/dotty/tools/dotc/core/tasty/NameBuffer.scala
@@ -12,6 +12,7 @@ import TastyName._
import TastyFormat._
class NameBuffer extends TastyBuffer(10000) {
+ import NameBuffer._
private val nameRefs = new mutable.LinkedHashMap[TastyName, NameRef]
@@ -40,13 +41,12 @@ class NameBuffer extends TastyBuffer(10000) {
nameIndex(name)
}
- private def withLength(op: => Unit): Unit = {
+ private def withLength(op: => Unit, lengthWidth: Int = 1): Unit = {
val lengthAddr = currentAddr
- writeByte(0)
+ for (i <- 0 until lengthWidth) writeByte(0)
op
val length = currentAddr.index - lengthAddr.index - 1
- assert(length < 128)
- putNat(lengthAddr, length, 1)
+ putNat(lengthAddr, length, lengthWidth)
}
def writeNameRef(ref: NameRef) = writeNat(ref.index)
@@ -64,7 +64,9 @@ class NameBuffer extends TastyBuffer(10000) {
withLength { writeNameRef(qualified); writeNameRef(selector) }
case Signed(original, params, result) =>
writeByte(SIGNED)
- withLength { writeNameRef(original); writeNameRef(result); params.foreach(writeNameRef) }
+ withLength(
+ { writeNameRef(original); writeNameRef(result); params.foreach(writeNameRef) },
+ if ((params.length + 2) * maxIndexWidth <= maxNumInByte) 1 else 2)
case Expanded(prefix, original) =>
writeByte(EXPANDED)
withLength { writeNameRef(prefix); writeNameRef(original) }
@@ -91,3 +93,9 @@ class NameBuffer extends TastyBuffer(10000) {
}
}
}
+
+object NameBuffer {
+ private val maxIndexWidth = 3 // allows name indices up to 2^21.
+ private val payloadBitsPerByte = 7 // determined by nat encoding in TastyBuffer
+ private val maxNumInByte = (1 << payloadBitsPerByte) - 1
+}