-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Allow prefix operators on the LHS of assignments #13328
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
Changes from 3 commits
5939849
6b04f15
614812b
1010692
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1886,6 +1886,7 @@ object Parsers { | |
* | `return' [Expr] | ||
* | ForExpr | ||
* | [SimpleExpr `.'] id `=' Expr | ||
* | PrefixOperator SimpleExpr `=' Expr | ||
* | SimpleExpr1 ArgumentExprs `=' Expr | ||
* | PostfixExpr [Ascription] | ||
* | ‘inline’ InfixExpr MatchClause | ||
|
@@ -2045,7 +2046,7 @@ object Parsers { | |
def expr1Rest(t: Tree, location: Location): Tree = in.token match | ||
case EQUALS => | ||
t match | ||
case Ident(_) | Select(_, _) | Apply(_, _) => | ||
case Ident(_) | Select(_, _) | Apply(_, _) | PrefixOp(_, _) => | ||
atSpan(startOffset(t), in.skipToken()) { | ||
val loc = if location.inArgs then location else Location.ElseWhere | ||
Assign(t, subPart(() => expr(loc))) | ||
|
@@ -2206,8 +2207,9 @@ object Parsers { | |
isOperator = !(location.inArgs && followingIsVararg()), | ||
maybePostfix = true) | ||
|
||
/** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr | ||
*/ | ||
/** PrefixExpr ::= [PrefixOperator'] SimpleExpr | ||
* PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
class Foo(var value: Int):
def `unary_+_=`(value: Int): Unit = this.value = +value
def `unary_-_=`(value: Int): Unit = this.value = -value
def `unary_~_=`(value: Int): Unit = this.value = ~value
end Foo
def test =
val x = Foo(9)
+x = 10 // error: value unary_+ is not a member of Foo - did you mean x.unary_+_=?
-x = 10 // error: value unary_- is not a member of Foo, but could be made available as an extension method.
~x = 10 // error: value unary_~ is not a member of Foo - did you mean x.unary_~_=? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's because Foo is missing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does that mean that we cannot write There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, exactly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? Do we typecheck There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something like that, yes. Using a setter |
||
*/ | ||
val prefixExpr: Location => Tree = location => | ||
if isIdent && nme.raw.isUnary(in.name) | ||
&& in.canStartExprTokens.contains(in.lookahead.token) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ final class Baz private (val x: Int) extends AnyVal { | |
def unary_- : Baz = ??? | ||
def unary_+[T] : Baz = ??? | ||
def unary_!() : Baz = ??? // error | ||
def `unary_!_=`() : Baz = ??? // ok | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file tests that the definitions of unary operators are well defined. If not an error is emitted.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is a full version of the equivalent test case for Test case
tests/neg-custom-args/fatal-warnings/i9241b.scala class Foo {
def `unary_!_=`() : Foo = this // error
def `unary_+_=`(using Int)(): Foo = this // error
def `unary_-_=`()(implicit i: Int): Foo = this // error
def `unary_~_=`[T](): Foo = this // error
}
class Bar {
def `unary_!_=`(value: Int) : Baz = ???
def `unary_-_=`[T](value: T)(using T): Baz = ???
def `unary_~_=`() : Baz = ??? // error
def `unary_+_=`(value: Int, value2: Int) : Baz = ??? // error
}
final class Baz private (val x: Int) extends AnyVal {
def `unary_!_=`() : Baz = ??? // error
def `unary_+_=`(value: Int) : Baz = ???
}
extension (x: Int)
def `unary_!_=` : Int = ??? // error
def `unary_+_=`[T] : Int = ??? // error
def `unary_-_=`() : Int = ??? // error
def `unary_~_=`(using Int) : Int = ??? // error
end extension
extension (x: Long)
def `unary_!_=`(value: Int): Int = ???
def `unary_+_=`[T](value: T) : Int = ???
def `unary_-_=`(value: Int) : Int = ???
def `unary_~_=`(value: Int)(using Int) : Int = ???
end extension
extension [T](x: Byte)
def `unary_!_=` : Int = ??? // error
def `unary_+_=`[U] : Int = ??? // error
def `unary_-_=`() : Int = ??? // error
def `unary_~_=`(using Int) : Int = ??? // error
end extension
extension [T](x: Short)
def `unary_!_=`(value: Int): Int = ???
def `unary_+_=`[U](value: T) : Int = ???
def `unary_-_=`(value: Int) : Int = ???
def `unary_~_=`(value: Int)(using Int) : Int = ???
end extension
extension (using Int)(x: Float)
def `unary_!_=` : Int = ??? // error
def `unary_+_=`[T] : Int = ??? // error
def `unary_-_=`() : Int = ??? // error
def `unary_~_=`(using Int) : Int = ??? // error
end extension
extension (using Int)(x: Double)
def `unary_!_=`(value: Int): Int = ???
def `unary_+_=`[T](value: T) : Int = ???
def `unary_-_=`(value: Int) : Int = ???
def `unary_~_=`(value: Int)(using Int) : Int = ???
end extension There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just wanted to make sure and show that we can now parse this. We can break it out in a different test, if you think that's better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then this is not the tests file where this should go. It will be extremely misleading to the next person that looks at the test. I suggest we put its own test file and include a comment that says that we can define it but we will not be able to use it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can enhance the i9241 checks in a later PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved the test and will add the checks in a subsequent PR. |
||
def unary_~(using Int) : Baz = ??? | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
class Ptr[T](var value: T): | ||
def `unary_!` : T = value | ||
def `unary_!_=`(value: T): Unit = this.value = value | ||
end Ptr | ||
|
||
def test = | ||
val x = Ptr(9) | ||
!x = 10 | ||
println(!x) |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What must have happened in Scala 2: We had similar logic here, but we did not mark prefix operations specially; so a prefix operation was accepted since it was represented by a
Select
. Similarly for infix. The following expression is parsed in Scala 2, but will get a type error afterwards:In dotc you get instead a parse error:
That's because we represent infix operations as
Infix
nodes instead ofApply
nodes.