diff --git a/kclvm/compiler/src/codegen/llvm/context.rs b/kclvm/compiler/src/codegen/llvm/context.rs index 663fe01b9..f56f87f93 100644 --- a/kclvm/compiler/src/codegen/llvm/context.rs +++ b/kclvm/compiler/src/codegen/llvm/context.rs @@ -1717,40 +1717,32 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { level } - /// Append a variable or update the existed local variable. - pub fn add_or_update_local_variable(&self, name: &str, value: BasicValueEnum<'ctx>) { + /// Append a variable or update the existed local variable within the current scope. + pub fn add_or_update_local_variable_within_scope( + &self, + name: &str, + value: BasicValueEnum<'ctx>, + ) { let current_pkgpath = self.current_pkgpath(); let mut pkg_scopes = self.pkg_scopes.borrow_mut(); let msg = format!("pkgpath {} is not found", current_pkgpath); let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); - let mut existed = false; - // Query the variable in all scopes. - for i in 0..scopes.len() { - let index = scopes.len() - i - 1; - let variables_mut = scopes[index].variables.borrow_mut(); + let index = scopes.len() - 1; + if let Some(scope) = scopes.last_mut() { + let mut variables_mut = scope.variables.borrow_mut(); match variables_mut.get(&name.to_string()) { // If the local variable is found, store the new value for the variable. // We cannot update rule/lambda/schema arguments because they are read-only. - Some(ptr) - if index > GLOBAL_LEVEL - && !self.local_vars.borrow().contains(name) - && !scopes[index].arguments.borrow().contains(name) => - { + Some(ptr) if index > GLOBAL_LEVEL => { self.builder.build_store(*ptr, value); - existed = true; } - _ => {} - } - } - // If not found, alloc a new variable. - if !existed { - let ptr = self.builder.build_alloca(self.value_ptr_type(), name); - self.builder.build_store(ptr, value); - // Store the value for the variable and add the variable into the current scope. - if let Some(last) = scopes.last_mut() { - let mut variables = last.variables.borrow_mut(); - variables.insert(name.to_string(), ptr); - } + _ => { + let ptr = self.builder.build_alloca(self.value_ptr_type(), name); + self.builder.build_store(ptr, value); + // Store the value for the variable and add the variable into the current scope. + variables_mut.insert(name.to_string(), ptr); + } + }; } } diff --git a/kclvm/compiler/src/codegen/llvm/node.rs b/kclvm/compiler/src/codegen/llvm/node.rs index 1e2c63afc..b2545035c 100644 --- a/kclvm/compiler/src/codegen/llvm/node.rs +++ b/kclvm/compiler/src/codegen/llvm/node.rs @@ -1972,9 +1972,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { config_if_entry_expr: &'ctx ast::ConfigIfEntryExpr, ) -> Self::Result { check_backtrack_stop!(self); - let cond = self - .walk_expr(&config_if_entry_expr.if_cond) - .expect(kcl_error::COMPILE_ERROR_MSG); + let cond = self.walk_expr(&config_if_entry_expr.if_cond)?; let then_block = self.append_block(""); let else_block = self.append_block(""); let end_block = self.append_block(""); @@ -1992,7 +1990,12 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { self.br(end_block); self.builder.position_at_end(else_block); let else_value = if let Some(orelse) = &config_if_entry_expr.orelse { - self.walk_expr(orelse).expect(kcl_error::COMPILE_ERROR_MSG) + // Config or config if entry expr. + if let ast::Expr::Config(config_expr) = &orelse.node { + self.walk_config_entries(&config_expr.items)? + } else { + self.walk_expr(orelse)? + } } else { self.none_value() }; @@ -2076,7 +2079,10 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { fn walk_config_expr(&self, config_expr: &'ctx ast::ConfigExpr) -> Self::Result { check_backtrack_stop!(self); - self.walk_config_entries(&config_expr.items) + self.enter_scope(); + let result = self.walk_config_entries(&config_expr.items); + self.leave_scope(); + result } fn walk_check_expr(&self, check_expr: &'ctx ast::CheckExpr) -> Self::Result { @@ -2830,7 +2836,6 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { items: &'ctx [NodeRef], ) -> CompileResult<'ctx> { let config_value = self.dict_value(); - self.enter_scope(); for item in items { let value = self.walk_expr(&item.node.value)?; if let Some(key) = &item.node.key { @@ -2869,7 +2874,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { if let Some(name) = &optional_name { let value = self.dict_get(config_value, self.native_global_string(name, "").into()); - self.add_or_update_local_variable(name, value); + self.add_or_update_local_variable_within_scope(name, value); } } else { // If the key does not exist, execute the logic of unpacking expression `**expr` here. @@ -2879,7 +2884,6 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { ); } } - self.leave_scope(); Ok(config_value) } } diff --git a/kclvm/evaluator/src/node.rs b/kclvm/evaluator/src/node.rs index 9dd43367e..f6edfdbac 100644 --- a/kclvm/evaluator/src/node.rs +++ b/kclvm/evaluator/src/node.rs @@ -836,7 +836,12 @@ impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { Ok(if is_truth { self.walk_config_entries(&config_if_entry_expr.items)? } else if let Some(orelse) = &config_if_entry_expr.orelse { - self.walk_expr(orelse)? + // Config or config if entry expr. + if let ast::Expr::Config(config_expr) = &orelse.node { + self.walk_config_entries(&config_expr.items)? + } else { + self.walk_expr(orelse)? + } } else { self.none_value() }) @@ -927,7 +932,10 @@ impl<'ctx> TypedResultWalker<'ctx> for Evaluator<'ctx> { #[inline] fn walk_config_expr(&self, config_expr: &'ctx ast::ConfigExpr) -> Self::Result { - self.walk_config_entries(&config_expr.items) + self.enter_scope(); + let result = self.walk_config_entries(&config_expr.items); + self.leave_scope(); + result } fn walk_check_expr(&self, check_expr: &'ctx ast::CheckExpr) -> Self::Result { @@ -1509,7 +1517,6 @@ impl<'ctx> Evaluator<'ctx> { pub(crate) fn walk_config_entries(&self, items: &'ctx [NodeRef]) -> EvalResult { let mut config_value = self.dict_value(); - self.enter_scope(); for item in items { let value = self.walk_expr(&item.node.value)?; if let Some(key) = &item.node.key { @@ -1547,14 +1554,13 @@ impl<'ctx> Evaluator<'ctx> { ); if let Some(name) = &optional_name { let value = self.dict_get_value(&config_value, name); - self.add_or_update_local_variable(name, value); + self.add_or_update_local_variable_within_scope(name, value); } } else { // If the key does not exist, execute the logic of unpacking expression `**expr` here. config_value.dict_insert_unpack(&mut self.runtime_ctx.borrow_mut(), &value) } } - self.leave_scope(); Ok(config_value) } } diff --git a/kclvm/evaluator/src/scope.rs b/kclvm/evaluator/src/scope.rs index de1acc450..1473ce882 100644 --- a/kclvm/evaluator/src/scope.rs +++ b/kclvm/evaluator/src/scope.rs @@ -301,34 +301,17 @@ impl<'ctx> Evaluator<'ctx> { level } - /// Append a variable or update the existed local variable. - pub fn add_or_update_local_variable(&self, name: &str, value: ValueRef) { + /// Append a variable or update the existed local variable within the current scope. + pub(crate) fn add_or_update_local_variable_within_scope(&self, name: &str, value: ValueRef) { let current_pkgpath = self.current_pkgpath(); - let is_local_var = self.is_local_var(name); let pkg_scopes = &mut self.pkg_scopes.borrow_mut(); let msg = format!("pkgpath {} is not found", current_pkgpath); let scopes = pkg_scopes.get_mut(¤t_pkgpath).expect(&msg); - let mut existed = false; - // Query the variable in all scopes. - for i in 0..scopes.len() { - let index = scopes.len() - i - 1; - let is_argument = scopes[index].arguments.contains(name); - let variables_mut = &mut scopes[index].variables; - match variables_mut.get(&name.to_string()) { - // If the local variable is found, store the new value for the variable. - // We cannot update rule/lambda/schema arguments because they are read-only. - Some(_) if index > GLOBAL_LEVEL && !is_local_var && !is_argument => { - variables_mut.insert(name.to_string(), value.clone()); - existed = true; - } - _ => {} - } - } - // If not found, alloc a new variable. - if !existed { - // Store the value for the variable and add the variable into the current scope. - if let Some(last) = scopes.last_mut() { - last.variables.insert(name.to_string(), value); + let index = scopes.len() - 1; + if let Some(scope) = scopes.last_mut() { + let variables_mut = &mut scope.variables; + if index > GLOBAL_LEVEL { + variables_mut.insert(name.to_string(), value.clone()); } } } diff --git a/test/grammar/datatype/dict/mutual_ref_15/main.k b/test/grammar/datatype/dict/mutual_ref_15/main.k new file mode 100644 index 000000000..88cbe423e --- /dev/null +++ b/test/grammar/datatype/dict/mutual_ref_15/main.k @@ -0,0 +1,24 @@ +render = lambda { + a = { + foo: "bar" + } + b = { + foo2: "bar2" + a: { + b: "c" + } + } + c = [a, b] +} + +out = render() +a = { + foo: "bar" +} +b = { + foo2: "bar2" + a: { + b: "c" + } +} +c = [a, b] diff --git a/test/grammar/datatype/dict/mutual_ref_15/stdout.golden b/test/grammar/datatype/dict/mutual_ref_15/stdout.golden new file mode 100644 index 000000000..565be56aa --- /dev/null +++ b/test/grammar/datatype/dict/mutual_ref_15/stdout.golden @@ -0,0 +1,16 @@ +out: +- foo: bar +- foo2: bar2 + a: + b: c +a: + foo: bar +b: + foo2: bar2 + a: + b: c +c: +- foo: bar +- foo2: bar2 + a: + b: c diff --git a/test/grammar/datatype/dict/mutual_ref_16/main.k b/test/grammar/datatype/dict/mutual_ref_16/main.k new file mode 100644 index 000000000..123f7c76c --- /dev/null +++ b/test/grammar/datatype/dict/mutual_ref_16/main.k @@ -0,0 +1,9 @@ +level0 = { + name = "orange" + level1_a = { + name = "apple" + } + level1_b = { + name = "pine" + name + } +} diff --git a/test/grammar/datatype/dict/mutual_ref_16/stdout.golden b/test/grammar/datatype/dict/mutual_ref_16/stdout.golden new file mode 100644 index 000000000..4c0d4c4a3 --- /dev/null +++ b/test/grammar/datatype/dict/mutual_ref_16/stdout.golden @@ -0,0 +1,6 @@ +level0: + name: orange + level1_a: + name: apple + level1_b: + name: pineorange