Skip to content

Commit 6c66a8d

Browse files
committed
interpreter: Fix variable mutation in block
When mutating a variable from a block, the interpreter would call the `replace_variable` method, which would get a reference to the variable, remove it, and then add a new one. However, this method wasn't scope-map aware: While we were removing the correct variable from the outtermost scope, we were adding the new one in the inner scope. An easy fix is to instead mutate the variable we were originally getting.
1 parent 1babd52 commit 6c66a8d

File tree

6 files changed

+83
-4
lines changed

6 files changed

+83
-4
lines changed

src/context.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,12 @@ impl Context {
198198

199199
/// Replace a variable or create it if it does not exist
200200
pub fn replace_variable(&mut self, var: Var) -> Result<(), Error> {
201-
// Remove the variable if it exists
202-
let _ = self.remove_variable(&var);
201+
match self.scope_map.get_variable_mut(var.name()) {
202+
None => self.add_variable(var)?,
203+
Some(var_ref) => var_ref.set_instance(var.instance()),
204+
}
203205

204-
self.add_variable(var)
206+
Ok(())
205207
}
206208

207209
/// Get a mutable reference on an existing function

src/context/scope_map.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ impl<V, F, T> Scope<V, F, T> {
2222
self.variables.get(name)
2323
}
2424

25+
/// Get a mutable reference on a variable from the scope map if it has been inserted already
26+
pub fn get_variable_mut(&mut self, name: &str) -> Option<&mut V> {
27+
self.variables.get_mut(name)
28+
}
29+
2530
/// Get a reference on a function from the scope map if is has been inserted already
2631
pub fn get_function(&self, name: &str) -> Option<&F> {
2732
self.functions.get(name)
@@ -144,6 +149,17 @@ impl<V, F, T> ScopeMap<V, F, T> {
144149
None
145150
}
146151

152+
pub fn get_variable_mut(&mut self, name: &str) -> Option<&mut V> {
153+
for scope in self.scopes.iter_mut() {
154+
match scope.get_variable_mut(name) {
155+
Some(v) => return Some(v),
156+
None => continue,
157+
};
158+
}
159+
160+
None
161+
}
162+
147163
/// Maybe get a function in any available scopes
148164
pub fn get_function(&self, name: &str) -> Option<&F> {
149165
// FIXME: Use find for code quality?

src/instruction/var_assignment.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,10 @@ impl TypeCheck for VarAssign {
157157
#[cfg(test)]
158158
mod tests {
159159
use super::*;
160-
use crate::jinko_fail;
161160
use crate::parser::{Construct, Token};
162161
use crate::value::{JkInt, JkString};
163162
use crate::ToObjectInstance;
163+
use crate::{jinko, jinko_fail};
164164

165165
#[test]
166166
fn non_mutable() {
@@ -223,4 +223,36 @@ mod tests {
223223
mut a0 = 14;
224224
};
225225
}
226+
227+
#[test]
228+
fn assign_mutable_in_block_187() {
229+
let ctx = jinko! {
230+
mut x = 1;
231+
{
232+
x = 0;
233+
}
234+
};
235+
236+
assert_eq!(
237+
ctx.get_variable("x").unwrap().instance(),
238+
JkInt::from(0).to_instance()
239+
);
240+
}
241+
242+
#[test]
243+
fn assign_mutable_in_function_187() {
244+
let ctx = jinko! {
245+
mut x = 1;
246+
func change_global() {
247+
x = 0;
248+
}
249+
250+
change_global()
251+
};
252+
253+
assert_eq!(
254+
ctx.get_variable("x").unwrap().instance(),
255+
JkInt::from(0).to_instance()
256+
);
257+
}
226258
}

tests/ft/regression/187.jk

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
mut x = 1;
2+
{
3+
x = 0;
4+
}
5+
6+
x

tests/ft/regression/187_fn.jk

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
mut global_x = 1;
2+
3+
func mutate_global() {
4+
func inner() {
5+
global_x = 0;
6+
}
7+
8+
inner()
9+
}
10+
11+
mutate_global();
12+
13+
x

tests/ft/regression/regression.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,13 @@ tests:
44
args:
55
- "tests/ft/regression/string_double_free.jk"
66
exit_code: 0
7+
- name: "Variable mutation in block #187"
8+
binary: "target/debug/jinko"
9+
args:
10+
- "tests/ft/regression/187.jk"
11+
exit_code: 0
12+
- name: "Variable mutation in function #187"
13+
binary: "target/debug/jinko"
14+
args:
15+
- "tests/ft/regression/187_fn.jk"
16+
exit_code: 0

0 commit comments

Comments
 (0)