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

Search for type tests when matching abstract applied type #13453

Merged
merged 1 commit into from Oct 8, 2021

Conversation

nicolasstucki
Copy link
Contributor

@nicolasstucki nicolasstucki commented Sep 2, 2021

  • Search for type tests when matching abstract applied type
  • Normalize types when synthesizing type to avoid match type in the TypeTest closure signature.

Fixes #13433

@nicolasstucki nicolasstucki self-assigned this Sep 2, 2021
@nicolasstucki nicolasstucki marked this pull request as ready for review September 7, 2021 13:28
@smarter
Copy link
Member

smarter commented Sep 7, 2021

Normalize types when synthesizing type test tp avoid isInstanceOf of a match type

What's the issue with isInstanceOf on a match type? And if there is an issue, shouldn't normalized be called at the point where we generate the isInstanceOf instead?

@nicolasstucki
Copy link
Contributor Author

What's the issue with isInstanceOf on a match type? And if there is an issue, shouldn't normalized be called at the point where we generate the isInstanceOf instead?

Investigating further it was not the isInstanceOf, it was the type of the TypeTest.
We generated

{
  def $anonfun(s: Any): Option[(s : Any) & Matcher[String]] =
    if s.isInstanceOf[Matcher[String]] then
      Some.apply[s.type & Matcher[String]](
        s.asInstanceOf[s.type & Matcher[String]]
      )
      else None
  closure($anonfun:scala.reflect.TypeTest[Any, Matcher[String]])
}

but if we write it explicitly we infer

{
  def $anonfun(s: Any): Option[(s : Any) & String] =
    {
      if s.isInstanceOf[Matcher[String]] then
        Some.apply[s.type & Matcher[String]](
          s.asInstanceOf[s.type & Matcher[String]]
        )
        else None
    }
  closure($anonfun)
}

I will investigate further

@nicolasstucki
Copy link
Contributor Author

As far as I can see it is only the synthesized version of the type test that needs this explicit normalization of the type.

@smarter
Copy link
Member

smarter commented Sep 9, 2021

By the way, is there any reason to limit ourselves to abstract types? One could imagine wanting to write a TypeTest instance for a concrete class with abstract type parameters:

import scala.reflect.Typeable

class Box[A](val a: A)(using val ta: Typeable[A])
object Box:
  given [A]: Typeable[Box[A]] with
    def unapply(x: Any) = x match
      case box: Box[_] =>
        if box.ta.unapply(box.a).isDefined then
          Some(box.asInstanceOf[x.type & Box[A]])
        else
          None
      case _ =>
        None

def func[A](box: Box[A]) = box match {
  case _: Box[String] => println("String")
  case _: Box[Int] => println("Int")
}

(this came up on gitter today: https://gitter.im/scala/scala?at=613a4ccf4c7be06b79abec3e)

@smarter
Copy link
Member

smarter commented Sep 9, 2021

(probably not something we want to change in this PR but worth discussing later)

@nicolasstucki
Copy link
Contributor Author

By the way, is there any reason to limit ourselves to abstract types?

That is a limitation that I would like to remove at some point. The only noticeable thing is that some pattern matches that had unchecked warnings might become checked, which will change the semantics of some existing programs. Unless we do not provide any interesting implementations of TypeTest out of the box.

@smarter
Copy link
Member

smarter commented Sep 20, 2021

I will investigate further

Are you still investigating or is this PR waiting on me?

@smarter smarter assigned nicolasstucki and unassigned smarter Sep 20, 2021
@nicolasstucki
Copy link
Contributor Author

I will investigate further

Are you still investigating or is this PR waiting on me?

Waiting for you.

The investigation concluded with #13453 (comment). Sorry to not have been clear.

@@ -50,7 +50,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
(formal, span) => formal.argInfos match {
case arg1 :: arg2 :: Nil if !defn.isBottomClass(arg2.typeSymbol) =>
val tp1 = fullyDefinedType(arg1, "TypeTest argument", span)
val tp2 = fullyDefinedType(arg2, "TypeTest argument", span)
val tp2 = fullyDefinedType(arg2, "TypeTest argument", span).normalized
Copy link
Contributor

Choose a reason for hiding this comment

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

That looks a bit suspicious. By default I would say that we need a really good reason to manually trigger type normalized. In principle all newly created types should already go thought nurmalization just after creation, and if fullyDefinedType creates non normalized types it might mean that there is a better spot in the code to do the normalization (deeper in the stack trace).

Note that we another place in the code where we do a fullyDefinedType call followed by a .normalized (which is equality suspicious):

https://github.com/lampepfl/dotty/blob/fe24fd3af2d985262b67394ac8d606a9830e0291/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala#L163

My intition tells me that there is a way to both fix this issue and remove that .normalized in Synthesizer, and that this way would a better solution that the change done in this PR...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Then it seems to be a general limitation. We should probably open a separate issue for this improvement.

@smarter smarter enabled auto-merge October 4, 2021 15:04
@nicolasstucki
Copy link
Contributor Author

Rebased

@smarter smarter merged commit f726a24 into scala:master Oct 8, 2021
@smarter smarter deleted the fix-13433 branch October 8, 2021 13:25
@Kordyjan Kordyjan added this to the 3.1.1 milestone Aug 2, 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.

Spurious missing TypeTest instance for a match type
4 participants