diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 6207e0a3d728..dfb1c6b7b7a1 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -861,12 +861,24 @@ trait ConstraintHandling { addConstraintInvocations += 1 val saved = canWidenAbstract canWidenAbstract = true - try bound match + + def contains(bound: Type) = bound match + case tpr: TypeParamRef => constraint.contains(tpr) + case tv: TypeVar => constraint.contains(tv) + case _ => false + + def add(bound: Type): Boolean = bound match + case AndType(bound1, bound2) if contains(bound1) ^ contains(bound2) => + add(bound1) && add(bound2) case bound: TypeParamRef if constraint contains bound => addParamBound(bound) + case bound: TypeVar if constraint.contains(bound) => + addParamBound(bound.origin) case _ => val pbound = avoidLambdaParams(bound) kindCompatible(param, pbound) && addBoundTransitively(param, pbound, !fromBelow) + + try add(bound) finally canWidenAbstract = saved addConstraintInvocations -= 1 diff --git a/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala b/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala index 9ae3fda8c6b9..ef5cdd4ce452 100644 --- a/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala +++ b/compiler/test/dotty/tools/dotc/core/ConstraintsTest.scala @@ -1,19 +1,16 @@ package dotty.tools -package dotc.core +package dotc +package core import vulpix.TestConfiguration -import dotty.tools.dotc.core.Contexts.{*, given} -import dotty.tools.dotc.core.Decorators.{*, given} -import dotty.tools.dotc.core.Symbols.* -import dotty.tools.dotc.core.Types.* -import dotty.tools.dotc.ast.tpd.* -import dotty.tools.dotc.typer.ProtoTypes.constrained +import Contexts.{*, given}, Decorators.{*, given}, Symbols.*, Types.* +import ast.tpd.* +import typer.ProtoTypes.* +import util.SimpleIdentitySet import org.junit.Test -import dotty.tools.DottyTest - class ConstraintsTest: @Test def mergeParamsTransitivity: Unit = @@ -91,3 +88,19 @@ class ConstraintsTest: assert(!ctx.typerState.constraint.occursAtToplevel(tvar.origin, entry), i"cyclic bound for ${tvar.origin}: ${entry} in ${ctx.typerState.constraint}") } + + @Test def splitIntersectedBounds: Unit = inCompilerContext(TestConfiguration.basicClasspath) { + val Foo = newTypeVar(TypeBounds.empty, "Foo".toTypeName) + val Bar = newTypeVar(TypeBounds.empty, "Bar".toTypeName) + val Int = defn.IntType + + val tp1 = Foo & Int + val tp2 = Bar & Int + val log = TypeComparer.explained(_.isSameType(tp1, tp2)) + //println(i"$log") + //println(i"${ctx.typerState.constraint}") + + val tree = ref(newAnonFun(defn.RootClass, MethodType(Nil, defn.UnitType))).appliedToNone + ctx.typer.interpolateTypeVars(tree, WildcardType, SimpleIdentitySet.empty) + assert(Foo =:= Int && Bar =:= Int, i"Foo=$Foo Bar=$Bar") + } diff --git a/tests/pos/i16524.scala b/tests/pos/i16524.scala new file mode 100644 index 000000000000..baa916d5873a --- /dev/null +++ b/tests/pos/i16524.scala @@ -0,0 +1,14 @@ +trait Axe +trait Foo[A] +trait Bar[B] { type Res } +class Ref[C](value: C) +trait Opt[+T] + +abstract class Test: + def mash[X](ref: Ref[X], foo: Foo[X], bar: Bar[X]): bar.Res + + def mkFoo[Y]: Foo[Opt[Y & Axe]] + def mkBar[Z]: Bar[Opt[Z & Axe]] { type Res = Z } + val optInt: Opt[Int & Axe] + + def test = mash(new Ref(optInt), mkFoo, mkBar)