Skip to content

space following unary minus produces misleading 'then expected' error message #13410

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
philwalk opened this issue Aug 28, 2021 · 6 comments · Fixed by #13412
Closed

space following unary minus produces misleading 'then expected' error message #13410

philwalk opened this issue Aug 28, 2021 · 6 comments · Fixed by #13412
Milestone

Comments

@philwalk
Copy link
Contributor

Compiler version

version:=3.0.2-RC2
revision:=b8b980d1d38367fbeb153381d7c0e243db52fcc4
buildTime:=2021-08-23 22:33:43+0200

Minimized example

1 object UnaryMinus {
2   def sign(x: Int): Int = {
3     if (x > 0) 1
4     else if (x < 0) - 1
5     else 0
6   }
7 }

Output

$ scalac3 jsrc/unaryMinus.sc
-- [E040] Syntax Error: jsrc\unaryMinus.sc:5:4 ---------------------------------------------------------------------------------------------------------------------------------------------------------
5 |    else 0
  |    ^^^^
  |    'then' expected, but 'else' found
-- [E008] Not Found Error: jsrc\unaryMinus.sc:4:20 -----------------------------------------------------------------------------------------------------------------------------------------------------
4 |    else if (x < 0) - 1
  |            ^^^^^^^^^
  |            value - is not a member of Boolean, but could be made available as an extension method.
  |
  |            One of the following imports might make progress towards fixing the problem:
  |
  |              import math.Fractional.Implicits.infixFractionalOps
  |              import math.Integral.Implicits.infixIntegralOps
  |              import math.Numeric.Implicits.infixNumericOps
  |
-- [E129] Potential Issue Warning: jsrc\unaryMinus.sc:4:23 ---------------------------------------------------------------------------------------------------------------------------------------------
4 |    else if (x < 0) - 1
  |                       ^
  |                       A pure expression does nothing in statement position; you may be omitting necessary parentheses

longer explanation available when compiling with `-explain`
1 warning found
2 errors found

Expectation

The scala2 compiler compiles without error, and recognizes the expression - 1 as negative one.
The scala3 compiler seems to interpret the minus sign as Boolean operator?, resulting in an invalid boolean expression.

Perhaps the "optional braces" feature mandates that unary minus tokenization rules must be tightened relative to scala2. If so, it might be worth some effort to improve the error message here.

This particular example is based on code generated automatically by Intellij when converting a java ternary operator to scala. I'm not sure why it inserted a space after the minus sign, but it's valid scala2. There might be a fair amount of scala2 code out there that will lead to this compile error.

@som-snytt
Copy link
Contributor

som-snytt commented Aug 29, 2021

You can use -source 3.0-migration as a workaround.

This isn't due to optional braces but to quiet control syntax, which detects the operator as "leading infix" and therefore a continuation of the conditional.

The doc says misleadingly that the parens may be omitted if there is a then; since there is no then, it's reasonable to expect it to compile. Edit: TIL "inferred then", which is why the leading infix test was added. Edit: TIL inferred then was removed last year. The leading infix test should also be removed, so it will always look ahead to check if a then is lexically in the near future.

My comment on the ticket says I started writing if-then exactly a year ago on US Labor Day.

#9746

@philwalk
Copy link
Contributor Author

Thanks for the suggestion, and the explanation!

@philwalk philwalk changed the title space following unary minus produces misleading optional braces error message space following unary minus produces misleading 'then expected' error message Aug 29, 2021
@philwalk
Copy link
Contributor Author

philwalk commented Aug 29, 2021

Not sure if this is related, but the following code fails unless the last line is preceded by a blank line:'

Source file func.sc :

object Main:
  def func(a: Int, b: Int) : Int =
    val (lo,hi) = if (a > b) (b,a) else (a,b)
    - (hi - lo)/2

3 compile errors reported:

$ scalac3 -classpath /opt/uejlib3/vastblue_3.jar func.sc
-- [E008] Not Found Error: func.sc:4:4 -----------------------------------------------------------------------------------------------------------------------------------------------------------------
3 |    val (lo,hi) = if (a > b) (b,a) else (a,b)
4 |    - (hi - lo)/2
  |                                        ^
  |                                        value - is not a member of (Int, Int).
  |                                        Note that `-` is treated as an infix operator in Scala 3.
  |                                        If you do not want that, insert a `;` or empty line in front
  |                                        or drop any spaces behind the operator.
-- [E045] Cyclic Error: func.sc:3:4 --------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 |    val (lo,hi) = if (a > b) (b,a) else (a,b)
  |    ^
  |    Recursive value $1$ needs type

longer explanation available when compiling with `-explain`
-- [E007] Type Mismatch Error: func.sc:4:17 ------------------------------------------------------------------------------------------------------------------------------------------------------------
4 |    - (hi - lo)/2
  |                 ^
  |                 Found:    Unit
  |                 Required: Int

longer explanation available when compiling with `-explain`
3 errors found

This version compiles with no errors:

object IntFunc:
  def func(a: Int, b: Int) : Int =
    val (lo,hi) = if (a > b) (b,a) else (a,b)

    - (hi - lo)/2

Another fix is to remove the space following the minus sign on the last line:

object IntFunc:
  def func(a: Int, b: Int) : Int =
    val (lo,hi) = if (a > b) (b,a) else (a,b)
    -(hi - lo)/2

@odersky
Copy link
Contributor

odersky commented Aug 29, 2021

It's not related. It is in fact intentional that - continues the expression on the previous line.

@odersky odersky reopened this Aug 29, 2021
@som-snytt
Copy link
Contributor

Probably unintended to reopen.

@odersky
Copy link
Contributor

odersky commented Aug 30, 2021

Yes indeed.

@odersky odersky closed this as completed Aug 30, 2021
@Kordyjan Kordyjan added this to the 3.1.0 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 a pull request may close this issue.

4 participants