Skip to content
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

Improve logic when to emit pattern type error #18093

Merged
merged 4 commits into from
Jul 6, 2023

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Jun 28, 2023

Improve logic when to emit "pattern type is incompatible with expected type" error.

Fixes #18083

The whole thing is not very satisfactory. We have an approximation which has both false positives and false negatives.

False positives: We are too lenient for numeric types and and/or types
False negatives: We ignore user-generated equals methods

The new approximation seems to be somewhat better than the old, but it's still an approximation. It begs the question why we attempt to do this at all.

@odersky odersky force-pushed the fix-18083 branch 3 times, most recently from 83bcfd6 to 67ffb4b Compare June 29, 2023 11:19
@odersky odersky requested a review from liufengyun June 29, 2023 13:13
// still compute `canEqual(A & B, B & A) = true`.
canEqual(a, b.tp1) || canEqual(a, b.tp2)

if !canEqual(tree.tpe, pt) then
Copy link
Contributor

Choose a reason for hiding this comment

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

Here is the history of the original code:

#9166 (comment)

@dwijnand has a good point there: it's not worth the effort to fix the bug. Maybe simply remove the code? Otherwise, more bugs will be reported in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree. Let's try to cut it down to

  protected def checkEqualityEvidence(tree: tpd.Tree, pt: Type)(using Context) : Unit =
    tree match
      case _: RefTree | _: Literal
      if !isVarPattern(tree) && !(pt <:< tree.tpe) =>
        withMode(Mode.GadtConstraintInference) {
          TypeComparer.constrainPatternType(tree.tpe, pt)
        }
        val cmp =
          untpd.Apply(
            untpd.Select(untpd.TypedSplice(tree), nme.EQ),
            untpd.TypedSplice(dummyTreeOfType(pt)))
        typedExpr(cmp, defn.BooleanType)
      case _ =>

odersky added 2 commits July 5, 2023 13:19
Improve logic when to emit "pattern type is incompatible with expected type"
error.

Fixes scala#18083

The whole thing is not very satisfactory. We have an approximation which has both
false positives and false negatives.

False positives: We are too lenient for numeric types and and/or types
False negatives: We ignore user-generated equals methods

The new approximation seems to be somewhat better than the old, but it's still an
approximation. It begs the question why we attempt to do this at all.
@odersky
Copy link
Contributor Author

odersky commented Jul 5, 2023

That change breaks quite a few tests, which specifically check for the pattern. I agree that making it an error as we did before is excessive since there could be implementations of equals that make the cases work. Maybe make it a linter warning instead?

Use -Wimplausible-patterns to do the checks formerly done in
checkEqualityEvidence.

Create a new source file typer/Linter.scala for all linting checks done at Typer.
@liufengyun
Copy link
Contributor

Maybe make it a linter warning instead?

A linting warning will be much more friendly.

@odersky
Copy link
Contributor Author

odersky commented Jul 5, 2023

With the latest commit we now do it under a linter option.

Use -Wimplausible-patterns to do the checks formerly done in checkEqualityEvidence.

This also creates a new source file typer/Linter.scala for all linting checks done at Typer.

Copy link
Contributor

@liufengyun liufengyun left a comment

Choose a reason for hiding this comment

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

LGTM

extends TypeMsg(ImplausiblePatternWarningID):
def msg(using Context) =
i"""|Implausible pattern:
|$pat could match selector of type $selType
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if the space is intentional:

Suggested change
|$pat could match selector of type $selType
|$pat could match selector of type $selType

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did that intentionally since I wanted to have some visual space between the program code and the actual text. Let's see how it works in practice.

|| clsB.isNumericValueClass

// Can type `a` possiblly have a common instance with type `b`?
def canEqual(a: Type, b: Type): Boolean = trace(i"canEqual $a $b"):
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: There is TypeComparer.provablyDisjoint, it seems to be good enough for the purpose of linting --- the semantics is a little different though.

Copy link
Contributor Author

@odersky odersky Jul 6, 2023

Choose a reason for hiding this comment

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

I looked at that, but it seems too different to be easily adaptable. And I don't want linter code to complicate the other code. So if we could use provably disjoint as is, it would be OK. But it seems the only point where we could use it is to implement the canHaveCommonChild functionality, and that one is simpler by itself.

@odersky odersky merged commit 54d6fcd into scala:main Jul 6, 2023
@odersky odersky deleted the fix-18083 branch July 6, 2023 07:52
@Kordyjan Kordyjan added this to the 3.4.0 milestone Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Flow typing leads to incorrect "pattern type is incompatible with expected type" error
3 participants