From 6648126cc388a6513ccb774b92f3b4e47ad17457 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Tue, 19 Aug 2025 09:14:11 +0100 Subject: [PATCH] fix: allow postfix setters under language.postfixOps (#23775) Allow for postfix operators to be followed by assigns. This enables the definition and use of the following syntax (more precisely the parsing of the `>_=` method as a `postfix operator + assign`): ```scala val v = new Vector(1, 2, 3) println(v) // prints <1, 2, 3> v<1> = 10 // assign 10 to element at index 1 println(v) // prints <1, 10, 3> println(v<1>) // prints: value at 1 is 10 // Definition of Vector: class Vector(values: Int*) { val data = values.toArray class Getter(i: Int) { def `>_=`(x: Int) = data(i) = x def > : Int = data(i) } def < (i:Int) = new Getter(i) override def toString = data.mkString("<", ", ", ">") } ``` [Cherry-picked de18af4dc1ea9c66cb98bfda80fa167d0112800c] --- .../dotty/tools/dotc/parsing/Parsers.scala | 3 ++- docs/_docs/internals/syntax.md | 1 + docs/_docs/reference/syntax.md | 1 + tests/pos/vec-access-syntax.scala | 23 +++++++++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/pos/vec-access-syntax.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 03bba910e348..a52ee313fefa 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2179,6 +2179,7 @@ object Parsers { * | ForExpr * | [SimpleExpr `.'] id `=' Expr * | PrefixOperator SimpleExpr `=' Expr + * | InfixExpr id [nl] `=' Expr -- only if language.postfixOps is enabled * | SimpleExpr1 ArgumentExprs `=' Expr * | PostfixExpr [Ascription] * | ‘inline’ InfixExpr MatchClause @@ -2339,7 +2340,7 @@ object Parsers { def expr1Rest(t: Tree, location: Location): Tree = if in.token == EQUALS then t match - case Ident(_) | Select(_, _) | Apply(_, _) | PrefixOp(_, _) => + case Ident(_) | Select(_, _) | Apply(_, _) | PrefixOp(_, _) | PostfixOp(_, _) => atSpan(startOffset(t), in.skipToken()) { val loc = if location.inArgs then location else Location.ElseWhere Assign(t, subPart(() => expr(loc))) diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index ae6d85c7deae..2188f4ee0d90 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -240,6 +240,7 @@ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[ | ForExpr | [SimpleExpr ‘.’] id ‘=’ Expr Assign(expr, expr) | PrefixOperator SimpleExpr ‘=’ Expr Assign(expr, expr) + | InfixExpr id [nl] `=' Expr Assign(expr, expr) -- only if language.postfixOps is enabled | SimpleExpr ArgumentExprs ‘=’ Expr Assign(expr, expr) | PostfixExpr [Ascription] | ‘inline’ InfixExpr MatchClause diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index be64abb07119..f3bb516de8b2 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -240,6 +240,7 @@ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[ | ForExpr | [SimpleExpr ‘.’] id ‘=’ Expr | PrefixOperator SimpleExpr ‘=’ Expr + | InfixExpr id [nl] `=' Expr -- only if language.postfixOps is enabled | SimpleExpr ArgumentExprs ‘=’ Expr | PostfixExpr [Ascription] | ‘inline’ InfixExpr MatchClause diff --git a/tests/pos/vec-access-syntax.scala b/tests/pos/vec-access-syntax.scala new file mode 100644 index 000000000000..524ede685529 --- /dev/null +++ b/tests/pos/vec-access-syntax.scala @@ -0,0 +1,23 @@ +import scala.language.postfixOps + +class Vector(values: Int*) { + val data = values.toArray + class Getter(i: Int) { + def `>_=`(x: Int) = + data(i) = x + def > : Int = + data(i) + } + def < (i:Int) = new Getter(i) + override def toString = data.mkString("<", ", ", ">") +} + +object Test { + def main(args: Array[String]): Unit = { + val v = new Vector(1, 2, 3) + println(v) // prints <1, 2, 3> + v<1> = 10 // assign 10 to element at index 1 + println(v) // prints <1, 10, 3> + println(v<1>) // prints: value at 1 is 10 + } +}