Skip to content

Fix match type bounds checking problem #23695

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,22 +145,26 @@ object Checking {
val checker = new TypeTraverser:
def traverse(tp: Type) =
tp match
case AppliedType(tycon, argTypes)
if !(tycon.typeSymbol.is(JavaDefined) && ctx.compilationUnit.isJava)
// Don't check bounds in Java units that refer to Java type constructors.
// Scala is not obliged to do Java type checking and in fact i17763 goes wrong
// if we attempt to check bounds of F-bounded mutually recursive Java interfaces.
// Do check all bounds in Scala units and those bounds in Java units that
// occur in applications of Scala type constructors.
&& !isCaptureChecking || tycon.typeSymbol.is(CaptureChecked)
// Don't check bounds when capture checking type constructors that were not
// themselves capture checked. Since the type constructor could not foresee
// possible capture sets, it's better to be lenient for backwards compatibility.
=>
checkAppliedType(
untpd.AppliedTypeTree(TypeTree(tycon), argTypes.map(TypeTree(_)))
.withType(tp).withSpan(tpt.span.toSynthetic),
tpt)
case tp @ AppliedType(tycon, argTypes) =>
// Should the type be re-checked in the CC phase?
// Exempted are types that are not themselves capture-checked.
// Since the type constructor could not foresee possible capture sets,
// it's better to be lenient for backwards compatibility.
// Also exempted are match aliases. See tuple-ops.scala for an example that
// would fail otherwise.
def checkableUnderCC =
tycon.typeSymbol.is(CaptureChecked) && !tp.isMatchAlias
if !(tycon.typeSymbol.is(JavaDefined) && ctx.compilationUnit.isJava)
// Don't check bounds in Java units that refer to Java type constructors.
// Scala is not obliged to do Java type checking and in fact i17763 goes wrong
// if we attempt to check bounds of F-bounded mutually recursive Java interfaces.
// Do check all bounds in Scala units and those bounds in Java units that
// occur in applications of Scala type constructors.
&& (!isCaptureChecking || checkableUnderCC) then
checkAppliedType(
untpd.AppliedTypeTree(TypeTree(tycon), argTypes.map(TypeTree(_)))
.withType(tp).withSpan(tpt.span.toSynthetic),
tpt)
case _ =>
traverseChildren(tp)
checker.traverse(tpt.tpe)
Expand Down
13 changes: 13 additions & 0 deletions tests/pos-custom-args/captures/tuple-ops.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
sealed trait Tupp

case object EmptyTupp extends Tupp
type EmptyTupp = EmptyTupp.type
infix case class `*::`[H, T <: Tupp](h: H, t: T) extends Tupp

type Union[T <: Tupp] = T match
case EmptyTupp => Nothing
case h *:: t => h | Union[t]

type Map[T <: Tupp, F[_ <: Union[T]]] <: Tupp = T match
case EmptyTupp => EmptyTupp
case h *:: t => F[h] *:: Map[t, F]
Loading