diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 835da2176a33..d59e8d616ec9 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -557,8 +557,8 @@ trait ConstraintHandling { // Widening can add extra constraints, in particular the widened type might // be a type variable which is now instantiated to `param`, and therefore // cannot be used as an instantiation of `param` without creating a loop. - // If that happens, we run `instanceType` again to find a new instantation. - // (we do not check for non-toplevel occurences: those should never occur + // If that happens, we run `instanceType` again to find a new instantiation. + // (we do not check for non-toplevel occurrences: those should never occur // since `addOneBound` disallows recursive lower bounds). if constraint.occursAtToplevel(param, widened) then instanceType(param, fromBelow) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 2d02546a6d5e..da8380755219 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1266,9 +1266,10 @@ object Types { /** Widen this type and if the result contains embedded soft union types, replace * them by their joins. * "Embedded" means: inside type lambdas, intersections or recursive types, - * in prefixes of refined types, or in hard union types. + * or in prefixes of refined types. Softs unions inside hard unions are + * left untouched. * If an embedded soft union is found, we first try to simplify or eliminate it by - * re-lubbing it while allowing type parameters to be constrained further. + * re-lubing it while allowing type parameters to be constrained further. * Any remaining union types are replaced by their joins. * * For instance, if `A` is an unconstrained type variable, then @@ -1303,8 +1304,8 @@ object Types { case tp => tp - /** Widen all top-level singletons reachable by dealiasing - * and going to the operands of & and |. + /** Widen all top-level singletons reachable by dealiasing and going to the + * operands of intersections and soft unions. * Overridden and cached in OrType. */ def widenSingletons(using Context): Type = dealias match { @@ -3266,7 +3267,7 @@ object Types { TypeComparer.lub(tp1.widenUnionWithoutNull, tp2.widenUnionWithoutNull, canConstrain = true) match case union: OrType => union.join case res => res - else derivedOrType(tp1.widenUnionWithoutNull, tp2.widenUnionWithoutNull) + else this if !isProvisional then myUnionPeriod = ctx.period myUnion @@ -3280,11 +3281,16 @@ object Types { if tp1.hasClassSymbol(defn.NothingClass) then tp2.atoms else if tp2.hasClassSymbol(defn.NothingClass) then tp1.atoms else tp1.atoms | tp2.atoms - val tp1w = tp1.widenSingletons - val tp2w = tp2.widenSingletons - myWidened = if ((tp1 eq tp1w) && (tp2 eq tp2w)) this else tp1w | tp2w atomsRunId = ctx.runId + myWidened = + if isSoft then + val tp1w = tp1.widenSingletons + val tp2w = tp2.widenSingletons + if ((tp1 eq tp1w) && (tp2 eq tp2w)) this else tp1w | tp2w + else + this + override def atoms(using Context): Atoms = ensureAtomsComputed() myAtoms diff --git a/tests/pos/widen-union.scala b/tests/pos/widen-union.scala index b0b64f0dc6c6..20e088c4bb6c 100644 --- a/tests/pos/widen-union.scala +++ b/tests/pos/widen-union.scala @@ -13,7 +13,6 @@ object Test2: || xs.corresponds(ys)(consistent(_, _)) // error, found: Any, required: Int | String object Test3: - def g[X](x: X | String): Int = ??? def y: Boolean | String = ??? g[Boolean](y) @@ -21,4 +20,14 @@ object Test3: g[Boolean](identity(y)) g(identity(y)) +object Test4: + def f(a: 2 | 3) = a + + def test() = + val x: 2 | 3 = 2 + val y = x + f(y) + def g: 2 | 3 = 2 + val z = g + f(z)