From 047a449a5102004c37ebd01d543d60aee9d7aa18 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 25 Jun 2019 17:50:47 +0200 Subject: [PATCH 1/2] Avoid infinite recursion when comparing Nothing and union types --- .../src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- tests/pos/singlesubtypes.scala | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/pos/singlesubtypes.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index a61bc0cf2a9a..c4bbf8b18b65 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -601,7 +601,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w case OrType(tp21, tp22) => if (tp2.atoms.nonEmpty && canCompare(tp2.atoms)) return tp1.atoms.nonEmpty && tp1.atoms.subsetOf(tp2.atoms) || - tp1.isRef(NothingClass) + tp1.widen.isRef(NothingClass) // The next clause handles a situation like the one encountered in i2745.scala. // We have: diff --git a/tests/pos/singlesubtypes.scala b/tests/pos/singlesubtypes.scala new file mode 100644 index 000000000000..dab41ebb3fdc --- /dev/null +++ b/tests/pos/singlesubtypes.scala @@ -0,0 +1,12 @@ +object E { + val a: String = ??? + val b: String = ??? +} + +object Test { + + val a: E.a.type = E.a + val b: E.b.type = E.b + + val c: a.type | b.type = ??? +} From f94fdda5217fecb968403632790fd8c34eea5c63 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 25 Jun 2019 22:17:57 +0200 Subject: [PATCH 2/2] Fix atoms computation It's possible that a singleton type has a disjunction of singleton types as underlying type. In this case `atoms` of the first type should return the disjunction, not the singleton type itself. --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 3 +-- compiler/src/dotty/tools/dotc/core/Types.scala | 7 +++++-- tests/pos/singlesubtypes.scala | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index c4bbf8b18b65..fd000257c75c 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -600,8 +600,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w compareTypeLambda case OrType(tp21, tp22) => if (tp2.atoms.nonEmpty && canCompare(tp2.atoms)) - return tp1.atoms.nonEmpty && tp1.atoms.subsetOf(tp2.atoms) || - tp1.widen.isRef(NothingClass) + return tp1.atoms.nonEmpty && tp1.atoms.subsetOf(tp2.atoms) || isSubType(tp1, NothingType) // The next clause handles a situation like the one encountered in i2745.scala. // We have: diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 36f2d07dbc8c..bea3f53df929 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1102,8 +1102,11 @@ object Types { } case _ => tp } - Set.empty + normalize(tp) - case tp: OrType => tp.atoms + val underlyingAtoms = tp.underlying.atoms + if (underlyingAtoms.isEmpty) Set.empty + normalize(tp) + else underlyingAtoms + 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 _ => Set.empty } diff --git a/tests/pos/singlesubtypes.scala b/tests/pos/singlesubtypes.scala index dab41ebb3fdc..bf4273669a02 100644 --- a/tests/pos/singlesubtypes.scala +++ b/tests/pos/singlesubtypes.scala @@ -9,4 +9,5 @@ object Test { val b: E.b.type = E.b val c: a.type | b.type = ??? + val d: a.type | b.type = c }