From 3cb87e51b42db2b80bfdc895f70f83be4562c081 Mon Sep 17 00:00:00 2001 From: Lukas Neubert <40118727+serkonda7@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:21:02 +0100 Subject: [PATCH] fix: register functions in package scope (#291) --- CHANGELOG.md | 3 ++- lib/bait/ast/ast.bt | 9 ++++++++- lib/bait/checker/fun.bt | 19 +++++++++++-------- lib/bait/gen/js/comptime.bt | 2 +- lib/bait/gen/js/expr.bt | 4 ++-- lib/bait/gen/js/fun.bt | 12 ++++++------ lib/bait/gen/js/stmt.bt | 2 +- lib/bait/parser/fun.bt | 21 ++++++++++++++------- lib/os/os.bt | 6 +++--- lib/os/os.c.bt | 4 ++-- lib/os/os.js.bt | 28 ++++++++++++++-------------- tests/out/error/other/is_private.out | 2 +- 12 files changed, 65 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ff59ee0..1264e1af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,10 @@ All notable changes are documented in this file. - Check for import alias name conflicts - 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 ### Other Changes -- fix: `static` visibility now controlled by `pub` keyword +- `static` visibility is now controlled by `pub` keyword - lexer: Disallow floats with trailing decimal point - fix(c): Prevent c error if blank identifier is used diff --git a/lib/bait/ast/ast.bt b/lib/bait/ast/ast.bt index aea59814..f6ebd3a9 100644 --- a/lib/bait/ast/ast.bt +++ b/lib/bait/ast/ast.bt @@ -92,7 +92,6 @@ pub struct FunDecl { pub lang Language pub name string pub mix_name string // TMP - pub mix_pkg string // TMP and TODO make this completely obsolete pub params []Param pub return_type Type pub is_test bool @@ -215,6 +214,14 @@ pub struct CallExpr { pub pos token.Pos } +pub fun (node CallExpr) full_name() string { + if node.pkg.length == 0 { + return node.name + } + + return node.pkg + "." + node.name +} + pub struct CallArg { global typ Type global expr Expr diff --git a/lib/bait/checker/fun.bt b/lib/bait/checker/fun.bt index 41279632..f48622bb 100644 --- a/lib/bait/checker/fun.bt +++ b/lib/bait/checker/fun.bt @@ -95,13 +95,13 @@ 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{ - mix_pkg = c.pkg return_type = info.return_type params = info.to_params() } c.scope.register(p.name, context.ScopeObject{ typ = p.typ kind = .function + pkg = c.pkg }) } else { c.scope.register(p.name, context.ScopeObject{ @@ -130,6 +130,12 @@ 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) + 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 @@ -138,16 +144,13 @@ fun (mut c Checker) fun_call(mut node ast.CallExpr) ast.Type { found = true } } - if not found { - c.error('unknown function ${node.mix_name}', node.pos) - return ast.ERROR_TYPE - } - mut def := c.table.fun_decls[node.mix_name] - if not def.is_pub and def.mix_pkg != c.pkg { - c.error('function ${def.mix_name} is private', node.pos) + // Check visibility + if not obj.is_pub and obj.pkg != c.pkg { + 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 diff --git a/lib/bait/gen/js/comptime.bt b/lib/bait/gen/js/comptime.bt index e55934ca..90e4188c 100644 --- a/lib/bait/gen/js/comptime.bt +++ b/lib/bait/gen/js/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/js/expr.bt b/lib/bait/gen/js/expr.bt index f380cd5f..17392e37 100644 --- a/lib/bait/gen/js/expr.bt +++ b/lib/bait/gen/js/expr.bt @@ -208,7 +208,7 @@ fun (mut g Gen) infix_expr(node ast.InfixExpr){ if node.op == .ne { g.write('!') } - g.write(js_esc(lsym.name + '_' + overload.mix_name)) + g.write(js_esc(lsym.name + '_' + overload.name)) g.write('(') g.expr(node.left) g.write(', ') @@ -325,7 +325,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 := js_esc(final_sym.name) g.write('${name}_str(') diff --git a/lib/bait/gen/js/fun.bt b/lib/bait/gen/js/fun.bt index 380e8669..e126d47d 100644 --- a/lib/bait/gen/js/fun.bt +++ b/lib/bait/gen/js/fun.bt @@ -5,9 +5,9 @@ package js import bait.ast fun (mut g Gen) fun_decl(node ast.FunDecl) { - if node.mix_name == 'testsuite_begin' { + if node.name == 'testsuite_begin' { g.has_test_begin = true - } else if node.mix_name == 'testsuite_end' { + } else if node.name == 'testsuite_end' { g.has_test_end = true } @@ -30,7 +30,7 @@ fun (mut g Gen) fun_decl(node ast.FunDecl) { mut name := '' if node.is_method { sym := g.table.get_sym(node.params[0].typ) - name = js_esc(sym.name + '_' + node.mix_name) + name = js_esc(sym.name + '_' + node.name) } else { name = js_esc(node.mix_name) } @@ -98,7 +98,7 @@ fun (mut g Gen) call_expr(node ast.CallExpr) { fun (mut g Gen) call_expr_no_or(node ast.CallExpr) { if node.is_method and node.lang != .bait { g.expr(node.left) - g.write('.' + node.mix_name + '(') + g.write('.' + node.name + '(') g.call_args(node.args) g.write(')') return @@ -107,7 +107,7 @@ fun (mut g Gen) call_expr_no_or(node ast.CallExpr) { if node.is_field { g.expr(node.left) g.write('.') - g.write(node.mix_name) + g.write(node.name) g.write('(') g.call_args(node.args) g.write(')') @@ -139,7 +139,7 @@ fun (mut g Gen) call_expr_no_or(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/js/stmt.bt b/lib/bait/gen/js/stmt.bt index d18b06db..611d4b61 100644 --- a/lib/bait/gen/js/stmt.bt +++ b/lib/bait/gen/js/stmt.bt @@ -162,7 +162,7 @@ fun (mut g Gen) assign_stmt(node ast.AssignStmt){ if lsym.overloads.contains(node.op.js_repr()) { g.write(' = ') overload := lsym.overloads[node.op.js_repr()] - g.write(js_esc(lsym.name + '_' + overload.mix_name)) + g.write(js_esc(lsym.name + '_' + overload.name)) g.write('(') g.expr(node.left) g.write(', ') diff --git a/lib/bait/parser/fun.bt b/lib/bait/parser/fun.bt index ad4bd800..77dd4c76 100644 --- a/lib/bait/parser/fun.bt +++ b/lib/bait/parser/fun.bt @@ -74,7 +74,6 @@ fun (mut p Parser) fun_decl() !ast.FunDecl{ is_pub = is_pub name = name mix_name = mix_name - mix_pkg = p.pkg_name generic_names = generic_names params = params return_type = return_type @@ -101,12 +100,20 @@ fun (mut p Parser) fun_decl() !ast.FunDecl{ } node.typ = p.table.find_or_register_fun(param_types, return_type, false) - p.pkg_scope.register_unique(mix_name, context.ScopeObject{ - kind = .function - typ = node.typ - pkg = p.pkg_name - is_pub = is_pub - }) + if lang == .bait { + p.pkg_scope.register_unique(name, context.ScopeObject{ + kind = .function + typ = node.typ + pkg = p.pkg_name + is_pub = is_pub + }) + } else { + ffi_scope := p.sema_ctx.obtain_root_scope(ffi.pkg) + ffi_scope.register(name, context.ScopeObject{ + kind = .function + is_pub = true + }) + } } if lang == .bait { diff --git a/lib/os/os.bt b/lib/os/os.bt index 56880fe6..2f9388c2 100644 --- a/lib/os/os.bt +++ b/lib/os/os.bt @@ -2,11 +2,11 @@ // SPDX-License-Identifier: MIT package os -pub fun walk_ext(dir string, ext string) []string { +pub fun walk_ext(basedir string, ext string) []string { mut ext_files := []string - all_files := ls(dir) + all_files := ls(basedir) for file in all_files { - fpath := join_path(dir, [file]) + fpath := join_path(basedir, [file]) if is_dir(fpath) { tmp := walk_ext(fpath, ext) ext_files.push_many(tmp) diff --git a/lib/os/os.c.bt b/lib/os/os.c.bt index 4250a80f..06636157 100644 --- a/lib/os/os.c.bt +++ b/lib/os/os.c.bt @@ -20,12 +20,12 @@ pub fun user_args() []string { return ARGS.slice(1, ARGS.length) } -pub fun ls(dir string) []string { +pub fun ls(d string) []string { mut res := []string #C.'struct dirent *pDirent; DIR *pDir; - pDir = opendir(dir.str); + pDir = opendir(d.str); while((pDirent = readdir(pDir)) != NULL) { if (strcmp(pDirent->d_name, ".") != 0 && strcmp(pDirent->d_name, "..") != 0) { Array_push(&res, (string[]){from_c_string(pDirent->d_name)}); diff --git a/lib/os/os.js.bt b/lib/os/os.js.bt index 9534f02a..e71eb9bd 100644 --- a/lib/os/os.js.bt +++ b/lib/os/os.js.bt @@ -10,8 +10,8 @@ pub fun user_args() []string { return ARGS.slice(2, ARGS.length) } -pub fun ls(dir string) []string { - return from_js_string_arr(#JS.fs.readdirSync(dir.str)) +pub fun ls(d string) []string { + return from_js_string_arr(#JS.fs.readdirSync(d.str)) } pub fun cp(src string, dest string) { @@ -42,8 +42,8 @@ pub fun is_root(path string) bool { return path == '/' } -pub fun chdir(dir string) { - #JS.process.chdir(dir.str) +pub fun chdir(d string) { + #JS.process.chdir(d.str) } pub fun home_dir() string { @@ -60,30 +60,30 @@ pub fun is_dir(path string) bool { return #JS.'JS.fs.lstatSync(path.str).isDirectory()' as bool } -pub fun mkdir(dir string) { - if exists(dir) { +pub fun mkdir(d string) { + if exists(d) { return } - #JS.fs.mkdirSync(dir.str) + #JS.fs.mkdirSync(d.str) } -pub fun mkdir_all(dir string) { - if exists(dir) { +pub fun mkdir_all(d string) { + if exists(d) { return } - #JS.'JS.fs.mkdirSync(dir.str, { recursive: true })' + #JS.'JS.fs.mkdirSync(d.str, { recursive: true })' } pub fun rm(path string) { #JS.fs.rmSync(path.str) } -pub fun rmdir(dir string) { - #JS.fs.rmdirSync(dir.str) +pub fun rmdir(d string) { + #JS.fs.rmdirSync(d.str) } -pub fun rmdir_all(dir string) { - #JS.'JS.fs.rmdirSync(dir.str, { recursive: true })' +pub fun rmdir_all(d string) { + #JS.'JS.fs.rmdirSync(d.str, { recursive: true })' } pub fun read_bytes(path string) []u8 { diff --git a/tests/out/error/other/is_private.out b/tests/out/error/other/is_private.out index 9be70e08..ef911113 100644 --- a/tests/out/error/other/is_private.out +++ b/tests/out/error/other/is_private.out @@ -5,5 +5,5 @@ tests/out/error/other/is_private.in.bt:10:14 error: enum bait.test_pkgs.pubpriv. tests/out/error/other/is_private.in.bt:11:9 error: type bait.test_pkgs.pubpriv.PrivEnum is private tests/out/error/other/is_private.in.bt:13:14 error: const `bait.test_pkgs.pubpriv.PRIV_CONST` is private tests/out/error/other/is_private.in.bt:15:13 error: static `bait.test_pkgs.pubpriv.priv_static` is private -tests/out/error/other/is_private.in.bt:17:9 error: function bait.test_pkgs.pubpriv.priv_fun is private +tests/out/error/other/is_private.in.bt:17:9 error: function `bait.test_pkgs.pubpriv.priv_fun` is private tests/out/error/other/is_private.in.bt:20:4 error: method priv_method is private