Skip to content

Commit 76d519c

Browse files
committed
Refactor handling of singletons
Don't base it on a subtype comparison since this is wrong for union types. This leaves the upper bound syntax as something to fix.
1 parent d07ee15 commit 76d519c

File tree

7 files changed

+39
-24
lines changed

7 files changed

+39
-24
lines changed

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

+7-11
Original file line numberDiff line numberDiff line change
@@ -647,9 +647,9 @@ trait ConstraintHandling {
647647
* At this point we also drop the @Repeated annotation to avoid inferring type arguments with it,
648648
* as those could leak the annotation to users (see run/inferred-repeated-result).
649649
*/
650-
def widenInferred(inst: Type, bound: Type, widenUnions: Boolean)(using Context): Type =
650+
def widenInferred(inst: Type, bound: Type, widen: Widen)(using Context): Type =
651651
def widenOr(tp: Type) =
652-
if widenUnions then
652+
if widen == Widen.Unions then
653653
val tpw = tp.widenUnion
654654
if tpw ne tp then
655655
if tpw.isTransparent() then
@@ -667,14 +667,10 @@ trait ConstraintHandling {
667667
val tpw = tp.widenSingletons(skipSoftUnions)
668668
if (tpw ne tp) && (tpw <:< bound) then tpw else tp
669669

670-
def isSingleton(tp: Type): Boolean = tp match
671-
case WildcardType(optBounds) => optBounds.exists && isSingleton(optBounds.bounds.hi)
672-
case _ => isSubTypeWhenFrozen(tp, defn.SingletonType)
673-
674670
val wideInst =
675-
if isSingleton(bound) then inst
671+
if widen == Widen.None || bound.isSingletonBounded then inst
676672
else
677-
val widenedFromSingle = widenSingle(inst, skipSoftUnions = widenUnions)
673+
val widenedFromSingle = widenSingle(inst, skipSoftUnions = widen == Widen.Unions)
678674
val widenedFromUnion = widenOr(widenedFromSingle)
679675
val widened = dropTransparentTraits(widenedFromUnion, bound)
680676
widenIrreducible(widened)
@@ -711,18 +707,18 @@ trait ConstraintHandling {
711707
* The instance type is not allowed to contain references to types nested deeper
712708
* than `maxLevel`.
713709
*/
714-
def instanceType(param: TypeParamRef, fromBelow: Boolean, widenUnions: Boolean, maxLevel: Int)(using Context): Type = {
710+
def instanceType(param: TypeParamRef, fromBelow: Boolean, widen: Widen, maxLevel: Int)(using Context): Type = {
715711
val approx = approximation(param, fromBelow, maxLevel).simplified
716712
if fromBelow then
717-
val widened = widenInferred(approx, param, widenUnions)
713+
val widened = widenInferred(approx, param, widen)
718714
// Widening can add extra constraints, in particular the widened type might
719715
// be a type variable which is now instantiated to `param`, and therefore
720716
// cannot be used as an instantiation of `param` without creating a loop.
721717
// If that happens, we run `instanceType` again to find a new instantiation.
722718
// (we do not check for non-toplevel occurrences: those should never occur
723719
// since `addOneBound` disallows recursive lower bounds).
724720
if constraint.occursAtToplevel(param, widened) then
725-
instanceType(param, fromBelow, widenUnions, maxLevel)
721+
instanceType(param, fromBelow, widen, maxLevel)
726722
else
727723
widened
728724
else

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -3253,8 +3253,8 @@ object TypeComparer {
32533253
def subtypeCheckInProgress(using Context): Boolean =
32543254
comparing(_.subtypeCheckInProgress)
32553255

3256-
def instanceType(param: TypeParamRef, fromBelow: Boolean, widenUnions: Boolean, maxLevel: Int = Int.MaxValue)(using Context): Type =
3257-
comparing(_.instanceType(param, fromBelow, widenUnions, maxLevel))
3256+
def instanceType(param: TypeParamRef, fromBelow: Boolean, widen: Widen, maxLevel: Int = Int.MaxValue)(using Context): Type =
3257+
comparing(_.instanceType(param, fromBelow, widen: Widen, maxLevel))
32583258

32593259
def approximation(param: TypeParamRef, fromBelow: Boolean, maxLevel: Int = Int.MaxValue)(using Context): Type =
32603260
comparing(_.approximation(param, fromBelow, maxLevel))
@@ -3274,8 +3274,8 @@ object TypeComparer {
32743274
def addToConstraint(tl: TypeLambda, tvars: List[TypeVar])(using Context): Boolean =
32753275
comparing(_.addToConstraint(tl, tvars))
32763276

3277-
def widenInferred(inst: Type, bound: Type, widenUnions: Boolean)(using Context): Type =
3278-
comparing(_.widenInferred(inst, bound, widenUnions))
3277+
def widenInferred(inst: Type, bound: Type, widen: Widen)(using Context): Type =
3278+
comparing(_.widenInferred(inst, bound, widen: Widen))
32793279

32803280
def dropTransparentTraits(tp: Type, bound: Type)(using Context): Type =
32813281
comparing(_.dropTransparentTraits(tp, bound))

compiler/src/dotty/tools/dotc/core/TypeOps.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ object TypeOps:
538538
val lo = TypeComparer.instanceType(
539539
tp.origin,
540540
fromBelow = variance > 0 || variance == 0 && tp.hasLowerBound,
541-
widenUnions = tp.widenUnions)(using mapCtx)
541+
tp.widenPolicy)(using mapCtx)
542542
val lo1 = apply(lo)
543543
if (lo1 ne lo) lo1 else tp
544544
case _ =>

compiler/src/dotty/tools/dotc/core/Types.scala

+23-4
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,17 @@ object Types extends TypeUtils {
328328
/** Is this type a (possibly aliased) singleton type? */
329329
def isSingleton(using Context): Boolean = dealias.isInstanceOf[SingletonType]
330330

331+
/** Is this upper-bounded by a (possibly aliased) singleton type?
332+
* Overridden in TypeVar
333+
*/
334+
def isSingletonBounded(using Context): Boolean = this match
335+
case _: SingletonType => true
336+
case tp: TypeRef => tp.name == tpnme.Singleton && tp.symbol == defn.SingletonClass
337+
case tp: TypeProxy => tp.superType.isSingletonBounded
338+
case AndType(tpL, tpR) => tpL.isSingletonBounded || tpR.isSingletonBounded
339+
case OrType(tpL, tpR) => tpL.isSingletonBounded && tpR.isSingletonBounded
340+
case _ => false
341+
331342
/** Is this type of kind `AnyKind`? */
332343
def hasAnyKind(using Context): Boolean = {
333344
@tailrec def loop(tp: Type): Boolean = tp match {
@@ -4934,8 +4945,14 @@ object Types extends TypeUtils {
49344945
tp
49354946
}
49364947

4948+
/** Widen unions when instantiating this variable in the current context? */
4949+
def widenPolicy(using Context): Widen =
4950+
if underlying.isSingletonBounded then Widen.None
4951+
else if ctx.typerState.constraint.isHard(this) then Widen.Singletons
4952+
else Widen.Unions
4953+
49374954
def typeToInstantiateWith(fromBelow: Boolean)(using Context): Type =
4938-
TypeComparer.instanceType(origin, fromBelow, widenUnions, nestingLevel)
4955+
TypeComparer.instanceType(origin, fromBelow, widenPolicy, nestingLevel)
49394956

49404957
/** Instantiate variable from the constraints over its `origin`.
49414958
* If `fromBelow` is true, the variable is instantiated to the lub
@@ -4951,9 +4968,6 @@ object Types extends TypeUtils {
49514968
else
49524969
instantiateWith(tp)
49534970

4954-
/** Widen unions when instantiating this variable in the current context? */
4955-
def widenUnions(using Context): Boolean = !ctx.typerState.constraint.isHard(this)
4956-
49574971
/** For uninstantiated type variables: the entry in the constraint (either bounds or
49584972
* provisional instance value)
49594973
*/
@@ -4996,6 +5010,11 @@ object Types extends TypeUtils {
49965010
def apply(using Context)(initOrigin: TypeParamRef, creatorState: TyperState | Null, nestingLevel: Int = ctx.nestingLevel) =
49975011
new TypeVar(initOrigin, creatorState, nestingLevel)
49985012

5013+
enum Widen:
5014+
case None // no widening
5015+
case Singletons // widen singletons but not unions
5016+
case Unions // widen singletons and unions
5017+
49995018
type TypeVars = SimpleIdentitySet[TypeVar]
50005019

50015020
// ------ MatchType ---------------------------------------------------------------

compiler/src/dotty/tools/dotc/typer/Namer.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2073,7 +2073,7 @@ class Namer { typer: Typer =>
20732073
if defaultTp.exists then TypeOps.SimplifyKeepUnchecked() else null)
20742074
match
20752075
case ctp: ConstantType if sym.isInlineVal => ctp
2076-
case tp => TypeComparer.widenInferred(tp, pt, widenUnions = true)
2076+
case tp => TypeComparer.widenInferred(tp, pt, Widen.Unions)
20772077

20782078
// Replace aliases to Unit by Unit itself. If we leave the alias in
20792079
// it would be erased to BoxedUnit.

compiler/src/dotty/tools/dotc/typer/Synthesizer.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
533533
val tparams = poly.paramRefs
534534
val variances = childClass.typeParams.map(_.paramVarianceSign)
535535
val instanceTypes = tparams.lazyZip(variances).map((tparam, variance) =>
536-
TypeComparer.instanceType(tparam, fromBelow = variance < 0, widenUnions = true)
536+
TypeComparer.instanceType(tparam, fromBelow = variance < 0, Widen.Unions)
537537
)
538538
val instanceType = resType.substParams(poly, instanceTypes)
539539
// this is broken in tests/run/i13332intersection.scala,

compiler/src/dotty/tools/dotc/typer/Typer.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -3239,8 +3239,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
32393239
val app1 = typed(app, if ctx.mode.is(Mode.Pattern) then pt else defn.TupleXXLClass.typeRef)
32403240
if (ctx.mode.is(Mode.Pattern)) app1
32413241
else {
3242-
val elemTpes = elems.lazyZip(pts).map((elem, pt) =>
3243-
TypeComparer.widenInferred(elem.tpe, pt, widenUnions = true))
3242+
val elemTpes = elems.lazyZip(pts).map: (elem, pt) =>
3243+
TypeComparer.widenInferred(elem.tpe, pt, Widen.Unions)
32443244
val resTpe = TypeOps.nestedPairs(elemTpes)
32453245
app1.cast(resTpe)
32463246
}

0 commit comments

Comments
 (0)