diff --git a/.gitignore b/.gitignore index 0d57d4d98..2873aec44 100644 --- a/.gitignore +++ b/.gitignore @@ -90,9 +90,5 @@ _a.out_*.* # Compiler_base .compiler_base -# LLVM -llvm* -llvm-* - # KCL mod lock file !.mod.lock diff --git a/kclvm/compiler/src/codegen/llvm/backtrack.rs b/kclvm/compiler/src/codegen/llvm/backtrack.rs new file mode 100644 index 000000000..ed3107962 --- /dev/null +++ b/kclvm/compiler/src/codegen/llvm/backtrack.rs @@ -0,0 +1,25 @@ +// Copyright The KCL Authors. All rights reserved. + +use super::context::LLVMCodeGenContext; +use crate::codegen::traits::BuilderMethods; +use inkwell::values::BasicValueEnum; + +impl<'ctx> LLVMCodeGenContext<'ctx> { + pub(crate) fn update_backtrack_meta( + &self, + name: &str, + schema_value: BasicValueEnum<'ctx>, + ) -> bool { + if let Some(backtrack_meta) = self.backtrack_meta.borrow_mut().as_mut() { + if name == backtrack_meta.target { + backtrack_meta.count += 1; + if backtrack_meta.count >= backtrack_meta.level { + backtrack_meta.stop = true; + self.ret(schema_value); + return true; + } + } + } + false + } +} diff --git a/kclvm/compiler/src/codegen/llvm/context.rs b/kclvm/compiler/src/codegen/llvm/context.rs index cc3964b1b..d11400f3b 100644 --- a/kclvm/compiler/src/codegen/llvm/context.rs +++ b/kclvm/compiler/src/codegen/llvm/context.rs @@ -4,6 +4,7 @@ use indexmap::{IndexMap, IndexSet}; use inkwell::basic_block::BasicBlock; use inkwell::builder::Builder; use inkwell::context::Context; +use inkwell::debug_info::{DICompileUnit, DebugInfoBuilder}; use inkwell::memory_buffer::MemoryBuffer; use inkwell::module::{Linkage, Module}; use inkwell::support::LLVMString; @@ -22,7 +23,6 @@ use std::rc::Rc; use std::str; use kclvm_ast::ast; -use kclvm_ast::walker::TypedResultWalker; use kclvm_error::*; use kclvm_runtime::{ApiFunc, MAIN_PKG_PATH, PKG_PATH_PREFIX}; use kclvm_sema::builtin; @@ -61,12 +61,15 @@ pub struct Scope<'ctx> { pub scalars: RefCell>>, /// schema_scalar_idx denotes whether a schema exists in the scalar list. pub schema_scalar_idx: RefCell, + /// Scope normal variables pub variables: RefCell>>, + /// Scope closures referenced by internal scope. pub closures: RefCell>>, + /// Potential arguments in the current scope, such as schema/lambda arguments. pub arguments: RefCell>, } -/// Schema internal order independent computation backtracking meta information. +/// Schema or Global internal order independent computation backtracking meta information. pub struct BacktrackMeta { pub target: String, pub level: usize, @@ -80,31 +83,50 @@ pub struct LLVMCodeGenContext<'ctx> { pub module: Module<'ctx>, pub builder: Builder<'ctx>, pub program: &'ctx ast::Program, - pub pkg_scopes: RefCell>>>>, pub functions: RefCell>>>, pub imported: RefCell>, - pub local_vars: RefCell>, pub schema_stack: RefCell>, pub lambda_stack: RefCell>, pub schema_expr_stack: RefCell>, pub pkgpath_stack: RefCell>, pub filename_stack: RefCell>, + /// Package scope to store variable pointers. + pub pkg_scopes: RefCell>>>>, + /// Local variables in the loop. + pub local_vars: RefCell>, + /// The names of possible assignment objects for the current instruction. pub target_vars: RefCell>, + /// Global string caches pub global_strings: RefCell>>>, + /// Global variable pointers cross different packages. pub global_vars: RefCell>>>, + /// The filename of the source file corresponding to the current instruction pub current_filename: RefCell, + /// The line number of the source file corresponding to the current instruction pub current_line: RefCell, + /// Error handler to store compile errors. pub handler: RefCell, - // Schema attr backtrack meta + /// Schema attr backtrack meta pub backtrack_meta: RefCell>, /// Import names mapping pub import_names: IndexMap>, - // No link mode + /// No link mode pub no_link: bool, - pub modules: RefCell>>>, + /// Debug mode + pub debug: bool, + /// Program modules according to AST modules + pub modules: RefCell>>>, + /// Program workdir pub workdir: String, } +/// LLVM module with debug info builder and compile unit. +pub struct DebugModule<'ctx> { + pub inner: Module<'ctx>, + pub dibuilder: DebugInfoBuilder<'ctx>, + pub compile_unit: DICompileUnit<'ctx>, +} + impl<'ctx> CodeGenObject for BasicValueEnum<'ctx> {} impl<'ctx> CodeGenObject for BasicTypeEnum<'ctx> {} @@ -311,9 +333,9 @@ impl<'ctx> BuilderMethods for LLVMCodeGenContext<'ctx> { fn lookup_function(&self, name: &str) -> Self::Function { if self.no_link { let pkgpath = self.current_pkgpath(); - let modules = self.modules.borrow_mut(); + let modules = self.modules.borrow(); let msg = format!("pkgpath {} is not found", pkgpath); - let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + let module = &modules.get(&pkgpath).expect(&msg).borrow().inner; if let Some(function) = module.get_function(name) { function } else { @@ -337,7 +359,7 @@ impl<'ctx> BuilderMethods for LLVMCodeGenContext<'ctx> { let pkgpath = self.current_pkgpath(); let msg = format!("pkgpath {} is not found", pkgpath); let modules = self.modules.borrow_mut(); - let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + let module = &modules.get(&pkgpath).expect(&msg).borrow_mut().inner; module.add_function(name, fn_ty, None) } else { self.module.add_function(name, fn_ty, None) @@ -548,7 +570,7 @@ impl<'ctx> ValueMethods for LLVMCodeGenContext<'ctx> { let pkgpath = self.current_pkgpath(); let modules = self.modules.borrow_mut(); let msg = format!("pkgpath {} is not found", pkgpath); - let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + let module = &modules.get(&pkgpath).expect(&msg).borrow_mut().inner; let fn_type = function.get_type(); function = module.add_function(function_name, fn_type, Some(Linkage::External)); } @@ -562,7 +584,7 @@ impl<'ctx> ValueMethods for LLVMCodeGenContext<'ctx> { let pkgpath = self.current_pkgpath(); let msg = format!("pkgpath {} is not found", pkgpath); let modules = self.modules.borrow_mut(); - let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + let module = &modules.get(&pkgpath).expect(&msg).borrow_mut().inner; module.add_global(tpe, Some(AddressSpace::default()), name) } else { self.module @@ -1248,6 +1270,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { backtrack_meta: RefCell::new(None), import_names, no_link, + debug: false, modules: RefCell::new(HashMap::new()), workdir, } @@ -1266,11 +1289,12 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { let has_main_pkg = self.program.pkgs.contains_key(MAIN_PKG_PATH); let function = if self.no_link { let mut modules = self.modules.borrow_mut(); - let name = if has_main_pkg { - MAIN_PKG_PATH.to_string() + // Pkgpath + let (pkgpath, function_name) = if has_main_pkg { + (MAIN_PKG_PATH.to_string(), MODULE_NAME.to_string()) } else { assert!(self.program.pkgs.len() == 1); - format!( + let pkgpath = format!( "{}{}", kclvm_runtime::PKG_PATH_PREFIX, self.program @@ -1278,18 +1302,17 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { .keys() .next() .expect(kcl_error::INTERNAL_ERROR_MSG) + ); + ( + pkgpath.clone(), + format!( + "${}.{}", + pkgpath_without_prefix!(pkgpath), + PKG_INIT_FUNCTION_SUFFIX + ), ) }; - let module = self.context.create_module(name.as_str()); - let function_name = if has_main_pkg { - MODULE_NAME.to_string() - } else { - format!( - "${}.{}", - pkgpath_without_prefix!(name), - PKG_INIT_FUNCTION_SUFFIX - ) - }; + let module = self.context.create_module(pkgpath.as_str()); let function = module.add_function( // Function name function_name.as_str(), @@ -1297,7 +1320,10 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { if has_main_pkg { fn_type } else { void_fn_type }, None, ); - modules.insert(name.to_string(), RefCell::new(module)); + modules.insert( + pkgpath.to_string(), + RefCell::new(self.create_debug_module(module)), + ); function } else { self.module.add_function( @@ -1398,7 +1424,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { }; let path = std::path::Path::new(&path); // Build LLVM module to a `.o` object file. - self.build_object_file(&module.borrow(), path)?; + self.build_object_file(&module.borrow().inner, path)?; } } else { // Build LLVM module to a `.o` object file. @@ -1408,51 +1434,6 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { Ok(()) } - /// Compile AST Modules, which requires traversing three times. - /// 1. scan all possible global variables and allocate undefined values to global pointers. - /// 2. build all user-defined schema/rule types. - /// 3. generate all LLVM IR codes for the third time. - fn compile_ast_modules(&self, modules: &'ctx [ast::Module]) { - // Scan global variables - for ast_module in modules { - { - self.filename_stack - .borrow_mut() - .push(ast_module.filename.clone()); - } - // Pre define global variables with undefined values - self.predefine_global_vars(ast_module); - { - self.filename_stack.borrow_mut().pop(); - } - } - // Scan global types - for ast_module in modules { - { - self.filename_stack - .borrow_mut() - .push(ast_module.filename.clone()); - } - self.compile_module_import_and_types(ast_module); - { - self.filename_stack.borrow_mut().pop(); - } - } - // Compile the ast module in the pkgpath. - for ast_module in modules { - { - self.filename_stack - .borrow_mut() - .push(ast_module.filename.clone()); - } - self.walk_module(ast_module) - .expect(kcl_error::COMPILE_ERROR_MSG); - { - self.filename_stack.borrow_mut().pop(); - } - } - } - /// Build LLVM module to a `.o` object file. /// /// TODO: WASM and cross platform build. @@ -1569,7 +1550,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { let pkgpath = self.current_pkgpath(); let msg = format!("pkgpath {} is not found", pkgpath); let modules = self.modules.borrow_mut(); - let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + let module = &modules.get(&pkgpath).expect(&msg).borrow_mut().inner; module.add_global(tpe, Some(AddressSpace::default()), name) } else { self.module @@ -1665,23 +1646,34 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { false } - /// Resolve variable in scope, return false when not found + /// Resolve variable in scope, return false when not found. + #[inline] pub fn resolve_variable(&self, name: &str) -> bool { + self.resolve_variable_level(name).is_some() + } + + /// Resolve variable level in scope, return None when not found. + pub fn resolve_variable_level(&self, name: &str) -> Option { // Find argument name in the scope let current_pkgpath = self.current_pkgpath(); let pkg_scopes = self.pkg_scopes.borrow(); let msg = format!("pkgpath {} is not found", current_pkgpath); let scopes = pkg_scopes.get(¤t_pkgpath).expect(&msg); - let mut existed = false; + let mut level = None; for i in 0..scopes.len() { let index = scopes.len() - i - 1; let variables = scopes[index].variables.borrow(); - if variables.get(&name.to_string()).is_some() { - existed = true; + let arguments = scopes[index].arguments.borrow(); + if variables.get(name).is_some() { + level = Some(index); + break; + } + if arguments.contains(name) { + level = Some(index); break; } } - existed + level } /// Append a variable or update the existed local variable. @@ -1696,7 +1688,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { let index = scopes.len() - i - 1; let variables_mut = scopes[index].variables.borrow_mut(); match variables_mut.get(&name.to_string()) { - // If the local varibale is found, store the new value for the variable. + // 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 @@ -1863,7 +1855,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { message: format!("name '{}' is not defined", name), ty: kcl_error::KCLErrorType::Compile, }); - let is_in_schema = self.schema_stack.borrow().len() > 0; + let is_in_schema = self.is_in_schema(); // System module if builtin::STANDARD_SYSTEM_MODULE_NAMES_WITH_AT.contains(&pkgpath.as_str()) { let pkgpath = &pkgpath[1..]; @@ -2019,7 +2011,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { let current_pkgpath = self.current_pkgpath(); let modules = self.modules.borrow(); let msg = format!("pkgpath {} is not found", current_pkgpath); - let module = modules.get(¤t_pkgpath).expect(&msg).borrow(); + let module = &modules.get(¤t_pkgpath).expect(&msg).borrow().inner; let tpe = self.value_ptr_type(); let mut global_var_maps = self.global_vars.borrow_mut(); let pkgpath = self.current_pkgpath(); @@ -2088,18 +2080,82 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { var_map }; // Capture schema `self` closure. - let is_in_schema = self.schema_stack.borrow().len() > 0; - if is_in_schema { - for shcmea_closure_name in value::SCHEMA_VARIABLE_LIST { + if self.is_in_schema() { + for schema_closure_name in value::SCHEMA_VARIABLE_LIST { let value = self - .get_variable(shcmea_closure_name) + .get_variable(schema_closure_name) .expect(kcl_error::INTERNAL_ERROR_MSG); - self.dict_insert_override_item(var_map, shcmea_closure_name, value); + self.dict_insert_override_item(var_map, schema_closure_name, value); } } var_map } + /// Load value from name. + pub fn load_value(&self, pkgpath: &str, names: &[&str]) -> CompileResult<'ctx> { + if names.is_empty() { + return Err(kcl_error::KCLError { + message: "error: read value from empty name".to_string(), + ty: kcl_error::KCLErrorType::Compile, + }); + } + let name = names[0]; + // Get variable from the scope. + let get = |name: &str| { + match ( + self.is_in_schema(), + self.is_in_lambda(), + self.is_local_var(name), + ) { + // Get from local or global scope + (false, _, _) | (_, _, true) => self.get_variable(name), + // Get variable from the current schema scope. + (true, false, false) => self.get_variable_in_schema(name), + // Get from local scope including lambda arguments, lambda variables, + // loop variables or global variables. + (true, true, _) => + // Get from local scope including lambda arguments, lambda variables, + // loop variables or global variables. + { + match self.resolve_variable_level(name) { + // Closure variable or local variables + Some(level) if level > GLOBAL_LEVEL => self.get_variable(name), + // Schema closure or global variables + _ => self.get_variable_in_schema(name), + } + } + } + }; + if names.len() == 1 { + get(name) + } else { + let mut value = if pkgpath.is_empty() { + get(name) + } else { + self.ok_result() + } + .expect(kcl_error::INTERNAL_ERROR_MSG); + for i in 0..names.len() - 1 { + let attr = names[i + 1]; + if i == 0 && !pkgpath.is_empty() { + value = if self.no_link { + self.get_external_variable_in_pkgpath(attr, pkgpath) + } else { + self.get_variable_in_pkgpath(attr, pkgpath) + } + .expect(kcl_error::INTERNAL_ERROR_MSG) + } else { + let attr = self.native_global_string(attr, "").into(); + value = self.build_call( + &ApiFunc::kclvm_value_load_attr.name(), + &[self.current_runtime_ctx_ptr(), value, attr], + ); + } + } + Ok(value) + } + } + /// Push a lambda definition scope into the lambda stack #[inline] pub fn push_lambda(&self, scope: usize) { @@ -2131,6 +2187,21 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { .expect(kcl_error::INTERNAL_ERROR_MSG) } + #[inline] + pub fn is_in_schema(&self) -> bool { + self.schema_stack.borrow().len() > 0 + } + + #[inline] + pub fn is_in_schema_expr(&self) -> bool { + self.schema_expr_stack.borrow().len() > 0 + } + + #[inline] + pub fn is_local_var(&self, name: &str) -> bool { + self.local_vars.borrow().contains(name) + } + /// Push a function call frame into the function stack #[inline] pub fn push_function(&self, function: FunctionValue<'ctx>) { diff --git a/kclvm/compiler/src/codegen/llvm/emit.rs b/kclvm/compiler/src/codegen/llvm/emit.rs index 6b6aa9681..c00116aac 100644 --- a/kclvm/compiler/src/codegen/llvm/emit.rs +++ b/kclvm/compiler/src/codegen/llvm/emit.rs @@ -1,4 +1,4 @@ -// Copyright 2021 The KCL Authors. All rights reserved. +// Copyright The KCL Authors. All rights reserved. use indexmap::IndexMap; use inkwell::module::Module; diff --git a/kclvm/compiler/src/codegen/llvm/metadata.rs b/kclvm/compiler/src/codegen/llvm/metadata.rs new file mode 100644 index 000000000..82a2bda5d --- /dev/null +++ b/kclvm/compiler/src/codegen/llvm/metadata.rs @@ -0,0 +1,38 @@ +// Copyright The KCL Authors. All rights reserved. + +use super::context::{DebugModule, LLVMCodeGenContext}; +use crate::codegen::traits::ProgramCodeGen; +use inkwell::module::Module; + +impl<'ctx> LLVMCodeGenContext<'ctx> { + pub(crate) fn create_debug_module(&self, module: Module<'ctx>) -> DebugModule<'ctx> { + let (dibuilder, compile_unit) = module.create_debug_info_builder( + true, + /* language */ inkwell::debug_info::DWARFSourceLanguage::C, + /* filename */ &self.current_pkgpath(), + /* directory */ ".", + /* producer */ "kcl", + /* is_optimized */ false, + /* compiler command line flags */ "", + /* runtime_ver */ 0, + /* split_name */ "", + /* kind */ inkwell::debug_info::DWARFEmissionKind::Full, + /* dwo_id */ 0, + /* split_debug_inling */ false, + /* debug_info_for_profiling */ false, + /* sys_root */ ".", + "", + ); + let debug_metadata_version = self.context.i32_type().const_int(3, false); + module.add_basic_value_flag( + "Debug Info Version", + inkwell::module::FlagBehavior::Warning, + debug_metadata_version, + ); + DebugModule { + inner: module, + dibuilder, + compile_unit, + } + } +} diff --git a/kclvm/compiler/src/codegen/llvm/mod.rs b/kclvm/compiler/src/codegen/llvm/mod.rs index 4eb26cd42..2cce6095d 100644 --- a/kclvm/compiler/src/codegen/llvm/mod.rs +++ b/kclvm/compiler/src/codegen/llvm/mod.rs @@ -5,8 +5,10 @@ //! //! Copyright 2021 The KCL Authors. All rights reserved. +mod backtrack; mod context; mod emit; +mod metadata; mod module; mod node; mod schema; diff --git a/kclvm/compiler/src/codegen/llvm/module.rs b/kclvm/compiler/src/codegen/llvm/module.rs index 2f16de940..21c3bf9c0 100644 --- a/kclvm/compiler/src/codegen/llvm/module.rs +++ b/kclvm/compiler/src/codegen/llvm/module.rs @@ -1,4 +1,5 @@ // Copyright The KCL Authors. All rights reserved. + use kclvm_ast::ast; use kclvm_ast::walker::TypedResultWalker; @@ -76,4 +77,49 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } } } + + /// Compile AST Modules, which requires traversing three times. + /// 1. scan all possible global variables and allocate undefined values to global pointers. + /// 2. build all user-defined schema/rule types. + /// 3. generate all LLVM IR codes for the third time. + pub(crate) fn compile_ast_modules(&self, modules: &'ctx [ast::Module]) { + // Scan global variables + for ast_module in modules { + { + self.filename_stack + .borrow_mut() + .push(ast_module.filename.clone()); + } + // Pre define global variables with undefined values + self.predefine_global_vars(ast_module); + { + self.filename_stack.borrow_mut().pop(); + } + } + // Scan global types + for ast_module in modules { + { + self.filename_stack + .borrow_mut() + .push(ast_module.filename.clone()); + } + self.compile_module_import_and_types(ast_module); + { + self.filename_stack.borrow_mut().pop(); + } + } + // Compile the ast module in the pkgpath. + for ast_module in modules { + { + self.filename_stack + .borrow_mut() + .push(ast_module.filename.clone()); + } + self.walk_module(ast_module) + .expect(kcl_error::COMPILE_ERROR_MSG); + { + self.filename_stack.borrow_mut().pop(); + } + } + } } diff --git a/kclvm/compiler/src/codegen/llvm/node.rs b/kclvm/compiler/src/codegen/llvm/node.rs index c1beeedf6..c104bf69f 100644 --- a/kclvm/compiler/src/codegen/llvm/node.rs +++ b/kclvm/compiler/src/codegen/llvm/node.rs @@ -1,4 +1,4 @@ -// Copyright 2021 The KCL Authors. All rights reserved. +// Copyright The KCL Authors. All rights reserved. use std::cell::RefCell; use std::collections::HashMap; @@ -38,8 +38,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { check_backtrack_stop!(self); utils::update_ctx_filename(self, stmt); utils::update_ctx_line_col(self, stmt); - self.target_vars.borrow_mut().clear(); - self.target_vars.borrow_mut().push("".to_string()); + utils::reset_target_vars(self); match &stmt.node { ast::Stmt::TypeAlias(type_alias) => self.walk_type_alias_stmt(type_alias), ast::Stmt::Expr(expr_stmt) => self.walk_expr_stmt(expr_stmt), @@ -112,7 +111,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { return Ok(value); } // Local variables including schema/rule/lambda - } else if self.schema_stack.borrow().len() > 0 { + } else if self.is_in_schema() { // Load the identifier value let org_value = self .walk_identifier_with_ctx( @@ -146,6 +145,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { fn walk_assign_stmt(&self, assign_stmt: &'ctx ast::AssignStmt) -> Self::Result { check_backtrack_stop!(self); self.local_vars.borrow_mut().clear(); + // Set target vars. for name in &assign_stmt.targets { self.target_vars .borrow_mut() @@ -157,8 +157,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { .expect(kcl_error::COMPILE_ERROR_MSG); if let Some(ty) = &assign_stmt.ty { let type_annotation = self.native_global_string_value(&ty.node.to_string()); - let is_in_schema = - self.schema_stack.borrow().len() > 0 || self.schema_expr_stack.borrow().len() > 0; + let is_in_schema = self.is_in_schema() || self.is_in_schema_expr(); value = self.build_call( &ApiFunc::kclvm_convert_collection_value.name(), &[ @@ -170,11 +169,12 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { ); } if assign_stmt.targets.len() == 1 { + // Store the single target let name = &assign_stmt.targets[0]; self.walk_identifier_with_ctx(&name.node, &name.node.ctx, Some(value)) .expect(kcl_error::COMPILE_ERROR_MSG); } else { - // Store targets + // Store multiple targets for name in &assign_stmt.targets { let value = self.value_deep_copy(value); self.walk_identifier_with_ctx(&name.node, &name.node.ctx, Some(value)) @@ -243,6 +243,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { let assert_result = self .walk_expr(&assert_stmt.test) .expect(kcl_error::COMPILE_ERROR_MSG); + // Assert statement error message. let msg = { if let Some(msg) = &assert_stmt.msg { self.walk_expr(msg).expect(kcl_error::COMPILE_ERROR_MSG) @@ -283,6 +284,8 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { fn walk_import_stmt(&self, import_stmt: &'ctx ast::ImportStmt) -> Self::Result { check_backtrack_stop!(self); let pkgpath = import_stmt.path.node.as_str(); + // Check if it has already been generated, there is no need to generate code + // for duplicate import statements. { let imported = self.imported.borrow_mut(); if imported.contains(pkgpath) { @@ -290,6 +293,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { } // Deref the borrow mut } + // Stardard or plugin modules. if builtin::STANDARD_SYSTEM_MODULES.contains(&pkgpath) || pkgpath.starts_with(plugin::PLUGIN_MODULE_PREFIX) { @@ -297,8 +301,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { return self.ok_result(); } else { let pkgpath = format!("{}{}", PKG_PATH_PREFIX, import_stmt.path.node); - self.pkgpath_stack.borrow_mut().push(pkgpath); - let pkgpath = format!("{}{}", PKG_PATH_PREFIX, import_stmt.path.node); + self.pkgpath_stack.borrow_mut().push(pkgpath.clone()); let has_pkgpath = self.program.pkgs.contains_key(&import_stmt.path.node); let func_before_block = if self.no_link { if has_pkgpath { @@ -325,7 +328,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { let basic_block = self.context.append_basic_block(function, ENTRY_NAME); self.builder.position_at_end(basic_block); self.push_function(function); - modules.insert(name, RefCell::new(module)); + modules.insert(name, RefCell::new(self.create_debug_module(module))); Some(func_before_block) } else { None @@ -336,40 +339,12 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { if has_pkgpath { // Init all builtin functions. self.init_scope(pkgpath.as_str()); - // Compile the ast module in the pkgpath. - for ast_module in self - .program - .pkgs - .get(&import_stmt.path.node) - .expect(kcl_error::INTERNAL_ERROR_MSG) - { - { - self.filename_stack - .borrow_mut() - .push(ast_module.filename.clone()); - } - self.compile_module_import_and_types(ast_module); - { - self.filename_stack.borrow_mut().pop(); - } - } - for ast_module in self - .program - .pkgs - .get(&import_stmt.path.node) - .expect(kcl_error::INTERNAL_ERROR_MSG) - { - { - self.filename_stack - .borrow_mut() - .push(ast_module.filename.clone()); - } - self.walk_stmts_except_import(&ast_module.body) - .expect(kcl_error::COMPILE_ERROR_MSG); - { - self.filename_stack.borrow_mut().pop(); - } - } + self.compile_ast_modules( + self.program + .pkgs + .get(&import_stmt.path.node) + .expect(kcl_error::INTERNAL_ERROR_MSG), + ); } self.pkgpath_stack.borrow_mut().pop(); if self.no_link { @@ -382,7 +357,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { let pkgpath = self.current_pkgpath(); let modules = self.modules.borrow_mut(); let msg = format!("pkgpath {} is not found", pkgpath); - let module = modules.get(&pkgpath).expect(&msg).borrow_mut(); + let module = &modules.get(&pkgpath).expect(&msg).borrow_mut().inner; if has_pkgpath { self.ret_void(); self.pop_function(); @@ -1564,17 +1539,8 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { ], ); // Update backtrack meta - { - if let Some(backtrack_meta) = self.backtrack_meta.borrow_mut().as_mut() { - if name == backtrack_meta.target { - backtrack_meta.count += 1; - if backtrack_meta.count >= backtrack_meta.level { - backtrack_meta.stop = true; - self.ret(schema_value); - return Ok(schema_value); - } - } - } + if self.update_backtrack_meta(name, schema_value) { + return Ok(schema_value); } self.br(end_block); self.builder.position_at_end(else_block); @@ -1789,8 +1755,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { self.dict_insert(dict_value, name.node.as_str(), value, 0, -1); } let pkgpath = self.native_global_string_value(&self.current_pkgpath()); - let is_in_schema = - self.schema_stack.borrow().len() > 0 || self.schema_expr_stack.borrow().len() > 0; + let is_in_schema = self.is_in_schema() || self.is_in_schema_expr(); Ok(self.build_call( &ApiFunc::kclvm_value_function_invoke.name(), &[ @@ -2024,8 +1989,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { check_backtrack_stop!(self); // Check the required attributes only when the values of all attributes // in the final schema are solved. - let is_in_schema = - self.schema_stack.borrow().len() > 0 || self.schema_expr_stack.borrow().len() > 0; + let is_in_schema = self.is_in_schema() || self.is_in_schema_expr(); { self.schema_expr_stack.borrow_mut().push(()); } @@ -2129,7 +2093,6 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { fn walk_lambda_expr(&self, lambda_expr: &'ctx ast::LambdaExpr) -> Self::Result { check_backtrack_stop!(self); let pkgpath = &self.current_pkgpath(); - let is_in_schema = self.schema_stack.borrow().len() > 0; // Higher-order lambda requires capturing the current lambda closure variable // as well as the closure of a more external scope. let last_closure_map = self.get_current_inner_scope_variable_map(); @@ -2160,9 +2123,9 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { let var = self.builder.build_alloca(tpe, value::LAMBDA_CLOSURE); self.builder.build_store(var, closure_map); self.add_variable(value::LAMBDA_CLOSURE, var); - if is_in_schema { - for shcmea_closure_name in value::SCHEMA_VARIABLE_LIST { - let string_ptr_value = self.native_global_string(shcmea_closure_name, "").into(); + if self.is_in_schema() { + for schema_closure_name in value::SCHEMA_VARIABLE_LIST { + let string_ptr_value = self.native_global_string(schema_closure_name, "").into(); let schema_value = self.build_call( &ApiFunc::kclvm_dict_get_value.name(), &[ @@ -2174,9 +2137,9 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { let value_ptr_type = self.value_ptr_type(); let var = self .builder - .build_alloca(value_ptr_type, shcmea_closure_name); + .build_alloca(value_ptr_type, schema_closure_name); self.builder.build_store(var, schema_value); - self.add_variable(shcmea_closure_name, var); + self.add_variable(schema_closure_name, var); } } self.walk_arguments(&lambda_expr.args, args, kwargs); @@ -2389,7 +2352,7 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { fn walk_module(&self, module: &'ctx ast::Module) -> Self::Result { check_backtrack_stop!(self); - // Compile all statements of the module + // Compile all statements of the module except all import statements self.walk_stmts_except_import(&module.body) } } @@ -2426,8 +2389,9 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { right_value: Option>, ) -> CompileResult<'ctx> { check_backtrack_stop!(self); - let is_in_schema = self.schema_stack.borrow().len() > 0; + let is_in_schema = self.is_in_schema(); match identifier_ctx { + // Store a.b.c = 1 ast::ExprContext::Store => { if identifier.names.len() == 1 { let name = identifier.names[0].node.as_str(); @@ -2438,7 +2402,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { name, right_value.expect(kcl_error::INTERNAL_ERROR_MSG), ); - // Local variables including schema/rule/lambda + // Lambda local variables. } else if self.is_in_lambda() { let value = right_value.expect(kcl_error::INTERNAL_ERROR_MSG); // If variable exists in the scope and update it, if not, add it to the scope. @@ -2458,101 +2422,28 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { self.store_variable(name, value); } } else { - let is_local_var = { - let local_vars = self.local_vars.borrow_mut(); - local_vars.contains(name) - }; - let value = if is_in_schema { - let value = right_value.expect(kcl_error::INTERNAL_ERROR_MSG); + let is_local_var = self.is_local_var(name); + let value = right_value.expect(kcl_error::INTERNAL_ERROR_MSG); + // Store schema attribute + if is_in_schema { let schema_value = self .get_variable(value::SCHEMA_SELF_NAME) .expect(kcl_error::INTERNAL_ERROR_MSG); + // Schema config let config_value = self .get_variable(value::SCHEMA_CONFIG_NAME) .expect(kcl_error::INTERNAL_ERROR_MSG); - let string_ptr_value = self.native_global_string(name, "").into(); - let has_key = self - .build_call( - &ApiFunc::kclvm_dict_has_value.name(), - &[config_value, string_ptr_value], - ) - .into_int_value(); - let has_key = self.builder.build_int_compare( - IntPredicate::NE, - has_key, - self.native_i8_zero(), - "", - ); - let last_block = self.append_block(""); - let then_block = self.append_block(""); - let else_block = self.append_block(""); - self.br(last_block); - self.builder.position_at_end(last_block); - let none_value = self.none_value(); - self.builder - .build_conditional_branch(has_key, then_block, else_block); - self.builder.position_at_end(then_block); - let config_entry = self.build_call( - &ApiFunc::kclvm_dict_get_entry.name(), - &[ - self.current_runtime_ctx_ptr(), - config_value, - string_ptr_value, - ], - ); - self.br(else_block); - self.builder.position_at_end(else_block); - let tpe = self.value_ptr_type(); - let phi = self.builder.build_phi(tpe, ""); - phi.add_incoming(&[ - (&none_value, last_block), - (&config_entry, then_block), - ]); - let config_value = phi.as_basic_value(); - if self.scope_level() >= INNER_LEVEL && !is_local_var { - self.dict_merge(schema_value, name, value, 1, -1); - self.value_union(schema_value, config_value); - let cal_map = self - .get_variable(value::SCHEMA_CAL_MAP) - .expect(kcl_error::INTERNAL_ERROR_MSG); - let backtrack_cache = self - .get_variable(value::BACKTRACK_CACHE) - .expect(kcl_error::INTERNAL_ERROR_MSG); - let runtime_type = self - .get_variable(value::SCHEMA_RUNTIME_TYPE) - .expect(kcl_error::INTERNAL_ERROR_MSG); - let name_native_str = self.native_global_string_value(name); - self.build_void_call( - &ApiFunc::kclvm_schema_backtrack_cache.name(), - &[ - self.current_runtime_ctx_ptr(), - schema_value, - backtrack_cache, - cal_map, - name_native_str, - runtime_type, - ], - ); - // Update backtrack meta - { - if let Some(backtrack_meta) = - self.backtrack_meta.borrow_mut().as_mut() - { - if name == backtrack_meta.target { - backtrack_meta.count += 1; - if backtrack_meta.count == backtrack_meta.level { - backtrack_meta.stop = true; - self.ret(schema_value); - return Ok(schema_value); - } - } - } - } + // If is in the backtrack, return the schema value. + if self.update_schema_scope_value( + schema_value, + config_value, + name, + Some(value), + ) { + return Ok(schema_value); } - value - } else { - right_value.expect(kcl_error::INTERNAL_ERROR_MSG) - }; + } + // Store loop variable if is_local_var || !is_in_schema { let var = self.builder.build_alloca(tpe, name); self.builder.build_store(var, value); @@ -2562,13 +2453,12 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } else { let names = &identifier.names; let name = names[0].node.as_str(); - let mut value = if is_in_schema { - self.get_variable_in_schema(name) - .expect(kcl_error::INTERNAL_ERROR_MSG) - } else { - self.get_variable(name) - .expect(kcl_error::INTERNAL_ERROR_MSG) - }; + // In KCL, we cannot modify global variables in other packages, + // so pkgpath is empty here. + let mut value = self + .load_value("", &[name]) + .expect(kcl_error::INTERNAL_ERROR_MSG); + // Convert `store a.b.c = 1` -> `%t = load &a; %t = load_attr %t %b; store_attr %t %c with 1` for i in 0..names.len() - 1 { let attr = names[i + 1].node.as_str(); let ctx = if matches!(identifier_ctx, ast::ExprContext::Store) @@ -2599,10 +2489,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { ], ); - let is_local_var = { - let local_vars = self.local_vars.borrow_mut(); - local_vars.contains(name) - }; + let is_local_var = self.is_local_var(name); let is_in_lambda = self.is_in_lambda(); // Set config value for the schema attribute if the attribute is in the schema and // it is not a local variable in the lambda function. @@ -2617,83 +2504,13 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { let config_value = self .get_variable(value::SCHEMA_CONFIG_NAME) .expect(kcl_error::INTERNAL_ERROR_MSG); - let string_ptr_value = - self.native_global_string(name, "").into(); - let has_key = self - .build_call( - &ApiFunc::kclvm_dict_has_value.name(), - &[config_value, string_ptr_value], - ) - .into_int_value(); - // The config has the attribute key? - let has_key = self.builder.build_int_compare( - IntPredicate::NE, - has_key, - self.native_i8_zero(), - "", - ); - let last_block = self.append_block(""); - let then_block = self.append_block(""); - let else_block = self.append_block(""); - self.br(last_block); - self.builder.position_at_end(last_block); - let none_value = self.none_value(); - self.builder - .build_conditional_branch(has_key, then_block, else_block); - self.builder.position_at_end(then_block); - let config_entry = self.build_call( - &ApiFunc::kclvm_dict_get_entry.name(), - &[ - self.current_runtime_ctx_ptr(), - config_value, - string_ptr_value, - ], - ); - self.br(else_block); - self.builder.position_at_end(else_block); - let tpe = self.value_ptr_type(); - let phi = self.builder.build_phi(tpe, ""); - phi.add_incoming(&[ - (&none_value, last_block), - (&config_entry, then_block), - ]); - let config_value = phi.as_basic_value(); - self.value_union(schema_value, config_value); - let cal_map = self - .get_variable(value::SCHEMA_CAL_MAP) - .expect(kcl_error::INTERNAL_ERROR_MSG); - let backtrack_cache = self - .get_variable(value::BACKTRACK_CACHE) - .expect(kcl_error::INTERNAL_ERROR_MSG); - let runtime_type = self - .get_variable(value::SCHEMA_RUNTIME_TYPE) - .expect(kcl_error::INTERNAL_ERROR_MSG); - let name_native_str = self.native_global_string_value(name); - self.build_void_call( - &ApiFunc::kclvm_schema_backtrack_cache.name(), - &[ - self.current_runtime_ctx_ptr(), - schema_value, - backtrack_cache, - cal_map, - name_native_str, - runtime_type, - ], - ); - // Update backtrack meta - { - if let Some(backtrack_meta) = - self.backtrack_meta.borrow_mut().as_mut() - { - if name == backtrack_meta.target { - backtrack_meta.count += 1; - if backtrack_meta.count == backtrack_meta.level { - backtrack_meta.stop = true; - self.ret(schema_value); - return Ok(schema_value); - } - } - } + if self.update_schema_scope_value( + schema_value, + config_value, + name, + None, + ) { + return Ok(schema_value); } } } @@ -2702,80 +2519,15 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } Ok(right_value.expect(kcl_error::INTERNAL_ERROR_MSG)) } - ast::ExprContext::Load => { - let name = identifier.names[0].node.as_str(); - let is_local_var = { - let local_vars = self.local_vars.borrow_mut(); - local_vars.contains(name) - }; - if identifier.names.len() == 1 { - if is_in_schema && !is_local_var { - self.get_variable_in_schema(name) - } else { - self.get_variable(name) - } - } else { - let names = &identifier.names; - let name = names[0].node.as_str(); - let mut value = if identifier.pkgpath.is_empty() { - if is_in_schema && !is_local_var { - self.get_variable_in_schema(name) - .expect(kcl_error::INTERNAL_ERROR_MSG) - } else { - self.get_variable(name) - .expect(kcl_error::INTERNAL_ERROR_MSG) - } - } else { - self.ok_result().expect(kcl_error::INTERNAL_ERROR_MSG) - }; - for i in 0..names.len() - 1 { - let attr = names[i + 1].node.as_str(); - let ctx = if matches!(identifier_ctx, ast::ExprContext::Store) - && i != names.len() - 2 - && names.len() > 2 - { - &ast::ExprContext::Load - } else { - identifier_ctx - }; - match ctx { - ast::ExprContext::Load => { - if i == 0 && !identifier.pkgpath.is_empty() { - value = if self.no_link { - self.get_external_variable_in_pkgpath( - attr, - &identifier.pkgpath, - ) - .expect(kcl_error::INTERNAL_ERROR_MSG) - } else { - self.get_variable_in_pkgpath(attr, &identifier.pkgpath) - .expect(kcl_error::INTERNAL_ERROR_MSG) - } - } else { - let attr = self.native_global_string(attr, "").into(); - value = self.build_call( - &ApiFunc::kclvm_value_load_attr.name(), - &[self.current_runtime_ctx_ptr(), value, attr], - ); - } - } - ast::ExprContext::Store => { - let attr = self.native_global_string(attr, "").into(); - self.build_void_call( - &ApiFunc::kclvm_dict_set_value.name(), - &[ - self.current_runtime_ctx_ptr(), - value, - attr, - right_value.expect(kcl_error::INTERNAL_ERROR_MSG), - ], - ); - } - } - } - Ok(value) - } - } + // Load .a.b.c + ast::ExprContext::Load => self.load_value( + &identifier.pkgpath, + &identifier + .names + .iter() + .map(|n| n.node.as_str()) + .collect::>(), + ), } } @@ -2852,6 +2604,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } else { self.none_value() }; + // Arguments are immutable, so we place them in different scopes. self.store_argument_in_current_scope(&arg_name.get_name()); self.walk_identifier_with_ctx(arg_name, &ast::ExprContext::Store, Some(arg_value)) .expect(kcl_error::COMPILE_ERROR_MSG); diff --git a/kclvm/compiler/src/codegen/llvm/schema.rs b/kclvm/compiler/src/codegen/llvm/schema.rs index 797906603..ed3b04e16 100644 --- a/kclvm/compiler/src/codegen/llvm/schema.rs +++ b/kclvm/compiler/src/codegen/llvm/schema.rs @@ -1,20 +1,24 @@ -// Copyright 2021 The KCL Authors. All rights reserved. +// Copyright The KCL Authors. All rights reserved. use inkwell::values::{BasicValueEnum, FunctionValue}; -use inkwell::AddressSpace; +use inkwell::{AddressSpace, IntPredicate}; use kclvm_ast::ast; +use kclvm_runtime::ApiFunc; use kclvm_sema::pkgpath_without_prefix; use std::collections::HashMap; use std::str; use super::context::LLVMCodeGenContext; -use crate::codegen::error as kcl_error; -use crate::codegen::traits::{BuilderMethods, DerivedValueCalculationMethods, ValueMethods}; +use crate::codegen::traits::{ + BuilderMethods, DerivedTypeMethods, DerivedValueCalculationMethods, ProgramCodeGen, + ValueMethods, +}; +use crate::codegen::{error as kcl_error, INNER_LEVEL}; use crate::value; impl<'ctx> LLVMCodeGenContext<'ctx> { /// Emit all left identifiers because all the attribute can be forward referenced. - pub fn emit_left_identifiers( + pub(crate) fn emit_left_identifiers( &self, body: &'ctx [Box>], index_signature: &'ctx Option>, @@ -166,7 +170,7 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } } - pub fn get_schema_config_meta( + pub(crate) fn get_schema_config_meta( &self, n: Option<&'ctx ast::Node>, t: &'ctx ast::ConfigExpr, @@ -240,4 +244,81 @@ impl<'ctx> LLVMCodeGenContext<'ctx> { } config_meta } + + pub(crate) fn update_schema_scope_value( + &self, + schema_value: BasicValueEnum<'ctx>, // Schema self value + config_value: BasicValueEnum<'ctx>, // Schema config value + name: &str, // Schema arribute name + value: Option>, // Optional right override value + ) -> bool { + // Attribute name + let string_ptr_value = self.native_global_string(name, "").into(); + // i8 has_key + let has_key = self + .build_call( + &ApiFunc::kclvm_dict_has_value.name(), + &[config_value, string_ptr_value], + ) + .into_int_value(); + // i1 has_key + let has_key = + self.builder + .build_int_compare(IntPredicate::NE, has_key, self.native_i8_zero(), ""); + let last_block = self.append_block(""); + let then_block = self.append_block(""); + let else_block = self.append_block(""); + self.br(last_block); + self.builder.position_at_end(last_block); + let none_value = self.none_value(); + self.builder + .build_conditional_branch(has_key, then_block, else_block); + self.builder.position_at_end(then_block); + let config_entry = self.build_call( + &ApiFunc::kclvm_dict_get_entry.name(), + &[ + self.current_runtime_ctx_ptr(), + config_value, + string_ptr_value, + ], + ); + self.br(else_block); + self.builder.position_at_end(else_block); + let tpe = self.value_ptr_type(); + let phi = self.builder.build_phi(tpe, ""); + phi.add_incoming(&[(&none_value, last_block), (&config_entry, then_block)]); + let config_value = phi.as_basic_value(); + if self.scope_level() >= INNER_LEVEL && !self.local_vars.borrow().contains(name) { + if let Some(value) = value { + self.dict_merge(schema_value, name, value, 1, -1); + } + self.value_union(schema_value, config_value); + let cal_map = self + .get_variable(value::SCHEMA_CAL_MAP) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let backtrack_cache = self + .get_variable(value::BACKTRACK_CACHE) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let runtime_type = self + .get_variable(value::SCHEMA_RUNTIME_TYPE) + .expect(kcl_error::INTERNAL_ERROR_MSG); + let name_native_str = self.native_global_string_value(name); + self.build_void_call( + &ApiFunc::kclvm_schema_backtrack_cache.name(), + &[ + self.current_runtime_ctx_ptr(), + schema_value, + backtrack_cache, + cal_map, + name_native_str, + runtime_type, + ], + ); + // Update backtrack meta + if self.update_backtrack_meta(name, schema_value) { + return true; + } + } + false + } } diff --git a/kclvm/compiler/src/codegen/llvm/utils.rs b/kclvm/compiler/src/codegen/llvm/utils.rs index b6c1cd11f..7bffb4a2f 100644 --- a/kclvm/compiler/src/codegen/llvm/utils.rs +++ b/kclvm/compiler/src/codegen/llvm/utils.rs @@ -1,4 +1,4 @@ -// Copyright 2021 The KCL Authors. All rights reserved. +// Copyright The KCL Authors. All rights reserved. use kclvm_ast::ast; use kclvm_runtime::ApiFunc; @@ -13,7 +13,7 @@ use super::context::LLVMCodeGenContext; */ /// Update runtime context pkgpath -pub fn update_ctx_pkgpath(gen: &LLVMCodeGenContext, pkgpath: &str) { +pub(crate) fn update_ctx_pkgpath(gen: &LLVMCodeGenContext, pkgpath: &str) { gen.build_void_call( &ApiFunc::kclvm_context_set_kcl_pkgpath.name(), &[ @@ -24,7 +24,10 @@ pub fn update_ctx_pkgpath(gen: &LLVMCodeGenContext, pkgpath: &str) { } /// Update runtime context filename -pub fn update_ctx_filename<'ctx, T>(gen: &'ctx LLVMCodeGenContext, node: &'ctx ast::Node) { +pub(crate) fn update_ctx_filename<'ctx, T>( + gen: &'ctx LLVMCodeGenContext, + node: &'ctx ast::Node, +) { if !node.filename.is_empty() { gen.build_void_call( &ApiFunc::kclvm_context_set_kcl_filename.name(), @@ -37,7 +40,10 @@ pub fn update_ctx_filename<'ctx, T>(gen: &'ctx LLVMCodeGenContext, node: &'ctx a } /// Update runtime context line and column -pub fn update_ctx_line_col<'ctx, T>(gen: &'ctx LLVMCodeGenContext, node: &'ctx ast::Node) { +pub(crate) fn update_ctx_line_col<'ctx, T>( + gen: &'ctx LLVMCodeGenContext, + node: &'ctx ast::Node, +) { let mut current_line = gen.current_line.borrow_mut(); if node.line != *current_line { *current_line = node.line; @@ -53,7 +59,7 @@ pub fn update_ctx_line_col<'ctx, T>(gen: &'ctx LLVMCodeGenContext, node: &'ctx a } /// Update runtime context line and column -pub fn update_ctx_current_line(gen: &LLVMCodeGenContext) { +pub(crate) fn update_ctx_current_line(gen: &LLVMCodeGenContext) { let current_line = gen.current_line.borrow_mut(); gen.build_void_call( &ApiFunc::kclvm_context_set_kcl_line_col.name(), @@ -64,3 +70,9 @@ pub fn update_ctx_current_line(gen: &LLVMCodeGenContext) { ], ); } + +/// Reset target vars +pub(crate) fn reset_target_vars(gen: &LLVMCodeGenContext) { + gen.target_vars.borrow_mut().clear(); + gen.target_vars.borrow_mut().push("".to_string()); +} diff --git a/kclvm/version/src/lib.rs b/kclvm/version/src/lib.rs index 4f76524b1..33c4a4331 100644 --- a/kclvm/version/src/lib.rs +++ b/kclvm/version/src/lib.rs @@ -1,7 +1,7 @@ //! Copyright The KCL Authors. All rights reserved. pub const VERSION: &str = include_str!("./../../../VERSION"); -pub const CHECK_SUM: &str = "20ab3eb4b9179219d6837a57f5d35286"; +pub const CHECK_SUM: &str = "0c20ab3eb4b9179219d6837a57f5d352"; /// Get kCL full version string with the format `{version}-{check_sum}`. #[inline] diff --git a/test/grammar/lambda/in_schema_6/main.k b/test/grammar/lambda/in_schema_6/main.k new file mode 100644 index 000000000..33259269a --- /dev/null +++ b/test/grammar/lambda/in_schema_6/main.k @@ -0,0 +1,9 @@ +schema Name: + y: int = 2 + z: int = lambda x { + y = 1 + z = y + 1 # y is 1 instead of 2 + }(1) + + +n = Name {} diff --git a/test/grammar/lambda/in_schema_6/stdout.golden b/test/grammar/lambda/in_schema_6/stdout.golden new file mode 100644 index 000000000..e338aaaa7 --- /dev/null +++ b/test/grammar/lambda/in_schema_6/stdout.golden @@ -0,0 +1,3 @@ +n: + y: 2 + z: 2 diff --git a/test/grammar/lambda/in_schema_7/main.k b/test/grammar/lambda/in_schema_7/main.k new file mode 100644 index 000000000..9c000f117 --- /dev/null +++ b/test/grammar/lambda/in_schema_7/main.k @@ -0,0 +1,10 @@ +schema Name: + y: {str:str} = {} + z: {str:str} = lambda x { + y: {str:str} = {} + y.key = x + y + }("value") + + +n = Name {} diff --git a/test/grammar/lambda/in_schema_7/stdout.golden b/test/grammar/lambda/in_schema_7/stdout.golden new file mode 100644 index 000000000..af6136c94 --- /dev/null +++ b/test/grammar/lambda/in_schema_7/stdout.golden @@ -0,0 +1,4 @@ +n: + y: {} + z: + key: value diff --git a/test/grammar/lambda/in_schema_8/main.k b/test/grammar/lambda/in_schema_8/main.k new file mode 100644 index 000000000..0aff96ec2 --- /dev/null +++ b/test/grammar/lambda/in_schema_8/main.k @@ -0,0 +1,8 @@ +x: int = 1 +schema Name: + x: int = 2 + z: int = lambda a { + a + x # x is the schema x instead of global x + }(2) + +n = Name {} diff --git a/test/grammar/lambda/in_schema_8/stdout.golden b/test/grammar/lambda/in_schema_8/stdout.golden new file mode 100644 index 000000000..3a1d53e25 --- /dev/null +++ b/test/grammar/lambda/in_schema_8/stdout.golden @@ -0,0 +1,4 @@ +x: 1 +n: + x: 2 + z: 4