diff --git a/bytecode/src/bytecode.rs b/bytecode/src/bytecode.rs index ecb834272..93f799c89 100644 --- a/bytecode/src/bytecode.rs +++ b/bytecode/src/bytecode.rs @@ -243,6 +243,7 @@ pub enum Instruction { BuildMap { size: usize, unpack: bool, + for_call: bool, }, BuildSlice { size: usize, @@ -519,7 +520,11 @@ impl Instruction { BuildTuple { size, unpack } => w!(BuildTuple, size, unpack), BuildList { size, unpack } => w!(BuildList, size, unpack), BuildSet { size, unpack } => w!(BuildSet, size, unpack), - BuildMap { size, unpack } => w!(BuildMap, size, unpack), + BuildMap { + size, + unpack, + for_call, + } => w!(BuildMap, size, unpack, for_call), BuildSlice { size } => w!(BuildSlice, size), ListAppend { i } => w!(ListAppend, i), SetAdd { i } => w!(SetAdd, i), diff --git a/compiler/src/compile.rs b/compiler/src/compile.rs index a7e81460b..c457ae8e9 100644 --- a/compiler/src/compile.rs +++ b/compiler/src/compile.rs @@ -658,6 +658,7 @@ impl Compiler { self.emit(Instruction::BuildMap { size: num_kw_only_defaults, unpack: false, + for_call: false, }); } @@ -896,6 +897,7 @@ impl Compiler { self.emit(Instruction::BuildMap { size: num_annotations, unpack: false, + for_call: false, }); } @@ -1464,6 +1466,7 @@ impl Compiler { self.emit(Instruction::BuildMap { size: subsize, unpack: false, + for_call: false, }); size += 1; } @@ -1472,10 +1475,15 @@ impl Compiler { self.emit(Instruction::BuildMap { size, unpack: false, + for_call: false, }); } if size > 1 || has_unpacking { - self.emit(Instruction::BuildMap { size, unpack: true }); + self.emit(Instruction::BuildMap { + size, + unpack: true, + for_call: false, + }); } Ok(()) } @@ -1704,12 +1712,17 @@ impl Compiler { self.emit(Instruction::BuildMap { size: subsize, unpack: false, + for_call: false, }); size += 1; } } if size > 1 { - self.emit(Instruction::BuildMap { size, unpack: true }); + self.emit(Instruction::BuildMap { + size, + unpack: true, + for_call: true, + }); } Ok(()) } @@ -1855,6 +1868,7 @@ impl Compiler { self.emit(Instruction::BuildMap { size: 0, unpack: false, + for_call: false, }); } } diff --git a/tests/snippets/function_args.py b/tests/snippets/function_args.py index 7c2663e3f..d1754a62f 100644 --- a/tests/snippets/function_args.py +++ b/tests/snippets/function_args.py @@ -105,3 +105,12 @@ with assert_raises(SyntaxError): with assert_raises(SyntaxError): exec("def f(a=1, b): pass") + + +def f(a): + pass + +x = {'a': 1} +y = {'a': 2} +with assert_raises(TypeError): + f(**x, **y) diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 3e785fcc7..97161f3f3 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -284,9 +284,11 @@ impl Frame { self.push_value(list_obj); Ok(None) } - bytecode::Instruction::BuildMap { size, unpack } => { - self.execute_build_map(vm, *size, *unpack) - } + bytecode::Instruction::BuildMap { + size, + unpack, + for_call, + } => self.execute_build_map(vm, *size, *unpack, *for_call), bytecode::Instruction::BuildSlice { size } => self.execute_build_slice(vm, *size), bytecode::Instruction::ListAppend { i } => { let list_obj = self.nth_value(*i); @@ -809,13 +811,29 @@ impl Frame { Ok(None) } - fn execute_build_map(&self, vm: &VirtualMachine, size: usize, unpack: bool) -> FrameResult { + fn execute_build_map( + &self, + vm: &VirtualMachine, + size: usize, + unpack: bool, + for_call: bool, + ) -> FrameResult { let map_obj = vm.ctx.new_dict(); if unpack { for obj in self.pop_multiple(size) { // Take all key-value pairs from the dict: let dict: PyDictRef = obj.downcast().expect("Need a dictionary to build a map."); for (key, value) in dict { + if for_call { + if map_obj.contains_key(&key, vm) { + let key_repr = vm.to_repr(&key)?; + let msg = format!( + "got multiple values for keyword argument {}", + key_repr.as_str() + ); + return Err(vm.new_type_error(msg)); + } + } map_obj.set_item(&key, value, vm).unwrap(); } }