From 45e15269dec56a68112f48d3f0871e1aed226588 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 24 Jun 2021 14:26:06 +0200 Subject: [PATCH 1/2] Widen unions before finding members If a prefix in a findMember is an OrType, widen it to its join before computing the selection denotation. This mimics what is done if the prefix is pulled out into a temporary variable. Fixes #12909 --- .../tools/dotc/core/SymDenotations.scala | 5 ++- tests/pos/i12909.scala | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i12909.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index b24a4a7bae2c..112bd182b1ad 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -2049,7 +2049,10 @@ object SymDenotations { override final def findMember(name: Name, pre: Type, required: FlagSet, excluded: FlagSet)(using Context): Denotation = val raw = if excluded.is(Private) then nonPrivateMembersNamed(name) else membersNamed(name) - raw.filterWithFlags(required, excluded).asSeenFrom(pre).toDenot(pre) + val pre1 = pre match + case pre: OrType => pre.widenUnion + case _ => pre + raw.filterWithFlags(required, excluded).asSeenFrom(pre1).toDenot(pre1) final def findMemberNoShadowingBasedOnFlags(name: Name, pre: Type, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags)(using Context): Denotation = diff --git a/tests/pos/i12909.scala b/tests/pos/i12909.scala new file mode 100644 index 000000000000..face34e2e5a3 --- /dev/null +++ b/tests/pos/i12909.scala @@ -0,0 +1,42 @@ +package example + +final case class Writer[W, A](run: (W, A)) { + def map[B](f: A => B): Writer[W, B] = ??? + + def flatMap[B](f: A => Writer[W, B]): Writer[W, B] = ??? +} + +object Main { + implicit class WriterOps[A](a: A) { + def set[W](w: W): Writer[W, A] = ??? + } + + def x1[A]: Writer[Vector[String], Option[A]] = ??? + + val failure = for { + a1 <- { + Option(1) match { + case Some(x) => + x1[Boolean] + case _ => + Option.empty[Boolean].set(Vector.empty[String]) + } + } + a2 <- x1[String] + } yield () + + val success = for { + a1 <- { + val temp = Option(1) match { + case Some(x) => + x1[Boolean] + case _ => + Option.empty[Boolean].set(Vector.empty[String]) + } + // why ??? + temp + } + a2 <- x1[String] + } yield () + +} \ No newline at end of file From a0e4b8af98ee1371e68c03339f947ae7c0b27b90 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 24 Jun 2021 23:11:02 +0200 Subject: [PATCH 2/2] Cache widenUnion results --- .../src/dotty/tools/dotc/core/Types.scala | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 80e0601a68ee..aaad035266dd 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1282,13 +1282,10 @@ object Types { case tp => tp.widenUnionWithoutNull + /** Overridden in OrType */ def widenUnionWithoutNull(using Context): Type = widen match - case tp @ OrType(lhs, rhs) if tp.isSoft => - TypeComparer.lub(lhs.widenUnionWithoutNull, rhs.widenUnionWithoutNull, canConstrain = true) match - case union: OrType => union.join - case res => res - case tp: AndOrType => - tp.derivedAndOrType(tp.tp1.widenUnionWithoutNull, tp.tp2.widenUnionWithoutNull) + case tp: AndType => + tp.derivedAndType(tp.tp1.widenUnionWithoutNull, tp.tp2.widenUnionWithoutNull) case tp: RefinedType => tp.derivedRefinedType(tp.parent.widenUnion, tp.refinedName, tp.refinedInfo) case tp: RecType => @@ -3198,6 +3195,20 @@ object Types { myJoin } + private var myUnion: Type = _ + private var myUnionPeriod: Period = Nowhere + + override def widenUnionWithoutNull(using Context): Type = + if myUnionPeriod != ctx.period then + myUnion = + if isSoft then + TypeComparer.lub(tp1.widenUnionWithoutNull, tp2.widenUnionWithoutNull, canConstrain = true) match + case union: OrType => union.join + case res => res + else derivedOrType(tp1.widenUnionWithoutNull, tp2.widenUnionWithoutNull) + if !isProvisional then myUnionPeriod = ctx.period + myUnion + private var atomsRunId: RunId = NoRunId private var myAtoms: Atoms = _ private var myWidened: Type = _