diff --git a/LANGUAGE.md b/LANGUAGE.md index 92849e0..332cfed 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -16,8 +16,9 @@ roadmap and milestone history. - file-local relative imports, import aliases, and qualified member access - transparent declaration aliases with `Name :: alias package.Member`; functions/type factories, named types, and globals retain their original declaration or storage identity -- `hide` makes any named top-level declaration package-local; declarations are public by default, - leading underscores are ordinary identifier characters, and imports are always file-local +- bare `hide` and explicit `hide(package)` make a named top-level declaration package-local; + `hide(file)` makes it file-local; declarations are public by default, leading underscores + are ordinary identifier characters, and imports are always file-local - relative `.h` imports as synthetic C header package namespaces - native `name test { ... }` declarations with fallible-void results inferred from `testing.Error` and errors propagated by `try`, plus anonymous @@ -45,7 +46,7 @@ roadmap and milestone history. - void-payload tagged-union variants, anonymous struct payloads, contextual `.variant`, `.variant{payload}`, and `.variant{field = value}` construction - native sum composition with `A | B` for unbacked enums and tagged unions, optionally grouped as `(A | B)`, using program-global `u16` variant ids - fallible channel types `T ! E`, where `E` is a native enum, native struct, tagged union, or supported sum composition; `void ! E` functions complete successfully on fallthrough, and void-success `catch` handlers may fall through without `yield` -- bodyful hidden functions and root `main` may write `T!` to infer a specialization-local error channel from propagated `try` expressions and concretely typed error returns; inference composes only existing named error types, never synthesizes variants, and requires at least one inferred error +- bodyful local functions and root `main` may write `T!` to infer a specialization-local error channel from propagated `try` expressions and concretely typed error returns; inference composes only existing named error types, never synthesizes variants, and requires at least one inferred error #### distinct types diff --git a/README.md b/README.md index e4416db..50ec5ee 100644 --- a/README.md +++ b/README.md @@ -208,14 +208,14 @@ mem :: import "@std/mem" value :: math.sum(other_math.value, 1) ``` -Top-level declarations are public by default. Prefix a declaration with `hide` -to keep it local to its package; sibling files can use it, but importing packages -cannot. Leading underscores have no visibility meaning. Imports are always file-local -and cannot be hidden or re-exported: +Top-level declarations are public by default. Prefix a declaration with bare `hide` +to make it package-local, or spell the scope explicitly with `hide(package)` or +`hide(file)`. Leading underscores have no visibility meaning. Imports are always +file-local and cannot use visibility modifiers or be re-exported: ```bro -hide helper func() i32 { return 42 } -hide State :: struct { value i32 } +hide shared_helper func() i32 { return 42 } +hide(file) implementation_detail func() i32 { return shared_helper() } ``` Current prototype features: diff --git a/compiler/ast/ast.odin b/compiler/ast/ast.odin index 5b11e62..8b397e8 100644 --- a/compiler/ast/ast.odin +++ b/compiler/ast/ast.odin @@ -243,7 +243,7 @@ Function :: struct { generated: bool, analysis_root: bool, test: bool, - package_hidden: bool, + visibility: types.Visibility, has_body: bool, variadic: bool, params: []Param, @@ -266,7 +266,7 @@ Global :: struct { immutable: bool, external: bool, writable: bool, - package_hidden: bool, + visibility: types.Visibility, expr: Expr_Id, diagnostic: source.Diagnostic_Id, } @@ -314,7 +314,7 @@ Declaration_Alias :: struct { target_pkg: Package_Id, target: u32, kind: Declaration_Alias_Kind, - package_hidden: bool, + visibility: types.Visibility, valid: bool, diagnostic: source.Diagnostic_Id, } diff --git a/compiler/checker/checker.odin b/compiler/checker/checker.odin index fe2a899..9c5abc8 100644 --- a/compiler/checker/checker.odin +++ b/compiler/checker/checker.odin @@ -139,17 +139,19 @@ Build_Ctx :: struct { } Function_Index_Entry :: struct { - scope: ast.Package_Id, - hidden: bool, - name: symbol.Id, - id: ast.Function_Id, + scope: ast.Package_Id, + file: ast.File_Id, + visibility: types.Visibility, + name: symbol.Id, + id: ast.Function_Id, } Global_Index_Entry :: struct { - scope: ast.Package_Id, - hidden: bool, - name: symbol.Id, - id: ast.Global_Id, + scope: ast.Package_Id, + file: ast.File_Id, + visibility: types.Visibility, + name: symbol.Id, + id: ast.Global_Id, } Import_Index_Entry :: struct { @@ -1537,12 +1539,17 @@ find_function_symbol :: proc(index: []Function_Index_Entry, scope: ast.Package_I visible := ast.INVALID_FUNCTION for low < len(index) && index[low].scope == scope && index[low].name == name { entry := index[low] - if entry.hidden { + switch entry.visibility { + case .Public: + visible = entry.id + case .Package: if file != ast.INVALID_FILE { + visible = entry.id + } + case .File: + if entry.file == file { return entry.id } - } else { - visible = entry.id } low += 1 } @@ -1564,12 +1571,17 @@ find_global_symbol :: proc(index: []Global_Index_Entry, scope: ast.Package_Id, n visible := ast.INVALID_GLOBAL for low < len(index) && index[low].scope == scope && index[low].name == name { entry := index[low] - if entry.hidden { + switch entry.visibility { + case .Public: + visible = entry.id + case .Package: if file != ast.INVALID_FILE { + visible = entry.id + } + case .File: + if entry.file == file { return entry.id } - } else { - visible = entry.id } low += 1 } @@ -1612,12 +1624,12 @@ build_symbol_indexes :: proc(checker: ^Checker) { if function.generated { continue } - checker.function_index[function_index] = Function_Index_Entry{scope=function.pkg, hidden=function.package_hidden, name=function.name, id=ast.function_id(id)} + checker.function_index[function_index] = Function_Index_Entry{scope=function.pkg, file=function.file, visibility=function.visibility, name=function.name, id=ast.function_id(id)} function_index += 1 } for alias in checker.ast_module.aliases { if alias.valid && alias.kind == .Function { - checker.function_index[function_index] = Function_Index_Entry{scope=alias.pkg, hidden=alias.package_hidden, name=alias.name, id=ast.Function_Id(alias.target)} + checker.function_index[function_index] = Function_Index_Entry{scope=alias.pkg, file=alias.file, visibility=alias.visibility, name=alias.name, id=ast.Function_Id(alias.target)} function_index += 1 } } @@ -1631,12 +1643,12 @@ build_symbol_indexes :: proc(checker: ^Checker) { } checker.global_index = make([]Global_Index_Entry, global_count, checker.allocator) for global, id in checker.ast_module.globals { - checker.global_index[id] = Global_Index_Entry{scope=global.pkg, hidden=global.package_hidden, name=global.name, id=ast.global_id(id)} + checker.global_index[id] = Global_Index_Entry{scope=global.pkg, file=global.file, visibility=global.visibility, name=global.name, id=ast.global_id(id)} } global_index := len(checker.ast_module.globals) for alias in checker.ast_module.aliases { if alias.valid && alias.kind == .Global { - checker.global_index[global_index] = Global_Index_Entry{scope=alias.pkg, hidden=alias.package_hidden, name=alias.name, id=ast.Global_Id(alias.target)} + checker.global_index[global_index] = Global_Index_Entry{scope=alias.pkg, file=alias.file, visibility=alias.visibility, name=alias.name, id=ast.Global_Id(alias.target)} global_index += 1 } } @@ -1737,7 +1749,7 @@ configure_entry_point :: proc(checker: ^Checker) { type_from_syntax(checker, function.result, function.pkg, function.file), &checker.module.types, ) - if function.package_hidden && function.has_body && !function.c_abi && !function.infer_error && + if function.visibility == .Package && function.has_body && !function.c_abi && !function.infer_error && len(function.params) == 0 && !types.is_valid(function.error) && types.equal(result, io_type) { provider = ast.function_id(function_id) } @@ -1774,9 +1786,25 @@ declared_type_named :: proc(checker: ^Checker, pkg: ast.Package_Id, name: symbol } -type_declaration_conflicts :: proc(checker: ^Checker, pkg: ast.Package_Id, name: symbol.Id) -> bool { +declaration_scopes_overlap :: proc( + left_visibility: types.Visibility, + left_file: ast.File_Id, + right_visibility: types.Visibility, + right_file: ast.File_Id, +) -> bool { + return left_visibility != .File || right_visibility != .File || left_file == right_file +} + +type_declaration_conflicts :: proc( + checker: ^Checker, + pkg: ast.Package_Id, + name: symbol.Id, + visibility: types.Visibility, + file: ast.File_Id, +) -> bool { for item in checker.module.types.nodes { - if item.declared && item.pkg == u32(pkg) && item.name == u32(name) { + if item.declared && item.pkg == u32(pkg) && item.name == u32(name) && + declaration_scopes_overlap(visibility, file, item.visibility, ast.File_Id(item.file)) { return true } } @@ -3996,11 +4024,11 @@ validate_declarations :: proc(checker: ^Checker) { function.span, "inferred error channels are not allowed on 'c_func'", ) - } else if !function.package_hidden && !root_main { + } else if function.visibility == .Public && !root_main { checker.template_diagnostics[function_id] = source.add( checker.diagnostics, function.span, - "inferred error channels are only allowed on hidden functions and root main", + "inferred error channels are only allowed on local functions and root main", ) } } @@ -13971,8 +13999,13 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) { symbol_text(checker, function.name), ) } - checker.specs[id].result = types.I64 - spec.result = types.I64 + recovery_result := types.fallible_success(spec.result, &checker.module.types) if function.infer_error else types.INVALID + if !types.is_void(recovery_result) && !types.is_noreturn(recovery_result) && + !is_runtime_type(checker, recovery_result) { + recovery_result = types.I64 + } + checker.specs[id].result = recovery_result + spec.result = recovery_result } for arg in spec.args { if !is_runtime_type(checker, arg) { @@ -14810,26 +14843,29 @@ check :: proc( continue } for previous in ast_module.functions[:index] { - if !previous.generated && previous.pkg == function.pkg && previous.name == function.name { + if !previous.generated && previous.pkg == function.pkg && previous.name == function.name && + declaration_scopes_overlap(previous.visibility, previous.file, function.visibility, function.file) { source.addf(diagnostics, function.span, "duplicate function '%s'", symbol_text(&checker, function.name)) } } for global in ast_module.globals { - if global.pkg == function.pkg && global.name == function.name { + if global.pkg == function.pkg && global.name == function.name && + declaration_scopes_overlap(global.visibility, global.file, function.visibility, function.file) { source.addf(diagnostics, function.span, "package declaration '%s' conflicts with a global", symbol_text(&checker, function.name)) } } - if type_declaration_conflicts(&checker, function.pkg, function.name) { + if type_declaration_conflicts(&checker, function.pkg, function.name, function.visibility, function.file) { source.addf(diagnostics, function.span, "function '%s' shadows visible type", symbol_text(&checker, function.name)) } } for global, index in ast_module.globals { for previous in ast_module.globals[:index] { - if previous.pkg == global.pkg && previous.name == global.name { + if previous.pkg == global.pkg && previous.name == global.name && + declaration_scopes_overlap(previous.visibility, previous.file, global.visibility, global.file) { source.addf(diagnostics, global.span, "duplicate global '%s'", symbol_text(&checker, global.name)) } } - if type_declaration_conflicts(&checker, global.pkg, global.name) { + if type_declaration_conflicts(&checker, global.pkg, global.name, global.visibility, global.file) { source.addf(diagnostics, global.span, "global '%s' shadows visible type", symbol_text(&checker, global.name)) } } diff --git a/compiler/loader/loader.odin b/compiler/loader/loader.odin index 2d3af7d..3b5b6ed 100644 --- a/compiler/loader/loader.odin +++ b/compiler/loader/loader.odin @@ -1249,14 +1249,29 @@ load_package :: proc( return pkg_id } +declaration_scopes_overlap :: proc( + left_visibility: types.Visibility, + left_file: ast.File_Id, + right_visibility: types.Visibility, + right_file: ast.File_Id, +) -> bool { + return left_visibility != .File || right_visibility != .File || left_file == right_file +} + +declaration_visible_in_file :: proc(visibility: types.Visibility, declaration_file, file: ast.File_Id) -> bool { + return visibility != .File || declaration_file == file +} + declaration_conflicts :: proc(module: ^ast.Module, pkg: ast.Package_Id, file: ast.File_Id, name: symbol.Id) -> bool { for function in module.functions { - if function.pkg == pkg && function.name == name { + if function.pkg == pkg && function.name == name && + declaration_visible_in_file(function.visibility, function.file, file) { return true } } for global in module.globals { - if global.pkg == pkg && global.name == name { + if global.pkg == pkg && global.name == name && + declaration_visible_in_file(global.visibility, global.file, file) { return true } } @@ -1327,7 +1342,9 @@ find_visible_enum_global :: proc( public_only := false, ) -> ast.Global_Id { for global, index in module.globals { - if global.pkg == pkg && global.name == name && (!global.package_hidden || !public_only) { + if global.pkg == pkg && global.name == name && + ((public_only && global.visibility == .Public) || + (!public_only && declaration_visible_in_file(global.visibility, global.file, file))) { return ast.global_id(index) } } @@ -1451,17 +1468,20 @@ resolve_enum_values :: proc(state: ^State) { alias_conflicts_with_declaration :: proc(module: ^ast.Module, alias: ast.Declaration_Alias) -> bool { for function in module.functions { - if function.pkg == alias.pkg && function.name == alias.name { + if function.pkg == alias.pkg && function.name == alias.name && + declaration_scopes_overlap(function.visibility, function.file, alias.visibility, alias.file) { return true } } for global in module.globals { - if global.pkg == alias.pkg && global.name == alias.name { + if global.pkg == alias.pkg && global.name == alias.name && + declaration_scopes_overlap(global.visibility, global.file, alias.visibility, alias.file) { return true } } for item in module.type_store.nodes { - if item.declared && item.pkg == u32(alias.pkg) && item.name == u32(alias.name) { + if item.declared && item.pkg == u32(alias.pkg) && item.name == u32(alias.name) && + declaration_scopes_overlap(item.visibility, ast.File_Id(item.file), alias.visibility, alias.file) { return true } } @@ -1473,7 +1493,7 @@ direct_alias_target :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symb target: u32 kinds := 0 for function, index in module.functions { - if function.pkg == pkg && function.name == name && !function.generated && !function.package_hidden { + if function.pkg == pkg && function.name == name && !function.generated && function.visibility == .Public { kind = .Function target = u32(ast.function_id(index)) kinds += 1 @@ -1481,7 +1501,7 @@ direct_alias_target :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symb } } for global, index in module.globals { - if global.pkg == pkg && global.name == name && !global.package_hidden { + if global.pkg == pkg && global.name == name && global.visibility == .Public { kind = .Global target = u32(ast.global_id(index)) kinds += 1 @@ -1498,24 +1518,24 @@ direct_alias_target :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symb return kind, target, kinds } -hidden_alias_target_exists :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symbol.Id) -> bool { +non_public_alias_target_exists :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symbol.Id) -> bool { for function in module.functions { - if function.pkg == pkg && function.name == name && function.package_hidden { + if function.pkg == pkg && function.name == name && function.visibility != .Public { return true } } for global in module.globals { - if global.pkg == pkg && global.name == name && global.package_hidden { + if global.pkg == pkg && global.name == name && global.visibility != .Public { return true } } for item in module.type_store.nodes { - if item.declared && item.pkg == u32(pkg) && item.name == u32(name) && item.package_hidden { + if item.declared && item.pkg == u32(pkg) && item.name == u32(name) && item.visibility != .Public { return true } } for alias in module.aliases { - if alias.valid && alias.pkg == pkg && alias.name == name && alias.package_hidden { + if alias.valid && alias.pkg == pkg && alias.name == name && alias.visibility != .Public { return true } } @@ -1524,7 +1544,7 @@ hidden_alias_target_exists :: proc(module: ^ast.Module, pkg: ast.Package_Id, nam find_public_alias :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symbol.Id) -> int { for alias, index in module.aliases { - if alias.valid && alias.pkg == pkg && alias.name == name && !alias.package_hidden { + if alias.valid && alias.pkg == pkg && alias.name == name && alias.visibility == .Public { return index } } @@ -1581,11 +1601,11 @@ resolve_declaration_alias :: proc(state: ^State, index: int, states: []u8) -> bo return false } - if hidden_alias_target_exists(state.module, alias.target_pkg, alias.member) { + if non_public_alias_target_exists(state.module, alias.target_pkg, alias.member) { alias.diagnostic = source.addf( state.diagnostics, alias.span, - "package member '%s.%s' is package-hidden", + "package member '%s.%s' is not public", symbol.resolve(state.symbols, alias.qualifier), symbol.resolve(state.symbols, alias.member), ) @@ -1611,7 +1631,8 @@ validate_declaration_aliases :: proc(state: ^State) { continue } for previous in state.module.aliases[:index] { - if previous.pkg == alias.pkg && previous.name == alias.name { + if previous.pkg == alias.pkg && previous.name == alias.name && + declaration_scopes_overlap(previous.visibility, previous.file, alias.visibility, alias.file) { alias.diagnostic = source.addf(state.diagnostics, alias.span, "duplicate declaration alias '%s'", name) alias.valid = false break @@ -1661,9 +1682,9 @@ validate_declaration_aliases :: proc(state: ^State) { u32(alias.pkg), u32(alias.name), file=u32(alias.file), - package_hidden=alias.package_hidden, + visibility=alias.visibility, ) - if !types.define_alias(&state.module.type_store, id, types.Type(alias.target), alias.package_hidden) { + if !types.define_alias(&state.module.type_store, id, types.Type(alias.target), alias.visibility) { alias.diagnostic = source.addf(state.diagnostics, alias.span, "duplicate type declaration '%s'", symbol.resolve(state.symbols, alias.name)) alias.valid = false } diff --git a/compiler/parser/parser.odin b/compiler/parser/parser.odin index 3808791..801603a 100644 --- a/compiler/parser/parser.odin +++ b/compiler/parser/parser.odin @@ -31,7 +31,12 @@ Parser :: struct { capture_pipe: bool, // A parenthesized `if` condition ends before a leading-dot brace-less body. if_condition: bool, - hidden_names: [dynamic]symbol.Id, + local_names: [dynamic]Local_Name, +} + +Local_Name :: struct { + name: symbol.Id, + visibility: types.Visibility, } MAX_EXPRESSION_NESTING :: 256 @@ -43,16 +48,16 @@ token_text :: proc(parser: ^Parser, tok: token.Token) -> string { return parser.source_file.text[int(tok.span.start):int(tok.span.end)] } -package_hidden_name :: proc(parser: ^Parser, name: symbol.Id) -> bool { - for hidden in parser.hidden_names { - if hidden == name { - return true +declaration_visibility :: proc(parser: ^Parser, name: symbol.Id) -> types.Visibility { + for local in parser.local_names { + if local.name == name { + return local.visibility } } - return false + return .Public } -collect_hidden_names :: proc(parser: ^Parser) { +collect_local_names :: proc(parser: ^Parser) { depth := 0 for item, index in parser.tokens.items { #partial switch item.kind { @@ -61,9 +66,32 @@ collect_hidden_names :: proc(parser: ^Parser) { case .Right_Brace: depth = max(depth-1, 0) case .Keyword_Hide: - if depth == 0 && index+1 < len(parser.tokens.items) && - parser.tokens.items[index+1].kind == .Identifier { - append(&parser.hidden_names, parser.tokens.items[index+1].symbol) + if depth != 0 { + continue + } + visibility := types.Visibility.Package + name_index := index+1 + if name_index < len(parser.tokens.items) && + parser.tokens.items[name_index].kind == .Left_Paren { + if index+4 >= len(parser.tokens.items) || + parser.tokens.items[index+2].kind != .Identifier || + parser.tokens.items[index+3].kind != .Right_Paren { + continue + } + scope := token_text(parser, parser.tokens.items[index+2]) + if scope == "file" { + visibility = .File + } else if scope != "package" { + continue + } + name_index = index+4 + } + if name_index < len(parser.tokens.items) && + parser.tokens.items[name_index].kind == .Identifier { + append(&parser.local_names, Local_Name{ + name=parser.tokens.items[name_index].symbol, + visibility=visibility, + }) } case: } @@ -437,7 +465,7 @@ parse_type_atom :: proc(parser: ^Parser) -> ast.Type_Syntax { u32(name.symbol), u32(qualifier), u32(parser.file), - !symbol.is_valid(qualifier) && package_hidden_name(parser, name.symbol), + types.Visibility.Public if symbol.is_valid(qualifier) else declaration_visibility(parser, name.symbol), ) if token_text(parser, name) == "struct_type" && current(parser).kind == .Bang && peek(parser).kind == .Left_Paren { @@ -2605,7 +2633,7 @@ parse_while :: proc(parser: ^Parser) -> ast.Stmt_Id { return id } -parse_function :: proc(parser: ^Parser, name: token.Token, c_abi, package_hidden: bool) { +parse_function :: proc(parser: ^Parser, name: token.Token, c_abi: bool, visibility: types.Visibility) { advance(parser) if _, ok := allow(parser, .Left_Paren); !ok { source.add(parser.diagnostics, current(parser).span, "expected '(' after 'func'") @@ -2633,7 +2661,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi, package_hidden pkg=parser.pkg, file=parser.file, c_abi=c_abi, - package_hidden=package_hidden, + visibility=visibility, has_body=false, variadic=variadic, params=params, @@ -2653,7 +2681,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi, package_hidden pkg=parser.pkg, file=parser.file, c_abi=c_abi, - package_hidden=package_hidden, + visibility=visibility, has_body=true, variadic=variadic, params=params, @@ -2838,9 +2866,9 @@ parse_inline_union_type :: proc(parser: ^Parser) -> types.Type { return types.union_anonymous(&parser.module.type_store, fields[:], tag) } -parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout, package_hidden: bool, is_union := false) { +parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, visibility: types.Visibility, is_union := false) { start := advance(parser) - id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), package_hidden=package_hidden) + id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), visibility=visibility) // A tagged union spells its discriminant in parens: `union(Enum)` reuses an existing // enum; `union(enum)` synthesizes one from the variant names after the body is parsed. tag := types.INVALID @@ -2874,7 +2902,7 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout, package_hidde "native union declarations require a body" if is_union else "native struct declarations require a body", ) } - if !types.define_record(&parser.module.type_store, id, nil, c_layout, true, is_union, tag=tag, declared_tag=declared_tag, package_hidden=package_hidden) { + if !types.define_record(&parser.module.type_store, id, nil, c_layout, true, is_union, tag=tag, declared_tag=declared_tag, visibility=visibility) { source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name)) } if !ended_by_newline { @@ -2911,7 +2939,7 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout, package_hidde if is_union && (inferred_tag || types.is_valid(declared_tag)) { tag = synthesize_union_tag(parser, fields[:]) } - if !types.define_record(&parser.module.type_store, id, fields[:], c_layout, false, is_union, tag=tag, declared_tag=declared_tag, tuple=tuple, package_hidden=package_hidden) { + if !types.define_record(&parser.module.type_store, id, fields[:], c_layout, false, is_union, tag=tag, declared_tag=declared_tag, tuple=tuple, visibility=visibility) { source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name)) } else if !c_layout && !is_union && !tuple { for value, index in defaults { @@ -2930,10 +2958,10 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout, package_hidde _ = finish_statement(parser) } -parse_opaque :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) { +parse_opaque :: proc(parser: ^Parser, name: token.Token, visibility: types.Visibility) { start := advance(parser) - id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), package_hidden=package_hidden) - if !types.define_record(&parser.module.type_store, id, nil, false, true, false, package_hidden=package_hidden) { + id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), visibility=visibility) + if !types.define_record(&parser.module.type_store, id, nil, false, true, false, visibility=visibility) { source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name)) } if current(parser).kind == .Left_Brace { @@ -2958,11 +2986,11 @@ synthesize_union_tag :: proc(parser: ^Parser, fields: []types.Field) -> types.Ty return types.enum_anonymous(&parser.module.type_store, members, types.U16) } -parse_distinct :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) { +parse_distinct :: proc(parser: ^Parser, name: token.Token, visibility: types.Visibility) { start := advance(parser) child := parse_type(parser) - id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), package_hidden=package_hidden) - if !types.define_distinct(&parser.module.type_store, id, child, package_hidden) { + id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), visibility=visibility) + if !types.define_distinct(&parser.module.type_store, id, child, visibility) { source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name)) } if !types.is_valid(child) { @@ -2971,7 +2999,7 @@ parse_distinct :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) _ = finish_statement(parser) } -parse_alias :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) { +parse_alias :: proc(parser: ^Parser, name: token.Token, visibility: types.Visibility) { start := advance(parser) saved := parser.cursor if current(parser).kind == .Identifier && peek(parser).kind == .Dot { @@ -2988,7 +3016,7 @@ parse_alias :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) { pkg=parser.pkg, file=parser.file, target_pkg=ast.INVALID_PACKAGE, - package_hidden=package_hidden, + visibility=visibility, valid=true, diagnostic=source.INVALID_DIAGNOSTIC, }) @@ -2999,8 +3027,8 @@ parse_alias :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) { } parser.cursor = saved child := parse_type(parser) - id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), package_hidden=package_hidden) - if !types.define_alias(&parser.module.type_store, id, child, package_hidden) { + id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), visibility=visibility) + if !types.define_alias(&parser.module.type_store, id, child, visibility) { source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name)) } if !types.is_valid(child) { @@ -3143,7 +3171,7 @@ parse_inline_enum_type :: proc(parser: ^Parser) -> types.Type { return types.enum_anonymous(&parser.module.type_store, members[:], backing) } -parse_enum :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) { +parse_enum :: proc(parser: ^Parser, name: token.Token, visibility: types.Visibility) { start := advance(parser) explicit_backing := false backing := types.INVALID @@ -3165,8 +3193,8 @@ parse_enum :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) { _ = finish_statement(parser) return } - id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), package_hidden=package_hidden) - if !types.define_enum(&parser.module.type_store, id, backing, members[:], explicit_backing, package_hidden) { + id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol), file=u32(parser.file), visibility=visibility) + if !types.define_enum(&parser.module.type_store, id, backing, members[:], explicit_backing, visibility) { source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name)) } if explicit_backing && deferred { @@ -3295,10 +3323,37 @@ parse_test :: proc(parser: ^Parser, name: token.Token) { } parse_top_level :: proc(parser: ^Parser) { - hide_token, package_hidden := allow(parser, .Keyword_Hide) + modifier := current(parser) + visibility := types.Visibility.Public + has_modifier := modifier.kind == .Keyword_Hide + if has_modifier { + visibility = .Package + advance(parser) + if _, scoped := allow(parser, .Left_Paren); scoped { + if current(parser).kind == .Identifier { + scope := advance(parser) + scope_text := token_text(parser, scope) + if scope_text == "file" { + visibility = .File + } else if scope_text != "package" { + source.addf( + parser.diagnostics, + scope.span, + "unknown hide scope '%s'; expected 'package' or 'file'", + scope_text, + ) + } + } else { + source.add(parser.diagnostics, current(parser).span, "expected 'package' or 'file' in hide scope") + } + if _, closed := allow(parser, .Right_Paren); !closed { + source.add(parser.diagnostics, current(parser).span, "expected ')' after hide scope") + } + } + } if current(parser).kind == .Keyword_Test && peek(parser).kind == .Keyword_Import { - if package_hidden { - source.add(parser.diagnostics, hide_token.span, "test imports cannot use 'hide'") + if has_modifier { + source.add(parser.diagnostics, modifier.span, "test imports cannot use a visibility modifier") } start := advance(parser) advance(parser) // consume 'import' @@ -3306,15 +3361,15 @@ parse_top_level :: proc(parser: ^Parser) { return } if current(parser).kind == .Keyword_Import { - if package_hidden { - source.add(parser.diagnostics, hide_token.span, "imports are already file-local and cannot use 'hide'") + if has_modifier { + source.add(parser.diagnostics, modifier.span, "imports are already file-local and cannot use a visibility modifier") } start := advance(parser) parse_import(parser, token.Token{}, start) return } if current(parser).kind != .Identifier { - message := "expected a declaration name after 'hide'" if package_hidden else "expected a top-level declaration" + message := "expected a declaration name after visibility modifier" if has_modifier else "expected a top-level declaration" source.add(parser.diagnostics, current(parser).span, message) for current(parser).kind != .Newline && current(parser).kind != .Eof { advance(parser) @@ -3324,8 +3379,8 @@ parse_top_level :: proc(parser: ^Parser) { } name := advance(parser) if current(parser).kind == .Keyword_Test { - if package_hidden { - source.add(parser.diagnostics, hide_token.span, "test declarations cannot use 'hide'") + if has_modifier { + source.add(parser.diagnostics, modifier.span, "test declarations cannot use a visibility modifier") } parse_test(parser, name) return @@ -3335,8 +3390,8 @@ parse_top_level :: proc(parser: ^Parser) { advance(parser) skip_newlines(parser) if current(parser).kind == .Keyword_Import { - if package_hidden { - source.add(parser.diagnostics, hide_token.span, "imports are already file-local and cannot use 'hide'") + if has_modifier { + source.add(parser.diagnostics, modifier.span, "imports are already file-local and cannot use a visibility modifier") } start := advance(parser) parse_import(parser, name, start) @@ -3346,7 +3401,7 @@ parse_top_level :: proc(parser: ^Parser) { } if current(parser).kind == .Keyword_Func || current(parser).kind == .Keyword_C_Func { c_abi := current(parser).kind == .Keyword_C_Func - parse_function(parser, name, c_abi, package_hidden) + parse_function(parser, name, c_abi, visibility) return } type_syntax := types.INVALID @@ -3375,32 +3430,32 @@ parse_top_level :: proc(parser: ^Parser) { source.add(parser.diagnostics, span_from(name.span, current(parser).span), "function declarations do not use '::'; write 'name func(...)' or 'name c_func(...)'") c_abi := current(parser).kind == .Keyword_C_Func - parse_function(parser, name, c_abi, package_hidden) + parse_function(parser, name, c_abi, visibility) return } if operator.kind == .Colon_Colon && (current(parser).kind == .Keyword_Struct || current(parser).kind == .Keyword_C_Struct) { - parse_struct(parser, name, current(parser).kind == .Keyword_C_Struct, package_hidden) + parse_struct(parser, name, current(parser).kind == .Keyword_C_Struct, visibility) return } if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Opaque { - parse_opaque(parser, name, package_hidden) + parse_opaque(parser, name, visibility) return } if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Union { - parse_struct(parser, name, false, package_hidden, is_union=true) + parse_struct(parser, name, false, visibility, is_union=true) return } if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Enum { - parse_enum(parser, name, package_hidden) + parse_enum(parser, name, visibility) return } if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Distinct { - parse_distinct(parser, name, package_hidden) + parse_distinct(parser, name, visibility) return } if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Alias { - parse_alias(parser, name, package_hidden) + parse_alias(parser, name, visibility) return } @@ -3411,7 +3466,7 @@ parse_top_level :: proc(parser: ^Parser) { name=name.symbol, pkg=parser.pkg, file=parser.file, - package_hidden=package_hidden, + visibility=visibility, type=type_syntax, immutable=operator.kind == .Colon_Colon, expr=expr, @@ -3432,9 +3487,9 @@ parse :: proc( diagnostics=diagnostics, module=ast.init_module(allocator), } - parser.hidden_names.allocator = allocator - defer delete(parser.hidden_names) - collect_hidden_names(&parser) + parser.local_names.allocator = allocator + defer delete(parser.local_names) + collect_local_names(&parser) skip_newlines(&parser) for current(&parser).kind != .Eof { parse_top_level(&parser) @@ -3459,9 +3514,9 @@ parse_into :: proc( pkg=pkg, file=file, } - parser.hidden_names.allocator = module.allocator - defer delete(parser.hidden_names) - collect_hidden_names(&parser) + parser.local_names.allocator = module.allocator + defer delete(parser.local_names) + collect_local_names(&parser) skip_newlines(&parser) for current(&parser).kind != .Eof { parse_top_level(&parser) diff --git a/compiler/types/types.odin b/compiler/types/types.odin index d1e4789..ee3baa4 100644 --- a/compiler/types/types.odin +++ b/compiler/types/types.odin @@ -56,6 +56,12 @@ Numeric_Category :: enum u8 { Float, } +Visibility :: enum u8 { + Public, + Package, + File, +} + Kind :: enum u8 { Invalid, Void, @@ -109,7 +115,7 @@ Node :: struct { tuple: bool, opaque: bool, declared: bool, - package_hidden: bool, + visibility: Visibility, explicit_backing: bool, } @@ -193,13 +199,21 @@ intern :: proc(store: ^Store, candidate: Node) -> Type { return id } -named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xffff_ffff, package_hidden := false) -> Type { - normalized_file := file if qualifier != 0 else u32(0) +named :: proc( + store: ^Store, + pkg, name: u32, + qualifier: u32 = 0, + file: u32 = 0xffff_ffff, + visibility := Visibility.Public, +) -> Type { + normalized_file := file if qualifier != 0 || visibility == .File else u32(0) for existing, index in store.nodes { if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Distinct || existing.kind == .Enum || existing.kind == .Struct || existing.kind == .Union) && existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier && - (qualifier == 0 || existing.file == normalized_file) { + ((qualifier != 0 && existing.file == normalized_file) || + (qualifier == 0 && visibility == .File && existing.visibility == .File && existing.file == normalized_file) || + (qualifier == 0 && visibility != .File && existing.visibility != .File)) { return DYNAMIC_START+Type(index) } } @@ -209,22 +223,41 @@ named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xf name=name, qualifier=qualifier, file=normalized_file, + visibility=visibility, }) } find_named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xffff_ffff) -> Type { + fallback := INVALID for existing, index in store.nodes { - if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Distinct || - existing.kind == .Enum || existing.kind == .Struct || existing.kind == .Union) && - existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier && - (!existing.package_hidden || file != 0xffff_ffff) { - return DYNAMIC_START+Type(index) + if (existing.kind != .Named && existing.kind != .Alias && existing.kind != .Distinct && + existing.kind != .Enum && existing.kind != .Struct && existing.kind != .Union) || + existing.pkg != pkg || existing.name != name || existing.qualifier != qualifier { + continue + } + if qualifier != 0 { + if existing.file == file { + return DYNAMIC_START+Type(index) + } + continue + } + switch existing.visibility { + case .Public: + fallback = DYNAMIC_START+Type(index) + case .Package: + if file != 0xffff_ffff { + fallback = DYNAMIC_START+Type(index) + } + case .File: + if existing.file == file { + return DYNAMIC_START+Type(index) + } } } - return INVALID + return fallback } -define_alias :: proc(store: ^Store, id, child: Type, package_hidden := false) -> bool { +define_alias :: proc(store: ^Store, id, child: Type, visibility := Visibility.Public) -> bool { existing, ok := node(store, id) if !ok || existing.kind != .Named || existing.declared { return false @@ -232,12 +265,12 @@ define_alias :: proc(store: ^Store, id, child: Type, package_hidden := false) -> index := int(id-DYNAMIC_START) store.nodes[index].kind = .Alias store.nodes[index].child = child - store.nodes[index].package_hidden = package_hidden + store.nodes[index].visibility = visibility store.nodes[index].declared = true return true } -define_distinct :: proc(store: ^Store, id, child: Type, package_hidden := false) -> bool { +define_distinct :: proc(store: ^Store, id, child: Type, visibility := Visibility.Public) -> bool { existing, ok := node(store, id) if !ok || existing.kind != .Named || existing.declared { return false @@ -245,12 +278,12 @@ define_distinct :: proc(store: ^Store, id, child: Type, package_hidden := false) index := int(id-DYNAMIC_START) store.nodes[index].kind = .Distinct store.nodes[index].child = child - store.nodes[index].package_hidden = package_hidden + store.nodes[index].visibility = visibility store.nodes[index].declared = true return true } -define_enum :: proc(store: ^Store, id, backing: Type, members: []Enum_Member, explicit_backing: bool, package_hidden := false) -> bool { +define_enum :: proc(store: ^Store, id, backing: Type, members: []Enum_Member, explicit_backing: bool, visibility := Visibility.Public) -> bool { existing, ok := node(store, id) if !ok || existing.kind != .Named || existing.declared { return false @@ -261,7 +294,7 @@ define_enum :: proc(store: ^Store, id, backing: Type, members: []Enum_Member, ex store.nodes[index].field_start = u32(len(store.enum_members)) store.nodes[index].field_count = u32(len(members)) store.nodes[index].explicit_backing = explicit_backing - store.nodes[index].package_hidden = package_hidden + store.nodes[index].visibility = visibility store.nodes[index].declared = true append(&store.enum_members, ..members) return true @@ -278,7 +311,7 @@ define_record :: proc( tag: Type = INVALID, declared_tag: Type = INVALID, tuple := false, - package_hidden := false, + visibility := Visibility.Public, ) -> bool { existing, ok := node(store, id) if !ok || (existing.kind != .Named && existing.kind != .Struct && existing.kind != .Union) || @@ -290,7 +323,7 @@ define_record :: proc( store.nodes[index].c_layout = c_layout store.nodes[index].tuple = tuple store.nodes[index].opaque = opaque - store.nodes[index].package_hidden = package_hidden + store.nodes[index].visibility = visibility store.nodes[index].declared = true store.nodes[index].explicit_size = explicit_size store.nodes[index].explicit_alignment = explicit_alignment diff --git a/compiler_tests.odin b/compiler_tests.odin index cac7fcb..5bbf7cf 100644 --- a/compiler_tests.odin +++ b/compiler_tests.odin @@ -3251,6 +3251,7 @@ inferred_error_channels_reject_unstable_or_untyped_contracts :: proc(t: ^testing Case :: struct { source: string, message: string, + absent: string, } cases := [?]Case{ { @@ -3259,7 +3260,7 @@ fail func() void ! Error { return .failed } visible func() void! { try fail() } main func() void {} `, - message="inferred error channels are only allowed on hidden functions and root main", + message="inferred error channels are only allowed on local functions and root main", }, { source=`Error :: enum { failed } @@ -3281,6 +3282,20 @@ main func() void { empty() catch |_| {} } `, message="could not infer a named error channel for 'empty'", }, + { + source=`Error :: enum { failed } +fail func() void ! Error { return .failed } +value func() i32 { return 1 } +main func() void! { + fail() catch |_| { + return + } + _ = try value() +} +`, + message="could not infer a named error channel for 'main'", + absent="non-void function must return a value", + }, } for test_case in cases { source_file := source.Source{path="test.bro", text=test_case.source} @@ -3291,10 +3306,14 @@ main func() void { empty() catch |_| {} } hir_module := checker.check(&ast_module, &diagnostics, &symbols) found := false + unexpected := false for diagnostic in diagnostics.items { found = found || strings.contains(diagnostic.message, test_case.message) + unexpected = unexpected || + len(test_case.absent) > 0 && strings.contains(diagnostic.message, test_case.absent) } testing.expect(t, found) + testing.expect(t, !unexpected) hir.destroy_module(&hir_module) ast.destroy_module(&ast_module) @@ -10345,14 +10364,18 @@ imports_are_file_local :: proc(t: ^testing.T) { } @(test) -hide_is_rejected_outside_named_top_level_declarations :: proc(t: ^testing.T) { +visibility_modifiers_are_rejected_outside_named_top_level_declarations :: proc(t: ^testing.T) { cases := [?]string{ `hide import "../dep"`, - `hide dep :: import "../dep"`, + `hide(file) dep :: import "../dep"`, `hide func() void {}`, `main func(hide value i32) void {}`, `Box :: struct { hide i32 }`, `main func() void { hide value i32 := 1 }`, + `hide() value :: 1`, + `hide(module) value :: 1`, + `hide("file") value :: 1`, + `hide(file value :: 1`, } for text in cases { source_file := source.Source{path="test.bro", text=text} @@ -10370,17 +10393,17 @@ hide_is_rejected_outside_named_top_level_declarations :: proc(t: ^testing.T) { } @(test) -hide_declarations_are_package_hidden :: proc(t: ^testing.T) { - output := "/tmp/brolang-test-hidden-declarations" +local_declarations_resolve_at_their_declared_scope :: proc(t: ^testing.T) { + output := "/tmp/brolang-test-local-declarations" defer _ = os.remove(output) status := compiler_core.compile_package("examples/packages/hidden_valid/app", output) testing.expect_value(t, status, 0) state := run_executable(output) - testing.expect_value(t, state.exit_code, 9) + testing.expect_value(t, state.exit_code, 15) } @(test) -package_hidden_declarations_resolve_locally_but_not_through_imports :: proc(t: ^testing.T) { +package_and_file_local_visibility_is_enforced :: proc(t: ^testing.T) { sources := source.init_store() defer source.destroy_store(&sources) diagnostics := source.init_store_diagnostics(&sources) @@ -10398,18 +10421,27 @@ package_hidden_declarations_resolve_locally_but_not_through_imports :: proc(t: ^ found_sibling_type := false found_import := false found_collision := false + found_file_sibling := false + found_file_sibling_value := false + found_file_sibling_type := false for diagnostic in diagnostics.items { found_sibling = found_sibling || strings.contains(diagnostic.message, "unknown symbol 'sibling'") found_sibling_value = found_sibling_value || strings.contains(diagnostic.message, "unknown symbol 'sibling_value'") found_sibling_type = found_sibling_type || strings.contains(diagnostic.message, "unknown or opaque record type 'Sibling'") found_import = found_import || strings.contains(diagnostic.message, "package 'dep' has no member 'secret'") found_collision = found_collision || strings.contains(diagnostic.message, "duplicate function 'collision'") + found_file_sibling = found_file_sibling || strings.contains(diagnostic.message, "unknown symbol 'file_sibling'") + found_file_sibling_value = found_file_sibling_value || strings.contains(diagnostic.message, "unknown symbol 'file_sibling_value'") + found_file_sibling_type = found_file_sibling_type || strings.contains(diagnostic.message, "unknown or opaque record type 'File_Sibling'") } testing.expect(t, !found_sibling) testing.expect(t, !found_sibling_value) testing.expect(t, !found_sibling_type) testing.expect(t, found_import) testing.expect(t, found_collision) + testing.expect(t, found_file_sibling) + testing.expect(t, found_file_sibling_value) + testing.expect(t, found_file_sibling_type) } @(test) @@ -10704,7 +10736,7 @@ main func() void {} testing.expect(t, loaded) wants := []string{ "has no member 'missing'", - "is package-hidden", + "is not public", "unknown symbol 'nope'", "unavailable imported package 'gone'", "package member 'dep.ambiguous' is ambiguous", diff --git a/examples/packages/hidden_invalid/app/a.bro b/examples/packages/hidden_invalid/app/a.bro index 3aa02fe..85c7606 100644 --- a/examples/packages/hidden_invalid/app/a.bro +++ b/examples/packages/hidden_invalid/app/a.bro @@ -8,6 +8,16 @@ hide Sibling :: struct { hide sibling_value :: 1 +hide(file) file_sibling func() i32 { + return 1 +} + +hide(file) File_Sibling :: struct { + value i32 +} + +hide(file) file_sibling_value :: 1 + collision c_func() i32 { return 1 } diff --git a/examples/packages/hidden_invalid/app/b.bro b/examples/packages/hidden_invalid/app/b.bro index 5e527a2..95ed97d 100644 --- a/examples/packages/hidden_invalid/app/b.bro +++ b/examples/packages/hidden_invalid/app/b.bro @@ -12,6 +12,12 @@ read_sibling_value func() i32 { return sibling_value } -main func() i32 { - return sibling() + dep.secret() + read_sibling(Sibling { value = 1 }) + read_sibling_value() +read_file_sibling func(value File_Sibling) i32 { + return value.value +} + +main func() i32 { + return file_sibling() + file_sibling_value + sibling() + dep.secret() + + read_sibling(Sibling { value = 1 }) + read_sibling_value() + + read_file_sibling(File_Sibling { value = 1 }) } diff --git a/examples/packages/hidden_invalid/dep/dep.bro b/examples/packages/hidden_invalid/dep/dep.bro index 6c976bd..50428f3 100644 --- a/examples/packages/hidden_invalid/dep/dep.bro +++ b/examples/packages/hidden_invalid/dep/dep.bro @@ -1,3 +1,3 @@ -hide secret c_func() i32 { +hide(package) secret c_func() i32 { return 1 } diff --git a/examples/packages/hidden_valid/app/a.bro b/examples/packages/hidden_valid/app/a.bro index 0ed8f57..3b0e93b 100644 --- a/examples/packages/hidden_valid/app/a.bro +++ b/examples/packages/hidden_valid/app/a.bro @@ -1,4 +1,4 @@ -hide helper func() i32 { +hide(package) helper func() i32 { thing Thing := Thing { value = value } return thing.value } @@ -38,7 +38,18 @@ hide Local_C_Record :: c_struct { value c_int } +hide(file) file_helper func() i32 { + return 1 +} + +hide(file) File_Thing :: struct { + value i32 +} + +hide(file) file_value :: 1 + from_a func() i32 { record Local_C_Record := Local_C_Record { value = 0 } - return helper() + local_foreign() + i32(record.value) + thing File_Thing := File_Thing { value = file_value } + return helper() + local_foreign() + file_helper() + thing.value + i32(record.value) } diff --git a/examples/packages/hidden_valid/app/b.bro b/examples/packages/hidden_valid/app/b.bro index d88239f..85ca4bc 100644 --- a/examples/packages/hidden_valid/app/b.bro +++ b/examples/packages/hidden_valid/app/b.bro @@ -5,9 +5,21 @@ Box :: struct { _value i32 } +hide(file) file_helper func() i32 { + return 2 +} + +hide(file) File_Thing :: struct { + value i32 +} + +hide(file) file_value :: 2 + from_b func(_input i32) i32 { _local Box := Box { _value = _input } record _C_Record := _C_Record { value = 0 } thing Thing := Thing { value = value } - return helper() + thing.value + _local._value + _foreign() + i32(record.value) + dep._visible() + file_thing File_Thing := File_Thing { value = file_value } + return helper() + thing.value + _local._value + _foreign() + i32(record.value) + + dep._visible() + file_helper() + file_thing.value } diff --git a/testbed/lexer/std/io/io.bro b/testbed/lexer/std/io/io.bro index a38ef13..07b8360 100644 --- a/testbed/lexer/std/io/io.bro +++ b/testbed/lexer/std/io/io.bro @@ -91,7 +91,7 @@ hide system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usi } } -hide system_write func(_ ?*mut anyopaque, stream WriteStream, bytes []u8) usize ! WriteError { +hide system_write func(_ ?*mut anyopaque, stream WriteStream, bytes []mut u8) usize ! WriteError { fd c_int :: c_int(stream) request usize := bytes.len maximum usize :: usize(maxval!(c_long)) diff --git a/testbed/mem_alloc/std/mem/mem.bro b/testbed/mem_alloc/std/mem/mem.bro index 11fdeaa..c7c6642 100644 --- a/testbed/mem_alloc/std/mem/mem.bro +++ b/testbed/mem_alloc/std/mem/mem.bro @@ -80,7 +80,7 @@ hide power_of_two func(value usize) bool { return true } -hide c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 { +hide c_alloc func(_ ?@mut anyopaque, size usize, alignment usize) ?*mut u8 { if power_of_two(alignment) == false { return null } @@ -98,7 +98,7 @@ hide c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 { return ptrcast!(u8, memory[0]) } -hide c_realloc func(_ ?*mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 { +hide c_realloc func(_ ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 { if power_of_two(alignment) == false { return null }