diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dfdfaea..0fe58179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes are documented in this file. - Properly warn if FFI is used in a file intended for a different backend - Improve various error messages and reduce noise - Functions can no longer be shadowed by variables +- fix: change `unnecessary mut` to be a warning ### Other Changes - `static` visibility is now controlled by `pub` keyword @@ -22,6 +23,7 @@ All notable changes are documented in this file. - fix(c): Prevent c error if blank identifier is used ### Compiler internals +- refac: improve handling of FunDecl and CallExpr - refac(gen): move generation of test main function into transformer diff --git a/lib/bait/ast/ast.bt b/lib/bait/ast/ast.bt index f6ebd3a9..5e2691dc 100644 --- a/lib/bait/ast/ast.bt +++ b/lib/bait/ast/ast.bt @@ -91,7 +91,7 @@ pub struct FunDecl { pub is_pub bool pub lang Language pub name string - pub mix_name string // TMP + pub key string // TODO rework generics and possibly remove this field pub params []Param pub return_type Type pub is_test bool @@ -112,7 +112,7 @@ pub struct Param { } pub fun (fn FunDecl) is_main() bool { - return not fn.is_method and fn.mix_name == 'main' + return not fn.is_method and fn.name == 'main' } pub struct LoopControlStmt { @@ -199,7 +199,7 @@ pub struct BoolLiteral { pub struct CallExpr { global lang Language - pub pkg string + global pkg string global name string global mix_name string // TMP global return_type Type diff --git a/lib/bait/ast/repr.bt b/lib/bait/ast/repr.bt index e912fdd2..f2051c51 100644 --- a/lib/bait/ast/repr.bt +++ b/lib/bait/ast/repr.bt @@ -11,7 +11,7 @@ pub fun (e Expr) repr() string { AsCast { e.expr.repr() + ' as XX' } BlankIdent { '_' } BoolLiteral { e.str() } - CallExpr{ e.mix_name + '()'} + CallExpr{ e.full_name() + '()'} CharLiteral { e.val } ComptimeVar { e.kind.str() } EnumVal{ e.val } @@ -36,10 +36,3 @@ pub fun (e Expr) repr() string { InvalidExpr { 'InvalidExpr' } } } - -pub fun (fn FunDecl) key() string { - if fn.is_method { - return '${fn.params[0].typ}.${fn.mix_name}' - } - return fn.mix_name -} diff --git a/lib/bait/ast/table.bt b/lib/bait/ast/table.bt index 02b8f60c..eade2aa1 100644 --- a/lib/bait/ast/table.bt +++ b/lib/bait/ast/table.bt @@ -3,7 +3,6 @@ package ast pub struct Table { - pub mut fun_decls map[string]FunDecl pub mut type_idxs map[string]Type pub mut type_symbols []TypeSymbol pub mut needed_equality_funs []Type @@ -176,7 +175,7 @@ pub fun (t Table) get_method(sym TypeSymbol, name string) FunDecl { mut s := sym for true { for m in s.methods { - if m.mix_name == name { + if m.name == name { return m } } diff --git a/lib/bait/ast/types.bt b/lib/bait/ast/types.bt index c8c90e0f..ff5f22f6 100644 --- a/lib/bait/ast/types.bt +++ b/lib/bait/ast/types.bt @@ -104,7 +104,7 @@ pub fun (sym TypeSymbol) find_field(name string, t Table) StructField { pub fun (sym TypeSymbol) has_method(name string) bool { for m in sym.methods { - if m.mix_name == name { + if m.name == name { return true } } diff --git a/lib/bait/checker/attribute.bt b/lib/bait/checker/attribute.bt index 1be92f78..17ef9872 100644 --- a/lib/bait/checker/attribute.bt +++ b/lib/bait/checker/attribute.bt @@ -101,12 +101,12 @@ fun (c Checker) attr_export(attr ast.Attribute) { c.export_names.push(attr.value) } -fun (c Checker) check_fun_attrs_on_call(call ast.CallExpr, def ast.FunDecl) { +fun (c Checker) check_fun_attrs_on_call(call ast.CallExpr, attrs []ast.Attribute) { mut is_deprecated := false mut depr_attr := ast.Attribute{} mut depr_date_attr := ast.Attribute{} - for attr in def.attrs { + for attr in attrs { if attr.name == 'deprecated_after' { depr_date_attr = attr is_deprecated = true @@ -118,7 +118,7 @@ fun (c Checker) check_fun_attrs_on_call(call ast.CallExpr, def ast.FunDecl) { } if is_deprecated { - mut depr_message := 'function "${call.mix_name}" ' + mut depr_message := 'function "${call.full_name()}" ' if depr_date_attr.name.length > 0 { depr_message += 'will be deprecated after ${depr_date_attr.value}' } else { diff --git a/lib/bait/checker/fun.bt b/lib/bait/checker/fun.bt index e17302b2..132f3da4 100644 --- a/lib/bait/checker/fun.bt +++ b/lib/bait/checker/fun.bt @@ -25,16 +25,16 @@ fun (mut c Checker) fun_decl(mut node ast.FunDecl) { if node.is_method { sym := c.table.get_sym(node.params[0].typ) if sym.info is ast.StructInfo { - field := sym.find_field(node.mix_name, c.table) + field := sym.find_field(node.name, c.table) if field.name.length > 0 { if c.table.get_sym(field.typ).kind == .fun_ { - c.error('struct has a field and method named ${node.mix_name}', node.pos) + c.error('struct has a field and method named ${node.name}', node.pos) } } } } else { if node.is_test { - c.gen_ctx.test_fun_names.push(node.mix_name) + c.gen_ctx.test_fun_names.push(node.name) } else if c.prefs.is_test { if node.name == "testsuite_begin" { c.gen_ctx.has_test_begin = true @@ -104,14 +104,12 @@ fun (c Checker) fun_params(params []ast.Param){ if sym.kind == .fun_ { info := sym.info as ast.FunInfo - c.table.fun_decls[p.name] = ast.FunDecl{ - return_type = info.return_type - params = info.to_params() - } c.scope.register(p.name, context.ScopeObject{ typ = p.typ kind = .function pkg = c.pkg + return_type = info.return_type + params = info.to_params() }) } else { c.scope.register(p.name, context.ScopeObject{ @@ -142,17 +140,13 @@ fun (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type { fun (mut c Checker) fun_call(mut node ast.CallExpr) ast.Type { obj := c.find_in_scope(node.name, node.pkg) if obj.kind != .function { - c.error('unknown function ${node.mix_name}', node.pos) + c.error('unknown function ${node.full_name()}', node.pos) return ast.ERROR_TYPE } - mut found := c.table.fun_decls.contains(node.mix_name) - if not found and not node.mix_name.contains('.') and c.pkg != 'builtin' { - full_name := c.pkg + '.' + node.mix_name - if c.table.fun_decls.contains(full_name) { - node.mix_name = full_name - found = true - } + // Set package + if node.pkg.length == 0 and obj.pkg != "main" and obj.pkg != "builtin" { + node.pkg = obj.pkg } // Check visibility @@ -160,32 +154,31 @@ fun (mut c Checker) fun_call(mut node ast.CallExpr) ast.Type { c.error("function `${node.full_name()}` is private", node.pos) } - mut def := c.table.fun_decls[node.mix_name] - node.noreturn = def.noreturn - node.return_type = def.return_type + node.noreturn = obj.noreturn + node.return_type = obj.return_type - c.check_fun_attrs_on_call(node, def) + c.check_fun_attrs_on_call(node, obj.attrs) // Check argument count - if node.args.length != def.params.length { - c.error('expected ${def.params.length} arguments but got ${node.args.length}', node.pos) + if node.args.length != obj.params.length { + c.error('expected ${obj.params.length} arguments but got ${node.args.length}', node.pos) return node.return_type } // Handle special builtin functions - if node.mix_name == 'println' or node.mix_name == 'eprintln' or node.mix_name == 'print' or node.mix_name == 'eprint' { + if node.name == 'println' or node.name == 'eprintln' or node.name == 'print' or node.name == 'eprint' { node.args[0].typ = c.non_void_expr(node.args[0].expr) or { ast.ERROR_TYPE } return ast.VOID_TYPE } // Check argument types - c.call_args(def, mut node, 0) + c.call_args(mut node, 0, obj.generic_names, obj.params) c.or_block(mut node) - c.set_conc_types(mut node, mut def) + c.set_conc_types(mut node, obj.generic_names, obj.key) - if node.mix_name == 'error' { + if node.name == 'error' { node.return_type = c.cur_fun.return_type } @@ -203,12 +196,12 @@ fun (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { final_sym := c.table.get_final_sym(left_sym) mut arg_offset := 1 - mut def := c.table.get_method(left_sym, node.mix_name) + mut def := c.table.get_method(left_sym, node.name) - if def.mix_name.length == 0 { + if def.name.length == 0 { // Check for callable struct field if left_sym.kind == .struct_ { - field := left_sym.find_field(node.mix_name, c.table) + field := left_sym.find_field(node.name, c.table) if field.name.length > 0 { field_sym := c.table.get_sym(field.typ) if field_sym.kind != .fun_ { @@ -220,7 +213,7 @@ fun (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { arg_offset = 0 node.is_field = true def = ast.FunDecl{ - mix_name = field.name + name = field.name params = info.to_params() return_type = info.return_type } @@ -228,9 +221,9 @@ fun (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } } - if def.mix_name.length == 0 { + if def.name.length == 0 { // Autogenerated str method - if node.mix_name == 'str' { + if node.name == 'str' { if node.args.length > 0 { c.error('expected 0 arguments but got ${node.args.length}', node.pos) return ast.STRING_TYPE @@ -242,12 +235,12 @@ fun (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { return node.return_type } - c.error('method ${node.mix_name} not found on type ${left_sym.name}', node.pos) + c.error('method ${node.name} not found on type ${left_sym.name}', node.pos) return ast.ERROR_TYPE } if not def.is_pub and left_sym.pkg != c.pkg { - c.error('method ${def.mix_name} is private', node.pos) + c.error('method ${def.name} is private', node.pos) } node.lang = def.lang @@ -266,7 +259,7 @@ fun (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { // The following is required for some builtin array and map methods node.left_type = def.params[0].typ - if final_sym.kind == .array and ['push', 'push_many_with_len', 'push_many'].contains(node.mix_name) { + if final_sym.kind == .array and ['push', 'push_many_with_len', 'push_many'].contains(node.name) { node.left_type = left_expr_type } @@ -288,7 +281,7 @@ fun (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } } - c.check_fun_attrs_on_call(node, def) + c.check_fun_attrs_on_call(node, def.attrs) // Check argument count if node.args.length + arg_offset != def.params.length { @@ -297,7 +290,7 @@ fun (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } // Array methods with one argument of `any` type, expecting the element - if final_sym.kind == .array and ['push', 'push_many_with_len'].contains(node.mix_name) { + if final_sym.kind == .array and ['push', 'push_many_with_len'].contains(node.name) { mut arg := node.args[0] info := final_sym.info as ast.ArrayInfo c.expected_type = info.elem_type @@ -308,7 +301,7 @@ fun (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { return node.return_type } // Array methods with one argument of `array` type, expecting the specific array type - if final_sym.kind == .array and ['concat', 'push_many'].contains(node.mix_name) { + if final_sym.kind == .array and ['concat', 'push_many'].contains(node.name) { mut arg := node.args[0] c.expected_type = left_expr_type arg.typ = c.expr(arg.expr) @@ -319,50 +312,52 @@ fun (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } // Check argument types - c.call_args(def, mut node, arg_offset) + c.call_args(mut node, arg_offset, def.generic_names, def.params) // Array methods with return type `array` - if left_sym.kind == .array and ['filter', 'reverse', 'slice', 'copy', 'from_js_arr'].contains(node.mix_name) { + if left_sym.kind == .array and ['filter', 'reverse', 'slice', 'copy', 'from_js_arr'].contains(node.name) { return left_expr_type } // Array methods with return type `any` - if left_sym.kind == .array and ['last'].contains(node.mix_name) { + if left_sym.kind == .array and node.name == "last" { return (left_sym.info as ast.ArrayInfo).elem_type } c.or_block(mut node) - c.set_conc_types(mut node, mut def) + c.set_conc_types(mut node, def.generic_names, def.key) return node.return_type } -fun (mut c Checker) set_conc_types(mut node ast.CallExpr, mut def ast.FunDecl) { +// TODO clean this up +fun (mut c Checker) set_conc_types(mut node ast.CallExpr, generic_names []string, key string) { if node.concrete_types.length == 0 { return } - c.need_generic_resolve = c.table.register_concrete(def.key(), node.concrete_types) or c.need_generic_resolve + c.need_generic_resolve = c.table.register_concrete(key, node.concrete_types) or c.need_generic_resolve ret_sym := c.table.get_sym(node.return_type) if ret_sym.kind == .generic { - idx := def.generic_names.index(ret_sym.name) + idx := generic_names.index(ret_sym.name) node.return_type = node.concrete_types[idx] } } -fun (mut c Checker) call_args(def ast.FunDecl, mut node ast.CallExpr, poffset i32) { - should_resolve_generics := def.generic_names.length != node.concrete_types.length +// TODO clean this up +fun (mut c Checker) call_args(mut node ast.CallExpr, poffset i32, generic_names []string, params []ast.Param) { + should_resolve_generics := generic_names.length != node.concrete_types.length mut save_as_concrete := false for i, mut arg in node.args { - param := def.params[i + poffset] + param := params[i + poffset] mut param_type := param.typ psym := c.table.get_sym(param_type) if should_resolve_generics and psym.kind == .generic { - gi := def.generic_names.index(psym.name) + gi := generic_names.index(psym.name) if gi < node.concrete_types.length { param_type = node.concrete_types[gi] } else if gi < c.cur_concrete_types.length { @@ -392,7 +387,7 @@ fun (mut c Checker) call_args(def ast.FunDecl, mut node ast.CallExpr, poffset i3 if arg.is_mut { if not param.is_mut { - c.error('unnecessary `mut` for argument `${expr.name}`', expr.pos) + c.warn('unnecessary `mut` for argument `${expr.name}`', expr.pos) } if not expr.is_mut { c.error('`${expr.name}` is not declared as mutable', expr.pos) @@ -410,14 +405,14 @@ fun (mut c Checker) or_block(mut node ast.CallExpr) { // Function does not return a result if ret_sym.kind != .result { if node.or_block.kind != .none { - c.error('${node.mix_name} does not return a result, it cannot have an `or {}` block or `!` at the end', node.pos) + c.error('${node.name} does not return a result, it cannot have an `or {}` block or `!` at the end', node.pos) } return } // Unhandled result if node.or_block.kind == .none { - c.error('${node.mix_name} returns a result, it must have an `or {}` block or propagation `!` at the end', node.pos) + c.error('${node.name} returns a result, it must have an `or {}` block or propagation `!` at the end', node.pos) return } @@ -429,7 +424,7 @@ fun (mut c Checker) or_block(mut node ast.CallExpr) { // Only possible in `main` or other result function cur_fun_ret_sym := c.table.get_sym(c.cur_fun.return_type) if cur_fun_ret_sym.kind != .result and not c.cur_fun.is_main() { - c.error('`${c.cur_fun.mix_name}` must return a result to use propagation', node.pos) + c.error('`${c.cur_fun.name}` must return a result to use propagation', node.pos) } return @@ -458,7 +453,7 @@ fun (mut c Checker) or_block(mut node ast.CallExpr) { fun (mut c Checker) resolve_generics_funs() { for mut fn in c.file.generic_funs { - gtypes := c.table.generic_fun_types[fn.key()] + gtypes := c.table.generic_fun_types[fn.key] for concrete in gtypes { c.cur_concrete_types = concrete c.fun_instance(mut fn) diff --git a/lib/bait/checker/return.bt b/lib/bait/checker/return.bt index abb9ba48..bab6bbd8 100644 --- a/lib/bait/checker/return.bt +++ b/lib/bait/checker/return.bt @@ -11,7 +11,7 @@ fun (mut c Checker) return_stmt(mut node ast.ReturnStmt) { if not c.check_types(expr_type, c.cur_fun.return_type) { mut msg := '' if c.cur_fun.return_type == ast.VOID_TYPE { - msg = 'function `${c.cur_fun.mix_name}` should return nothing' + msg = 'function `${c.cur_fun.name}` should return nothing' } else { msg = 'expected return value of type ${c.table.type_name(c.cur_fun.return_type)}' } diff --git a/lib/bait/checker/stmt.bt b/lib/bait/checker/stmt.bt index 83487fbe..78426383 100644 --- a/lib/bait/checker/stmt.bt +++ b/lib/bait/checker/stmt.bt @@ -69,7 +69,7 @@ fun (mut c Checker) expr_stmt(mut node ast.ExprStmt) { // Warn if function call with return value is not used if expr is ast.CallExpr { if expr.return_type > ast.VOID_TYPE { - c.warn("use `_ = ${expr.mix_name}()` to explicitly ignore the return value", expr.pos) + c.warn("use `_ = ${expr.full_name()}()` to explicitly ignore the return value", expr.pos) } return diff --git a/lib/bait/context/scope.bt b/lib/bait/context/scope.bt index 2952ee0b..a8d7c91d 100644 --- a/lib/bait/context/scope.bt +++ b/lib/bait/context/scope.bt @@ -17,6 +17,14 @@ pub struct ScopeObject{ pub mut is_mut bool pub mut pkg string // TODO SCOPES move this into Scope (requires multi return) pub mut expr ast.Expr := ast.Void{} // const / static: resolve type on use + + // Only for functions (Note: will be cleaned up in the refactor to use sumtypes) + pub noreturn bool + pub return_type ast.Type + pub params []ast.Param + pub attrs []ast.Attribute + pub generic_names []string + pub key string } pub enum ObjectKind { diff --git a/lib/bait/gen/c/cgen.bt b/lib/bait/gen/c/cgen.bt index 67338b09..7fe659dd 100644 --- a/lib/bait/gen/c/cgen.bt +++ b/lib/bait/gen/c/cgen.bt @@ -37,9 +37,6 @@ struct Gen { mut is_lhs_assign bool mut is_array_map_set bool mut is_for_loop_head bool - // Testing - mut has_test_begin bool - mut has_test_end bool // Cached comptime variables mut baitexe string mut baitdir string diff --git a/lib/bait/gen/c/comptime.bt b/lib/bait/gen/c/comptime.bt index 6d5100b0..0b59241c 100644 --- a/lib/bait/gen/c/comptime.bt +++ b/lib/bait/gen/c/comptime.bt @@ -25,7 +25,7 @@ fun (mut g Gen) get_comptime_val(kind token.ComptimeVar, pos token.Pos) string { line := g.get_comptime_val(.line, pos) '${file}:${line}' } - .fun_ { g.cur_fun.mix_name } + .fun_ { g.cur_fun.name } // Cached .baitexe { g.comptime_baitexe() } diff --git a/lib/bait/gen/c/expr.bt b/lib/bait/gen/c/expr.bt index 4d45dbc1..462dab3e 100644 --- a/lib/bait/gen/c/expr.bt +++ b/lib/bait/gen/c/expr.bt @@ -184,7 +184,7 @@ fun (mut g Gen) infix_expr(node ast.InfixExpr) { if node.op == .ne { g.write('!') } - g.write(c_esc(lsym.name + '_' + overload.mix_name)) + g.write(c_esc(lsym.name + '_' + overload.name)) g.write('(') g.expr(node.left) g.write(', ') @@ -293,7 +293,7 @@ fun (mut g Gen) expr_to_string(expr ast.Expr, typ ast.Type) { } str_def := g.table.get_method(sym, 'str') - if str_def.mix_name.length > 0 { + if str_def.name.length > 0 { final_sym := g.table.get_sym(str_def.params[0].typ) mut name := c_esc(final_sym.name) g.write('${name}_str(') diff --git a/lib/bait/gen/c/fun.bt b/lib/bait/gen/c/fun.bt index b6b7dc72..a7caf17d 100644 --- a/lib/bait/gen/c/fun.bt +++ b/lib/bait/gen/c/fun.bt @@ -17,18 +17,12 @@ fun (mut g Gen) anon_fun(node ast.AnonFun) { g.indent = last_indent g.empty_line = was_line_empty - g.write(node.decl.mix_name) + g.write(node.decl.name) } fun (mut g Gen) fun_decl(node ast.FunDecl) { - if node.mix_name == 'testsuite_begin' { - g.has_test_begin = true - } else if node.mix_name == 'testsuite_end' { - g.has_test_end = true - } - if node.generic_names.length > 0 and g.cur_concrete_types.length == 0 { - gtypes := g.table.generic_fun_types[node.key()] + gtypes := g.table.generic_fun_types[node.key] for conc_types in gtypes { for i, gn in node.generic_names { g.cur_concrete_types[gn] = conc_types[i] @@ -43,14 +37,24 @@ fun (mut g Gen) fun_decl(node ast.FunDecl) { g.cur_fun = node type_str := g.typ(node.return_type) - mut name := c_esc(node.mix_name) + + mut name := "" if node.is_method { sym := g.table.get_sym(node.params[0].typ) - name = c_esc(sym.name + '_' + node.mix_name) + name = c_name(sym.name + '_' + node.name) + } else { + // TODO remove exception for main and builtin + if g.pkg == "main" or g.pkg == "builtin" or node.is_test { + name = c_esc(node.name) + } else { + name = c_name(g.pkg + "." + node.name) + } } + if g.cur_concrete_types.length > 0 { name = g.get_concrete_name(name, g.cur_concrete_types.values()) } + s := '${type_str} ${name}(' g.fun_decls_out += s @@ -83,14 +87,14 @@ fun (mut g Gen) call_expr(node ast.CallExpr) { } else { g.write('.') } - g.write(node.mix_name) + g.write(node.name) g.write('(') g.call_args(node.args) g.write(')') return } - mut name := c_esc(node.mix_name) + mut name := c_esc(node.full_name()) if node.is_method { sym := g.table.get_sym(node.left_type) final_sym := g.table.get_final_sym(sym) @@ -109,9 +113,9 @@ fun (mut g Gen) call_expr(node ast.CallExpr) { } } - name = c_esc(sym.name + '_' + node.mix_name) + name = c_name(sym.name + '_' + node.name) } else if node.lang != .bait{ - name = node.mix_name.replace('C.', '') + name = node.name.replace('C.', '') } if node.concrete_types.length > 0 { @@ -120,7 +124,7 @@ fun (mut g Gen) call_expr(node ast.CallExpr) { g.write(name) - if not node.is_method and ['println', 'eprintln', 'print', 'eprint'].contains(node.mix_name) { + if not node.is_method and ['println', 'eprintln', 'print', 'eprint'].contains(node.name) { g.write('(') g.expr_to_string(node.args[0].expr, node.args[0].typ) g.write(')') diff --git a/lib/bait/gen/c/stmt.bt b/lib/bait/gen/c/stmt.bt index 551c4bee..647eb664 100644 --- a/lib/bait/gen/c/stmt.bt +++ b/lib/bait/gen/c/stmt.bt @@ -55,7 +55,7 @@ fun (mut g Gen) assign_stmt(node ast.AssignStmt) { if lsym.overloads.contains(node.op.c_repr()) { g.write(' = ') overload := lsym.overloads[node.op.c_repr()] - g.write(c_esc(lsym.name + '_' + overload.mix_name)) + g.write(c_esc(lsym.name + '_' + overload.name)) g.write('(') g.expr(node.left) g.write(', ') diff --git a/lib/bait/gen/js/fun.bt b/lib/bait/gen/js/fun.bt index e126d47d..d5c1b0e1 100644 --- a/lib/bait/gen/js/fun.bt +++ b/lib/bait/gen/js/fun.bt @@ -5,14 +5,8 @@ package js import bait.ast fun (mut g Gen) fun_decl(node ast.FunDecl) { - if node.name == 'testsuite_begin' { - g.has_test_begin = true - } else if node.name == 'testsuite_end' { - g.has_test_end = true - } - if node.generic_names.length > 0 and g.cur_concrete_types.length == 0 { - gtypes := g.table.generic_fun_types[node.key()] + gtypes := g.table.generic_fun_types[node.key] for conc_types in gtypes { for i, gn in node.generic_names { g.cur_concrete_types[gn] = conc_types[i] @@ -32,7 +26,12 @@ fun (mut g Gen) fun_decl(node ast.FunDecl) { sym := g.table.get_sym(node.params[0].typ) name = js_esc(sym.name + '_' + node.name) } else { - name = js_esc(node.mix_name) + // TODO remove exception for main and builtin + if g.pkg == "main" or g.pkg == "builtin" or node.is_test { + name = js_esc(node.name) + } else { + name = js_name(g.pkg + "." + node.name) + } } if g.cur_concrete_types.length > 0 { name = g.get_concrete_name(name, g.cur_concrete_types.values()) @@ -46,7 +45,7 @@ fun (mut g Gen) fun_decl(node ast.FunDecl) { export_attr := node.attrs.find_attr('export') if export_attr.name != '' { - g.writeln('module.exports.${export_attr.value} = ${js_esc(node.mix_name)}') + g.writeln('module.exports.${export_attr.value} = ${name}') } g.writeln('') @@ -114,7 +113,7 @@ fun (mut g Gen) call_expr_no_or(node ast.CallExpr) { return } - mut name := node.mix_name + mut name := node.full_name() if node.is_method { if name == 'str' { g.expr_to_string(node.left, node.left_type) @@ -123,14 +122,14 @@ fun (mut g Gen) call_expr_no_or(node ast.CallExpr) { sym := g.table.get_sym(node.left_type) final_sym := g.table.get_final_sym(sym) - if final_sym.kind == .array and ['push', 'push_many', 'push_many_with_len'].contains(node.mix_name) { - g.gen_array_method(node.mix_name, node, final_sym) + if final_sym.kind == .array and ['push', 'push_many', 'push_many_with_len'].contains(node.name) { + g.gen_array_method(node.name, node, final_sym) return } - name = js_esc(sym.name + '_' + node.mix_name) + name = js_name(sym.name + '_' + node.name) } else if node.lang == .bait{ - name = js_esc(node.mix_name) + name = js_esc(name) } if node.concrete_types.length > 0 { diff --git a/lib/bait/gen/js/jsgen.bt b/lib/bait/gen/js/jsgen.bt index 402e7d5a..661a0059 100644 --- a/lib/bait/gen/js/jsgen.bt +++ b/lib/bait/gen/js/jsgen.bt @@ -63,8 +63,6 @@ struct Gen { mut is_for_loop_head bool mut is_lhs_assign bool mut is_array_map_set bool - mut has_test_begin bool - mut has_test_end bool // Cached comptime variables mut baitexe string mut baitdir string diff --git a/lib/bait/parser/fun.bt b/lib/bait/parser/fun.bt index 77dd4c76..ef16e56e 100644 --- a/lib/bait/parser/fun.bt +++ b/lib/bait/parser/fun.bt @@ -43,17 +43,19 @@ fun (mut p Parser) fun_decl() !ast.FunDecl{ ffi := p.parse_ffi_pkg()! lang = ffi.lang - mut mix_name := "" + mut key := "" if lang != .bait { - mix_name = ffi.pkg + "." // TMP + key = ffi.pkg + "." // TMP } // normal function name name := p.check_name()! - mix_name += name - is_test := mix_name.starts_with('test_') - if not is_method and lang == .bait { - mix_name = p.prepend_pkg(mix_name) + key += name + is_test := name.starts_with('test_') + if is_method { + key = '${params[0].typ}.${name}' + } else if lang == .bait { + key = p.prepend_pkg(key) } // Generics @@ -73,7 +75,7 @@ fun (mut p Parser) fun_decl() !ast.FunDecl{ is_test = is_test is_pub = is_pub name = name - mix_name = mix_name + key = key generic_names = generic_names params = params return_type = return_type @@ -82,18 +84,16 @@ fun (mut p Parser) fun_decl() !ast.FunDecl{ lang = lang pos = pos } - p.attributes = []ast.Attribute // Register method or function if is_method { sym := p.table.get_sym(params[0].typ) // TODO move this error into checker - if lang == .bait and sym.has_method(mix_name) { - p.error('Method "${mix_name}" already exists on type "${sym.name}"')! + if lang == .bait and sym.has_method(name) { + p.error('Method "${name}" already exists on type "${sym.name}"')! } sym.methods.push(node) } else { - p.table.fun_decls[mix_name] = node mut param_types := []ast.Type for param in params { param_types.push(param.typ) @@ -106,16 +106,27 @@ fun (mut p Parser) fun_decl() !ast.FunDecl{ typ = node.typ pkg = p.pkg_name is_pub = is_pub + noreturn = p.attributes.has('noreturn') + return_type = return_type + params = params + attrs = p.attributes + generic_names = generic_names + key = key }) } else { ffi_scope := p.sema_ctx.obtain_root_scope(ffi.pkg) ffi_scope.register(name, context.ScopeObject{ kind = .function is_pub = true + return_type = return_type + params = params + key = key }) } } + p.attributes = []ast.Attribute + if lang == .bait { node.stmts = p.parse_block()! } @@ -142,7 +153,7 @@ fun (mut p Parser) anon_fun() !ast.AnonFun{ stmts := p.parse_block()! return ast.AnonFun{ decl = ast.FunDecl{ - mix_name = '_anon_${p.file_hash}_${p.lexer.offset()}' + name = '_anon_${p.file_hash}_${p.lexer.offset()}' params = params return_type = return_type stmts = stmts @@ -178,8 +189,7 @@ fun (mut p Parser) fun_params() ![]ast.Param{ fun (mut p Parser) fun_call(lang ast.Language) !ast.CallExpr{ pos := p.pos name := p.check_name()! - pkg := p.expr_pkg - mix_name := p.prepend_expr_pkg(name) + pkg := p.get_expr_pkg() p.check(.lpar)! args := p.call_args()! p.check(.rpar)! @@ -189,7 +199,6 @@ fun (mut p Parser) fun_call(lang ast.Language) !ast.CallExpr{ return ast.CallExpr{ pkg = pkg name = name - mix_name = mix_name args = args or_block = or_block pos = pos @@ -210,7 +219,6 @@ fun (mut p Parser) method_call(left ast.Expr) !ast.CallExpr { is_method = true left = left name = name - mix_name = name args = args or_block = or_block pos = pos diff --git a/lib/bait/parser/stmt.bt b/lib/bait/parser/stmt.bt index 54bdc2c0..b06fe0c7 100644 --- a/lib/bait/parser/stmt.bt +++ b/lib/bait/parser/stmt.bt @@ -56,10 +56,9 @@ fun (mut p Parser) script_mode_main() !ast.FunDecl { } mut node := ast.FunDecl{ - mix_name = 'main' + name = "main" return_type = ast.VOID_TYPE } - p.table.fun_decls['main'] = node node.stmts = stmts return node } @@ -372,7 +371,7 @@ fun (mut p Parser) interface_method(name string, rec_type ast.Type) !ast.FunDecl } return ast.FunDecl{ lang = .js - mix_name = name + name = name params = params return_type = return_type is_pub = true diff --git a/lib/bait/transformer/gen_test_main.bt b/lib/bait/transformer/gen_test_main.bt index 94509e44..d689aa95 100644 --- a/lib/bait/transformer/gen_test_main.bt +++ b/lib/bait/transformer/gen_test_main.bt @@ -6,14 +6,12 @@ import bait.ast fun (t Transformer) gen_test_main() &ast.File { mut test_main := ast.FunDecl{ - mix_name = "main" name = "main" } if t.gen_ctx.has_test_begin { test_main.stmts.push(ast.ExprStmt{ expr = ast.CallExpr{ - mix_name = "testsuite_begin" name = "testsuite_begin" } }) @@ -33,7 +31,6 @@ fun (t Transformer) gen_test_main() &ast.File { }) test_main.stmts.push(ast.ExprStmt{ expr = ast.CallExpr{ - mix_name = name name = name } }) @@ -42,7 +39,6 @@ fun (t Transformer) gen_test_main() &ast.File { if t.gen_ctx.has_test_end { test_main.stmts.push(ast.ExprStmt{ expr = ast.CallExpr{ - mix_name = "testsuite_end" name = "testsuite_end" } }) @@ -51,7 +47,6 @@ fun (t Transformer) gen_test_main() &ast.File { // exit(test_runner.exit_code()) test_main.stmts.push(ast.ExprStmt{ expr = ast.CallExpr{ - mix_name = "exit" name = "exit" args = [ ast.CallArg{ @@ -64,6 +59,7 @@ fun (t Transformer) gen_test_main() &ast.File { } }) return ast.File{ + pkg_name = "main" stmts = [test_main as ast.Stmt] } } diff --git a/lib/bait/transformer/transformer.bt b/lib/bait/transformer/transformer.bt index 48c6ce97..df257f6e 100644 --- a/lib/bait/transformer/transformer.bt +++ b/lib/bait/transformer/transformer.bt @@ -230,7 +230,7 @@ fun (t Transformer) index_expr(node ast.IndexExpr) ast.Expr { left = node.left left_type = ast.ARRAY_TYPE return_type = ast.ARRAY_TYPE - mix_name = "slice" + name = "slice" args = [ ast.CallArg{ expr = idx.low diff --git a/tests/out/error/mutability/fun_arg.in.bt b/tests/out/error/mutability/fun_arg.in.bt index d5333fd6..e9cc9e51 100644 --- a/tests/out/error/mutability/fun_arg.in.bt +++ b/tests/out/error/mutability/fun_arg.in.bt @@ -11,5 +11,5 @@ fun immutable(f Foo) {} fun call_change() { mut f := Foo{} change_me(f) // err - immutable(mut f) // err + immutable(mut f) // warn } diff --git a/tests/out/error/mutability/fun_arg.out b/tests/out/error/mutability/fun_arg.out index 11e1ed27..760fb6ad 100644 --- a/tests/out/error/mutability/fun_arg.out +++ b/tests/out/error/mutability/fun_arg.out @@ -1,2 +1,2 @@ +tests/out/error/mutability/fun_arg.in.bt:14:12 warning: unnecessary `mut` for argument `f` tests/out/error/mutability/fun_arg.in.bt:13:12 error: add `mut` to argument for parameter `f` -tests/out/error/mutability/fun_arg.in.bt:14:12 error: unnecessary `mut` for argument `f` diff --git a/tests/type/fun_types_test.bt b/tests/type/fun_types_test.bt index 6f91edd8..15fda410 100644 --- a/tests/type/fun_types_test.bt +++ b/tests/type/fun_types_test.bt @@ -1,11 +1,20 @@ // SPDX-FileCopyrightText: 2023-present Lukas Neubert // SPDX-License-Identifier: MIT +// This struct has to compile struct Foo { fun_field fun (string) i32 } -fun test_placeholder() { - // Above code just has to compile - assert true +fun takes_callback(s string, cb fun (string) i32) i32 { + return cb(s) +} + +fun my_cb(s string) i32 { + return s.length +} + +fun test_callback() { + assert takes_callback("hi", my_cb) == 2 + assert takes_callback("you", my_cb) == 3 }