Skip to content

Add warning for synchronized on value classes #20301

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

Closed
wants to merge 3 commits into from

Conversation

nox213
Copy link
Contributor

@nox213 nox213 commented Apr 30, 2024

fix #17493
Thanks to @mbovel for his collaboration during the Spree.

nox213 and others added 2 commits May 1, 2024 01:03
@mbovel mbovel requested a review from sjrd April 30, 2024 16:36
Copy link
Member

Choose a reason for hiding this comment

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

You should probably move this file to the tests/warn directory and remove the -Xfatal-warnings from the compiler's options

Copy link
Contributor Author

Choose a reason for hiding this comment

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

68c30d1
fixed

@nox213 nox213 requested a review from hamzaremmal May 1, 2024 02:31
Copy link
Member

@hamzaremmal hamzaremmal left a comment

Choose a reason for hiding this comment

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

Can you also squash everything in a single commit ?

Let's wait for @sjrd's review before merging it

@@ -1155,6 +1155,11 @@ object RefChecks {
then report.warning(ExtensionNullifiedByMember(sym, target.typeSymbol), sym.srcPos)
end checkExtensionMethods

def checkSynchronizedCallOnValue(tree: Tree)(using Context): Unit =
if tree.symbol == defn.Object_synchronized
&& ctx.owner.enclosingClass.isValueClass then
Copy link
Member

Choose a reason for hiding this comment

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

This won't do the right thing for an imported synchronized.

You shouldn't look at the context owner, but at the prefix of tree.tpe.

tree

override def transformSelect(tree: tpd.Select)(using Context): tpd.Tree =
if defn.ScalaBoxedClasses().contains(tree.qualifier.tpe.typeSymbol) && tree.name == nme.synchronized_ then
report.warning(SynchronizedCallOnBoxedClass(tree), tree.srcPos)
report.warning(SynchronizedCallOnValue(tree), tree.srcPos)
Copy link
Member

Choose a reason for hiding this comment

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

This should probably reuse checkSynchronizedCallOnValue. Otherwise explicit selections on AnyVal won't do the right thing, AFAICT.

Copy link
Member

Choose a reason for hiding this comment

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

We tried the following during the Spree:

//> using options -explain
class A(val s: String) extends AnyVal
def Test =
  val a = A("hello")
  a.synchronized { println("hello, world") } // warn

And this is already throws an error:

 ~/scala-snippets-5 scalac foo.scala
-- [E008] Not Found Error: foo.scala:5:4 ---------------------------------------
5 |  a.synchronized { println("hello, world") } // warn
  |  ^^^^^^^^^^^^^^
  |value synchronized is not a member of A, but could be made available as an extension method.
  |
  |The following import might fix the problem:
  |
  |  import scala.reflect.Selectable.reflectiveSelectable
  |
1 error found

Is this the case you have in mind?

Anyway, I guess it might be nice to use the same logic both for Select and Apply.

@mbovel
Copy link
Member

mbovel commented May 1, 2024

Can you also squash everything in a single commit ?

What is the benefit of squashing before merging? I would usually just use the "Squash and merge" feature from Github.

@mbovel
Copy link
Member

mbovel commented May 1, 2024

Thanks to @sjrd's comments and re-reading discussions on #17449, I think what we did here is not correct.

The call to synchronized is resolved to a call to Predef.synchronized (as @som-snytt already noted on the issue's description):

sbt:scala3> scalac -Xprint:typer -Yprint-debug tests/warn/i17493.scala
...
[[syntax trees at end of                     typer]] // tests/warn/i17493.scala
package <root>.this.<empty> {
  final class A(s: scala.this.Predef.String) extends <root>.this.scala.AnyVal.
    <init>() {
    val s: scala.this.Predef.String
    def g: scala.this.Unit(inf) =
      scala.this.Predef.synchronized[scala.this.Unit^(inf)](
        {
          scala.this.Predef.println("hello, world")
        }
      )
  }
...

So we should not warn about a call on a value.

However, we have an existing machinery for warning about top-level calls to synchronized, so we should probably just adapt it for when the call is inside a value class definition. I will open a separate PR with a new fix suggestion in a minute.

@nox213
Copy link
Contributor Author

nox213 commented May 1, 2024

I see. The issue wasn't that simple than I expected. I will close this PR since a new suggestion is already ready.

@nox213
Copy link
Contributor Author

nox213 commented May 1, 2024

Better approach #20312

@nox213 nox213 closed this May 1, 2024
odersky added a commit that referenced this pull request May 1, 2024
)

751cc2f is a more direct way to fix
#17493 than #20301.

7a9102a further generalizes
`checkAnyRefMethodCall`, as suggested by @odersky.
@nox213 nox213 deleted the fix_i17493 branch May 1, 2024 23:02
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.

Missing error on synchronized from value class
4 participants