Skip to content

Commit 240c1e4

Browse files
Merge pull request RustPython#839 from RustPython/nonlocal
Add nonlocal support.
2 parents 75e8f81 + 6d1b807 commit 240c1e4

File tree

4 files changed

+28
-2
lines changed

4 files changed

+28
-2
lines changed

tests/snippets/global_nonlocal.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,13 @@ def b():
1111
b()
1212
assert a == 4
1313

14+
def x():
15+
def y():
16+
nonlocal b
17+
b = 3
18+
b = 2
19+
y()
20+
return b
21+
22+
res = x()
23+
assert res == 3, str(res)

vm/src/bytecode.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub type Label = usize;
4141
#[derive(Debug, Clone, PartialEq)]
4242
pub enum NameScope {
4343
Local,
44+
NonLocal,
4445
Global,
4546
}
4647

vm/src/compile.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,12 @@ impl Compiler {
179179
let role = self.lookup_name(name);
180180
match role {
181181
SymbolRole::Global => bytecode::NameScope::Global,
182+
SymbolRole::Nonlocal => bytecode::NameScope::NonLocal,
182183
_ => bytecode::NameScope::Local,
183184
}
184185
}
185186

186187
fn load_name(&mut self, name: &str) {
187-
// TODO: if global, do something else!
188188
let scope = self.scope_for_name(name);
189189
self.emit(Instruction::LoadName {
190190
name: name.to_string(),

vm/src/frame.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ pub trait NameProtocol {
126126
fn store_name(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
127127
fn delete_name(&self, vm: &VirtualMachine, name: &str);
128128
fn load_cell(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
129+
fn store_cell(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
129130
fn load_global(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
130131
fn store_global(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
131132
}
@@ -154,6 +155,16 @@ impl NameProtocol for Scope {
154155
None
155156
}
156157

158+
fn store_cell(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef) {
159+
self.locals
160+
.iter()
161+
.skip(1)
162+
.next()
163+
.expect("no outer scope for non-local")
164+
.set_item(name, value, vm)
165+
.unwrap();
166+
}
167+
157168
fn store_name(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) {
158169
self.get_locals().set_item(key, value, vm).unwrap();
159170
}
@@ -1029,8 +1040,11 @@ impl Frame {
10291040
bytecode::NameScope::Global => {
10301041
self.scope.store_global(vm, name, obj);
10311042
}
1043+
bytecode::NameScope::NonLocal => {
1044+
self.scope.store_cell(vm, name, obj);
1045+
}
10321046
bytecode::NameScope::Local => {
1033-
self.scope.store_name(&vm, name, obj);
1047+
self.scope.store_name(vm, name, obj);
10341048
}
10351049
}
10361050
Ok(None)
@@ -1049,6 +1063,7 @@ impl Frame {
10491063
) -> FrameResult {
10501064
let optional_value = match name_scope {
10511065
bytecode::NameScope::Global => self.scope.load_global(vm, name),
1066+
bytecode::NameScope::NonLocal => self.scope.load_cell(vm, name),
10521067
bytecode::NameScope::Local => self.scope.load_name(&vm, name),
10531068
};
10541069

0 commit comments

Comments
 (0)