diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 5fbffe6e95ab..4029b06d2502 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -790,9 +790,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { (defn.AnyType /: tps)(glb) /** The least upper bound of two types - * @note We do not admit singleton types in or-types as lubs. + * @param keepSingletons If true, do not widen singletons when forming an OrType */ - def lub(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"lub(${tp1.show}, ${tp2.show})", subtyping, show = true) /*<|<*/ { + def lub(tp1: Type, tp2: Type, keepSingletons: Boolean = false): Type = /*>|>*/ ctx.traceIndented(s"lub(${tp1.show}, ${tp2.show}, $keepSingletons)", subtyping, show = true) /*<|<*/ { if (tp1 eq tp2) tp1 else if (!tp1.exists) tp1 else if (!tp2.exists) tp2 @@ -805,8 +805,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val t2 = mergeIfSuper(tp2, tp1) if (t2.exists) t2 else { - val tp1w = tp1.widen - val tp2w = tp2.widen + val tp1w = if (keepSingletons) tp1.widenExpr else tp1.widen + val tp2w = if (keepSingletons) tp2.widenExpr else tp2.widen if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w) else orType(tp1w, tp2w) // no need to check subtypes again } @@ -816,7 +816,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** The least upper bound of a list of types */ final def lub(tps: List[Type]): Type = - (defn.NothingType /: tps)(lub) + (defn.NothingType /: tps)(lub(_, _)) /** Merge `t1` into `tp2` if t1 is a subtype of some &-summand of tp2. */ @@ -1207,9 +1207,9 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) { super.hasMatchingMember(name, tp1, tp2) } - override def lub(tp1: Type, tp2: Type) = - traceIndented(s"lub(${show(tp1)}, ${show(tp2)})") { - super.lub(tp1, tp2) + override def lub(tp1: Type, tp2: Type, keepSingletons: Boolean = false) = + traceIndented(s"lub(${show(tp1)}, ${show(tp2)}, $keepSingletons)") { + super.lub(tp1, tp2, keepSingletons) } override def glb(tp1: Type, tp2: Type) = diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 77c6805f01a9..61b83fc81465 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -246,7 +246,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. case AndType(l, r) => simplify(l, theMap) & simplify(r, theMap) case OrType(l, r) => - simplify(l, theMap) | simplify(r, theMap) + ctx.typeComparer.lub(simplify(l, theMap), simplify(r, theMap), keepSingletons = true) case _ => (if (theMap != null) theMap else new SimplifyMap).mapOver(tp) } diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 7225ede143ae..2d69f64d724a 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -366,7 +366,7 @@ trait TypeAssigner { tree.withType(left.tpe & right.tpe) def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = - tree.withType(left.tpe | right.tpe) + tree.withType(ctx.typeComparer.lub(left.tpe, right.tpe, keepSingletons = true)) // RefinedTypeTree is missing, handled specially in Typer and Unpickler. diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 337285c04d15..91c615c1d80c 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -160,6 +160,7 @@ class tests extends CompilerTest { @Test def neg_validate = compileFile(negDir, "validate", xerrors = 18) @Test def neg_validateParsing = compileFile(negDir, "validate-parsing", xerrors = 7) @Test def neg_validateRefchecks = compileFile(negDir, "validate-refchecks", xerrors = 2) + @Test def neg_singletonsLubs = compileFile(negDir, "singletons-lubs", xerrors = 2) @Test def run_all = runFiles(runDir) diff --git a/tests/neg/singletons-lubs.scala b/tests/neg/singletons-lubs.scala new file mode 100644 index 000000000000..3a84cad61df9 --- /dev/null +++ b/tests/neg/singletons-lubs.scala @@ -0,0 +1,7 @@ +object Test { + def oneOrTwo(x: 1 | 2): 1 | 2 = x + def test: Unit = { + val foo: 3 | 4 = 1 // error + oneOrTwo(foo) // error + } +} diff --git a/tests/pos/singletons-lubs.scala b/tests/pos/singletons-lubs.scala new file mode 100644 index 000000000000..472ff622bebf --- /dev/null +++ b/tests/pos/singletons-lubs.scala @@ -0,0 +1,11 @@ +object Test { + def oneOrTwo(x: 1 | 2): 1 | 2 = x + def test: Unit = { + val foo: 1 | 2 = 1 + oneOrTwo(oneOrTwo(foo)) + 1 match { + case x: (1 | 2) => oneOrTwo(x) + //case x @ (1 | 2) => oneOrTwo(x) // disallowed to avoid deep subtyping checks + } + } +}