From 94ecc0523927e41206e7502a821eb0e367e0e63f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Apr 2021 09:47:39 +0200 Subject: [PATCH] Change order of operations in `lub` Try to drop a supertype defore distributing into Or. Fixes #12286 where a type variable was not instantiated to an OrType but was distributed into the constituents instead. --- .../dotty/tools/dotc/core/TypeComparer.scala | 53 +++++++++---------- tests/pos/i12286.scala | 13 +++++ 2 files changed, 38 insertions(+), 28 deletions(-) create mode 100644 tests/pos/i12286.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 801ba332c596..29920718fa1f 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1934,43 +1934,40 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling else if (!tp2.exists) tp1 else if tp1.isAny && !tp2.isLambdaSub || tp1.isAnyKind || isBottom(tp2) then tp2 else if tp2.isAny && !tp1.isLambdaSub || tp2.isAnyKind || isBottom(tp1) then tp1 - else tp2 match { // normalize to disjunctive normal form if possible. + else tp2 match case tp2: LazyRef => glb(tp1, tp2.ref) - case OrType(tp21, tp22) => - tp1 & tp21 | tp1 & tp22 case _ => - tp1 match { + tp1 match case tp1: LazyRef => glb(tp1.ref, tp2) - case OrType(tp11, tp12) => - tp11 & tp2 | tp12 & tp2 case _ => val tp1a = dropIfSuper(tp1, tp2) - if (tp1a ne tp1) glb(tp1a, tp2) - else { + if tp1a ne tp1 then glb(tp1a, tp2) + else val tp2a = dropIfSuper(tp2, tp1) - if (tp2a ne tp2) glb(tp1, tp2a) - else tp1 match { - case tp1: ConstantType => - tp2 match { - case tp2: ConstantType => - // Make use of the fact that the intersection of two constant types - // types which are not subtypes of each other is known to be empty. - // Note: The same does not apply to singleton types in general. - // E.g. we could have a pattern match against `x.type & y.type` - // which might succeed if `x` and `y` happen to be the same ref - // at run time. It would not work to replace that with `Nothing`. - // However, maybe we can still apply the replacement to - // types which are not explicitly written. - NothingType + if tp2a ne tp2 then glb(tp1, tp2a) + else tp2 match // normalize to disjunctive normal form if possible. + case OrType(tp21, tp22) => + tp1 & tp21 | tp1 & tp22 + case _ => + tp1 match + case OrType(tp11, tp12) => + tp11 & tp2 | tp12 & tp2 + case tp1: ConstantType => + tp2 match + case tp2: ConstantType => + // Make use of the fact that the intersection of two constant types + // types which are not subtypes of each other is known to be empty. + // Note: The same does not apply to singleton types in general. + // E.g. we could have a pattern match against `x.type & y.type` + // which might succeed if `x` and `y` happen to be the same ref + // at run time. It would not work to replace that with `Nothing`. + // However, maybe we can still apply the replacement to + // types which are not explicitly written. + NothingType + case _ => andType(tp1, tp2) case _ => andType(tp1, tp2) - } - case _ => andType(tp1, tp2) - } - } - } - } } def widenInUnions(using Context): Boolean = diff --git a/tests/pos/i12286.scala b/tests/pos/i12286.scala new file mode 100644 index 000000000000..d638c1304ab2 --- /dev/null +++ b/tests/pos/i12286.scala @@ -0,0 +1,13 @@ +sealed trait Base { def str: String } +case class One(str: String) extends Base +case class Two(str: String) extends Base +case class Three(str: String) extends Base + +case class Item(_id: String) + +private def doWithItem[T <: (One | Two | Three)] + (item: Item, value: T, action: (T) => Item) = doWithItemId(item._id, value, action) +private def doWithItemId[U <: (One | Two | Three)] + (itemId: String, value: U, action: (U) => Item) = + println(value.str) + Item("_id") \ No newline at end of file