diff options
Diffstat (limited to 'src/dotty/tools/dotc/core')
-rw-r--r-- | src/dotty/tools/dotc/core/ConstraintHandling.scala | 24 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeComparer.scala | 43 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeErasure.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 23 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/tasty/NameBuffer.scala | 18 |
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 +} |