Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1551,17 +1551,25 @@ class AmbiguousExtensionMethod(tree: untpd.Tree, expansion1: tpd.Tree, expansion
|are possible expansions of $tree"""
def explain(using Context) = ""

class ReassignmentToVal(name: Name)(using Context)
class ReassignmentToVal(name: Name, pt: Type)(using Context)
extends TypeMsg(ReassignmentToValID) {
def msg(using Context) = i"""Reassignment to val $name"""
def explain(using Context) =
i"""|You can not assign a new value to $name as values can't be changed.
|Keep in mind that every statement has a value, so you may e.g. use
| ${hl("val")} $name ${hl("= if (condition) 2 else 5")}
|In case you need a reassignable name, you can declare it as
|variable
| ${hl("var")} $name ${hl("=")} ...
|"""

def booleanExpected = pt.isRef(defn.BooleanClass)
def booleanNote =
if booleanExpected then
". Maybe you meant to write an equality test using `==`?"
Copy link
Member

Choose a reason for hiding this comment

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

or, for example, I intended to do <= for integers. I agree that having suggestions helps but I don't see where we draw the line for them. Going further, we could investigate further based on the types of lhs and rhs and suggest all the (2 characters methods ending (and starting?) with = that are compatible members)?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure, just what did you mean would suggest for selections. did you mean Int.!=? or perhaps Int.<=? etc.

Copy link
Contributor Author

@odersky odersky Oct 15, 2025

Choose a reason for hiding this comment

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

Surely not. The suggestion is for a beginner who comes from a context where = means equality. We have to special case for this and not pollute the message with other suggestions.

We just had a talk here were someone proposed to introduce an educational subset of Scala because the students were too much put off by the current error message. That's the issue this PR addresses.

else ""
def msg(using Context)
= i"""Reassignment to val $name$booleanNote"""

def explain(using Context) =
if booleanExpected then
i"""An equality test is written "x == y" using `==`. A single `=` stands
|for assigment, where a variable on the left gets a new value on the right.
|This is only permitted if the variable is declared with `var`."""
else
i"""You can not assign a new value to $name as values can't be changed.
|Reassigment is only permitted if the variable is declared with `var`."""
}

class TypeDoesNotTakeParameters(tpe: Type, params: List[untpd.Tree])(using Context)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Dynamic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ trait Dynamic {
case TypeApply(sel @ Select(qual, name), targs) if !isDynamicMethod(name) =>
typedDynamicAssign(qual, name, sel.span, targs)
case _ =>
errorTree(tree, ReassignmentToVal(tree.lhs.symbol.name))
errorTree(tree, ReassignmentToVal(tree.lhs.symbol.name, pt))
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1429,7 +1429,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
def lhs1 = adapt(lhsCore, LhsProto, locked)

def reassignmentToVal =
report.error(ReassignmentToVal(lhsCore.symbol.name), tree.srcPos)
report.error(ReassignmentToVal(lhsCore.symbol.name, pt), tree.srcPos)
cpy.Assign(tree)(lhsCore, typed(tree.rhs, lhs1.tpe.widen)).withType(defn.UnitType)

def canAssign(sym: Symbol) =
Expand Down
53 changes: 53 additions & 0 deletions tests/neg/reassignment.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
-- [E052] Type Error: tests/neg/reassignment.scala:4:10 ----------------------------------------------------------------
4 |val y = x = 0 // error
| ^^^^^
| Reassignment to val x
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| You can not assign a new value to x as values can't be changed.
| Reassigment is only permitted if the variable is declared with `var`.
---------------------------------------------------------------------------------------------------------------------
-- [E052] Type Error: tests/neg/reassignment.scala:5:19 ----------------------------------------------------------------
5 |val z: Boolean = x = 0 // error
| ^^^^^
| Reassignment to val x. Maybe you meant to write an equality test using `==`?
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| An equality test is written "x == y" using `==`. A single `=` stands
| for assigment, where a variable on the left gets a new value on the right.
| This is only permitted if the variable is declared with `var`.
---------------------------------------------------------------------------------------------------------------------
-- [E052] Type Error: tests/neg/reassignment.scala:6:13 ----------------------------------------------------------------
6 |def f = if x = 0 then 1 else 2 // error
| ^^^^^
| Reassignment to val x. Maybe you meant to write an equality test using `==`?
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| An equality test is written "x == y" using `==`. A single `=` stands
| for assigment, where a variable on the left gets a new value on the right.
| This is only permitted if the variable is declared with `var`.
---------------------------------------------------------------------------------------------------------------------
-- [E052] Type Error: tests/neg/reassignment.scala:8:4 -----------------------------------------------------------------
8 | x = 0 // error
| ^^^^^
| Reassignment to val x
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| You can not assign a new value to x as values can't be changed.
| Reassigment is only permitted if the variable is declared with `var`.
---------------------------------------------------------------------------------------------------------------------
-- [E052] Type Error: tests/neg/reassignment.scala:9:4 -----------------------------------------------------------------
9 | x = 2 // error
| ^^^^^
| Reassignment to val x. Maybe you meant to write an equality test using `==`?
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| An equality test is written "x == y" using `==`. A single `=` stands
| for assigment, where a variable on the left gets a new value on the right.
| This is only permitted if the variable is declared with `var`.
---------------------------------------------------------------------------------------------------------------------
10 changes: 10 additions & 0 deletions tests/neg/reassignment.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//> using options -explain

val x = 1
val y = x = 0 // error
val z: Boolean = x = 0 // error
def f = if x = 0 then 1 else 2 // error
def g: Boolean =
x = 0 // error
x = 2 // error