diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 1dfa04822766..7eb00a1be5f2 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -550,6 +550,10 @@ trait ConstraintHandling { inst end approximation + private def isTransparent(tp: Type)(using Context): Boolean = tp match + case AndType(tp1, tp2) => isTransparent(tp1) && isTransparent(tp2) + case _ => tp.typeSymbol.isTransparentTrait && !tp.isLambdaSub + /** If `tp` is an intersection such that some operands are transparent trait instances * and others are not, replace as many transparent trait instances as possible with Any * as long as the result is still a subtype of `bound`. But fall back to the @@ -563,7 +567,7 @@ trait ConstraintHandling { def dropOneTransparentTrait(tp: Type): Type = val tpd = tp.dealias - if tpd.typeSymbol.isTransparentTrait && !tpd.isLambdaSub && !kept.contains(tpd) then + if isTransparent(tpd) && !kept.contains(tpd) then dropped = tpd :: dropped defn.AnyType else tpd match @@ -648,7 +652,16 @@ trait ConstraintHandling { val wideInst = if isSingleton(bound) then inst - else dropTransparentTraits(widenIrreducible(widenOr(widenSingle(inst))), bound) + else + val widenedFromSingle = widenSingle(inst) + val widenedFromUnion = widenOr(widenedFromSingle) + val widened = + if (widenedFromUnion ne widenedFromSingle) && isTransparent(widenedFromUnion) then + widenedFromSingle + else + dropTransparentTraits(widenedFromUnion, bound) + widenIrreducible(widened) + wideInst match case wideInst: TypeRef if wideInst.symbol.is(Module) => TermRef(wideInst.prefix, wideInst.symbol.sourceModule) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 174244b4a456..48f2f90f05d8 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1839,7 +1839,11 @@ class Definitions { requiredClass("scala.collection.generic.IsMap"), requiredClass("scala.collection.generic.IsSeq"), requiredClass("scala.collection.generic.Subtractable"), - requiredClass("scala.collection.immutable.StrictOptimizedSeqOps") + requiredClass("scala.collection.immutable.StrictOptimizedSeqOps"), + AnyClass, + AnyValClass, + ObjectClass, + MatchableClass ) // ----- primitive value class machinery ------------------------------------------ diff --git a/tests/neg/harmonize.scala b/tests/neg/harmonize.scala index 0fe03d2d7600..72275a8f68fc 100644 --- a/tests/neg/harmonize.scala +++ b/tests/neg/harmonize.scala @@ -79,9 +79,9 @@ object Test { val a4 = ArrayBuffer(1.0f, 1L) val b4: ArrayBuffer[Double] = a4 // error: no widening val a5 = ArrayBuffer(1.0f, 1L, f()) - val b5: ArrayBuffer[AnyVal] = a5 + val b5: ArrayBuffer[Float | Long | Int] = a5 val a6 = ArrayBuffer(1.0f, 1234567890) - val b6: ArrayBuffer[AnyVal] = a6 + val b6: ArrayBuffer[Float | Int] = a6 def totalDuration(results: List[Long], cond: Boolean): Long = results.map(r => if (cond) r else 0).sum diff --git a/tests/neg/supertraits.scala b/tests/neg/supertraits.scala index 2fc79ca30f1d..6952c7640529 100644 --- a/tests/neg/supertraits.scala +++ b/tests/neg/supertraits.scala @@ -6,19 +6,20 @@ class C extends A, S val x = if ??? then B() else C() val x1: S = x // error -case object a -case object b +class Top +case object a extends Top +case object b extends Top val y = if ??? then a else b val y1: Product = y // error val y2: Serializable = y // error -enum Color { +enum Color extends Top { case Red, Green, Blue } -enum Nucleobase { +enum Nucleobase extends Top { case A, C, G, T } val z = if ??? then Color.Red else Nucleobase.G -val z1: reflect.Enum = z // error: Found: (z : Object) Required: reflect.Enum +val z1: reflect.Enum = z // error: Found: (z : Top) Required: reflect.Enum diff --git a/tests/neg/union.scala b/tests/neg/union.scala index 0a702ab70058..c6fd42e6629e 100644 --- a/tests/neg/union.scala +++ b/tests/neg/union.scala @@ -11,8 +11,9 @@ object Test { } object O { - class A - class B + class Top + class A extends Top + class B extends Top def f[T](x: T, y: T): T = x val x: A = f(new A { }, new A) diff --git a/tests/neg-custom-args/allow-deep-subtypes/i15365.scala b/tests/pos-deep-subtype/i15365.scala similarity index 100% rename from tests/neg-custom-args/allow-deep-subtypes/i15365.scala rename to tests/pos-deep-subtype/i15365.scala diff --git a/tests/pos/unions.scala b/tests/pos/unions.scala new file mode 100644 index 000000000000..4d46b6b5b27f --- /dev/null +++ b/tests/pos/unions.scala @@ -0,0 +1,7 @@ +object Test: + + def test = + val x = if ??? then "" else 1 + val _: String | Int = x + + diff --git a/tests/run/weak-conformance.scala b/tests/run/weak-conformance.scala index fdd28dbc524a..db551cf0ce15 100644 --- a/tests/run/weak-conformance.scala +++ b/tests/run/weak-conformance.scala @@ -26,11 +26,11 @@ object Test extends App { locally { def f(): Int = b + 1 val x1 = ArrayBuffer(b, 33, 5.5) ; x1: ArrayBuffer[Double] // b is an inline val - val x2 = ArrayBuffer(f(), 33, 5.5) ; x2: ArrayBuffer[AnyVal] // f() is not a constant + val x2 = ArrayBuffer(f(), 33, 5.5) ; x2: ArrayBuffer[Int | Double] // f() is not a constant val x3 = ArrayBuffer(5, 11L) ; x3: ArrayBuffer[Long] - val x4 = ArrayBuffer(5, 11L, 5.5) ; x4: ArrayBuffer[AnyVal] // Long and Double found + val x4 = ArrayBuffer(5, 11L, 5.5) ; x4: ArrayBuffer[Int | Long | Double] // Long and Double found val x5 = ArrayBuffer(1.0f, 2) ; x5: ArrayBuffer[Float] - val x6 = ArrayBuffer(1.0f, 1234567890); x6: ArrayBuffer[AnyVal] // loss of precision + val x6 = ArrayBuffer(1.0f, 1234567890); x6: ArrayBuffer[Float | Int] // loss of precision val x7 = ArrayBuffer(b, 33, 'a') ; x7: ArrayBuffer[Char] val x8 = ArrayBuffer(5.toByte, 11) ; x8: ArrayBuffer[Byte]