Skip to content
Merged
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
18 changes: 18 additions & 0 deletions manual/src/reference/variables-and-scopes.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,24 @@ let x = {
print(x); // 3
```

## Reassignment

The `=` operator can be used to reassign a value to an existing variable. When you reassign a variable to a value of a different type, the variable's type is widened to the least upper bound (LUB) of the old and new types.

```ndc
let x = 1; // type is Int
x = 2; // type is still Int
x = 3.14; // type widens to Number (LUB of Int and Float)
```

```ndc
let pos = (); // type is ()
pos = (1, 2); // type widens to Sequence<Any>
pos = ("a", "b"); // type is still Sequence<Any>
```

> **Tip:** For the best type inference, initialize variables with a value that matches the intended type. For example, use `let pos = (0, 0);` instead of `let pos = ();` if you intend to store a 2-tuple of numbers.

## Destructuring

Destructuring is more similar to how it works in python and cares mostly about where commas are and not so much about the delimiters (`[]`, `()`) used.
Expand Down
18 changes: 16 additions & 2 deletions ndc_analyser/src/analyser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,22 @@ impl Analyser {
Ok(StaticType::unit())
}
Expression::Assignment { l_value, r_value } => {
self.resolve_lvalue(l_value, *span)?;
self.analyse(r_value)?;
let old_type = self.resolve_lvalue(l_value, *span)?;
let new_type = self.analyse(r_value)?;

// Widen the binding's type to the LUB so subsequent uses
// see the broader type.
if let Lvalue::Identifier {
resolved: Some(target),
..
} = l_value
{
let widened = old_type.lub(&new_type);
if widened != old_type {
self.scope_tree.update_binding_type(*target, widened);
}
}

Ok(StaticType::unit())
}
Expression::OpAssignment {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Destructuring let where the variable was declared as () but later
// reassigned to a 2-tuple should not cause a false "not declared" error.
// The analyser widens the type via LUB on reassignment.
// expect-output: 4

let pos = ();
Expand Down
Loading