Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
11 changes: 6 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5052,11 +5052,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer

Linter.warnOnImplausiblePattern(tree, pt)

val cmp =
untpd.Apply(
untpd.Select(untpd.TypedSplice(tree), nme.EQ),
untpd.TypedSplice(dummyTreeOfType(pt)))
typedExpr(cmp, defn.BooleanType)
if ! (tree.tpe <:< pt && (tree.symbol.flags.isAllOf(Flags.EnumValue) || (tree.symbol.flags.isAllOf(Flags.Module | Flags.Case)))) then
Copy link
Contributor

@odersky odersky Aug 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some remarks:

  1. The motivation for the SIP was that it makes strictEquality easier to use, but this also changes the pattern matching behavior of patterns without that import. And it breaks the law that pattern matching values is done with an == check. I think this goes too far, even though this is what's specified in the SIP.

  2. We cannot implement a SIP change without going through an experimenal language import.

  3. I think it would be better to add code to assumedCanEqual instead. Under strictEquality and the experimental import, if the left operand (or either one) is an enum constant, and the other operand is a supertype, assume the two CanEqual.

Copy link
Author

@mberndt123 mberndt123 Aug 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @odersky,

Thank you for your review.

Regarding 1: 🤔 I'm not sure I understand. The SIP says “The semantics of pattern matching against a constant are otherwise unchanged”, meaning that it will still be equivalent to ==, except it doesn't require CanEqual. If that's not what it does, then it's a bug in my implementation. But I think that's solved by moving it to assumeCanEqual as you suggested in point 3, right?

Regarding 2: ✅ I've added an experimental language import, strictEqualityPatternMatching, to enable this behaviour. I also tried adding a Mode for this so it can be enabled with a command line flag similar to -language:strictEquality, but unless I'm misreading the code, the modes are stored in an Int bitmask and all the bits are already taken 😒

Regarding 3: ✅ I've moved the implementation to assumedCanEqual, which was a bit tricky because in that function the Tree isn't available, but I need it to know if it's a case object or enum case. It's also called from Synthesizer where there is no Tree, so I'm passing it as an Option[Tree] now.

val cmp =
untpd.Apply(
untpd.Select(untpd.TypedSplice(tree), nme.EQ),
untpd.TypedSplice(dummyTreeOfType(pt)))
typedExpr(cmp, defn.BooleanType)
case _ =>

private def checkStatementPurity(tree: tpd.Tree)(original: untpd.Tree, exprOwner: Symbol, isUnitExpr: Boolean = false)(using Context): Unit =
Expand Down
43 changes: 43 additions & 0 deletions compiler/test/dotty/tools/dotc/typer/SIP67Tests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package dotty.tools.dotc.typer

import dotty.tools.DottyTest
import dotty.tools.dotc.core.Contexts.*

import org.junit.Test
import org.junit.Assert.fail

class SIP67Tests extends DottyTest:

@Test
def sip67test1: Unit =
val source = """
import scala.language.strictEquality
enum Foo:
case Bar

val _ =
(??? : Foo) match
case Foo.Bar =>
"""
val ctx = checkCompile("typer", source)((_, ctx) => ())

if ctx.reporter.hasErrors then
fail("Unexpected compilation errors were reported")

@Test
def sip67test2: Unit =
val source = """
import scala.language.strictEquality

sealed trait Foo

object Foo:
case object Bar extends Foo

val _ =
(??? : Foo) match
case Foo.Bar =>
"""
val ctx = checkCompile("typer", source)((_, ctx) => ())
if ctx.reporter.hasErrors then
fail("Unexpected compilation errors were reported")
Loading