diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 1ebf13b0845f..aa550be11c0e 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1217,34 +1217,44 @@ object Types { /** The singleton types that must or may be in this type. @see Atoms. * Overridden and cached in OrType. */ - def atoms(using Context): Atoms = dealias match - case tp: SingletonType => - def normalize(tp: Type): Type = tp match - case tp: SingletonType => - tp.underlying.dealias match - case tp1: SingletonType => normalize(tp1) - case _ => - tp match - case tp: TermRef => tp.derivedSelect(normalize(tp.prefix)) - case _ => tp - case _ => tp - tp.underlying.atoms match - case as @ Atoms.Range(lo, hi) => - if hi.size == 1 then as // if there's just one atom, there's no uncertainty which one it is - else Atoms.Range(Set.empty, hi) - case Atoms.Unknown => - if tp.isStable then - val single = Set.empty + normalize(tp) - Atoms.Range(single, single) - else Atoms.Unknown - case tp: ExprType => tp.resType.atoms - case tp: OrType => tp.atoms // `atoms` overridden in OrType - case tp: AndType => tp.tp1.atoms & tp.tp2.atoms - case tp: TypeProxy => - tp.underlying.atoms match - case Atoms.Range(_, hi) => Atoms.Range(Set.empty, hi) - case Atoms.Unknown => Atoms.Unknown - case _ => Atoms.Unknown + def atoms(using Context): Atoms = + def normalize(tp: Type): Type = tp match + case tp: SingletonType => + tp.underlying.dealias match + case tp1: SingletonType => normalize(tp1) + case _ => + tp match + case tp: TermRef => tp.derivedSelect(normalize(tp.prefix)) + case _ => tp + case _ => tp + + def single(tp: Type): Atoms = + if tp.isStable then + val set = Set.empty + normalize(tp) + Atoms.Range(set, set) + else Atoms.Unknown + + dealias match + case tp: SingletonType => + tp.underlying.atoms match + case as @ Atoms.Range(lo, hi) => + if hi.size == 1 then as // if there's just one atom, there's no uncertainty which one it is + else Atoms.Range(Set.empty, hi) + case Atoms.Unknown => + single(tp) + case tp: ExprType => tp.resType.atoms + case tp: OrType => tp.atoms // `atoms` overridden in OrType + case tp: AndType => tp.tp1.atoms & tp.tp2.atoms + case tp: TypeRef if tp.symbol.is(ModuleClass) => + // The atom of a module class is the module itself, + // this corresponds to the special case in TypeComparer + // which ensures that `X$ <:< X.type` returns true. + single(tp.symbol.companionModule.termRef.asSeenFrom(tp.prefix, tp.symbol.owner)) + case tp: TypeProxy => + tp.underlying.atoms match + case Atoms.Range(_, hi) => Atoms.Range(Set.empty, hi) + case Atoms.Unknown => Atoms.Unknown + case _ => Atoms.Unknown private def dealias1(keep: AnnotatedType => Context ?=> Boolean)(using Context): Type = this match { case tp: TypeRef => diff --git a/tests/patmat/outer-ref-checks.scala b/tests/patmat/outer-ref-checks.scala index 9b2de381ad73..b9b6d563be3a 100644 --- a/tests/patmat/outer-ref-checks.scala +++ b/tests/patmat/outer-ref-checks.scala @@ -16,7 +16,6 @@ class Outer { def belongsOtherOuter(a: Outer#Inner): Unit = a match { case Inner(s) => // unchecked warning - case O.Inner(s) => // unchecked warning case _ => } } diff --git a/tests/pos/object-union-inf.scala b/tests/pos/object-union-inf.scala new file mode 100644 index 000000000000..6e6a2d1f1239 --- /dev/null +++ b/tests/pos/object-union-inf.scala @@ -0,0 +1,19 @@ +class A +object ObjA extends A +class B +object ObjB extends B + +object Test { + def foo[T <: A | B](t: T): List[T] = List(t) + val x: ObjA.type | ObjB.type = ObjA + + // infers `T = ObjA$ | ObjB$` instead of `ObjA.type | ObjB.type` due to the + // use of `widenSingleton` in TypeComparer#secondTry when the lhs is a union + // type. + val y = foo(ObjA : ObjA.type | ObjB.type) + + // This only compiles if `ObjA$ <:< ObjA.type`, there is a special-case in + // `firstTry` for that but we also need a special case in `atoms` since unions + // are involved. + val z: List[ObjA.type | ObjB.type] = y +}