file-local / package-local decls
This commit is contained in:
+4
-3
@@ -16,8 +16,9 @@ roadmap and milestone history.
|
|||||||
- file-local relative imports, import aliases, and qualified member access
|
- file-local relative imports, import aliases, and qualified member access
|
||||||
- transparent declaration aliases with `Name :: alias package.Member`; functions/type factories,
|
- transparent declaration aliases with `Name :: alias package.Member`; functions/type factories,
|
||||||
named types, and globals retain their original declaration or storage identity
|
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,
|
- bare `hide` and explicit `hide(package)` make a named top-level declaration package-local;
|
||||||
leading underscores are ordinary identifier characters, and imports are always file-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
|
- relative `.h` imports as synthetic C header package namespaces
|
||||||
- native `name test { ... }` declarations with fallible-void results inferred from `testing.Error`
|
- native `name test { ... }` declarations with fallible-void results inferred from `testing.Error`
|
||||||
and errors propagated by `try`, plus anonymous
|
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
|
- 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
|
- 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`
|
- 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
|
#### distinct types
|
||||||
|
|
||||||
|
|||||||
@@ -208,14 +208,14 @@ mem :: import "@std/mem"
|
|||||||
value :: math.sum(other_math.value, 1)
|
value :: math.sum(other_math.value, 1)
|
||||||
```
|
```
|
||||||
|
|
||||||
Top-level declarations are public by default. Prefix a declaration with `hide`
|
Top-level declarations are public by default. Prefix a declaration with bare `hide`
|
||||||
to keep it local to its package; sibling files can use it, but importing packages
|
to make it package-local, or spell the scope explicitly with `hide(package)` or
|
||||||
cannot. Leading underscores have no visibility meaning. Imports are always file-local
|
`hide(file)`. Leading underscores have no visibility meaning. Imports are always
|
||||||
and cannot be hidden or re-exported:
|
file-local and cannot use visibility modifiers or be re-exported:
|
||||||
|
|
||||||
```bro
|
```bro
|
||||||
hide helper func() i32 { return 42 }
|
hide shared_helper func() i32 { return 42 }
|
||||||
hide State :: struct { value i32 }
|
hide(file) implementation_detail func() i32 { return shared_helper() }
|
||||||
```
|
```
|
||||||
|
|
||||||
Current prototype features:
|
Current prototype features:
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ Function :: struct {
|
|||||||
generated: bool,
|
generated: bool,
|
||||||
analysis_root: bool,
|
analysis_root: bool,
|
||||||
test: bool,
|
test: bool,
|
||||||
package_hidden: bool,
|
visibility: types.Visibility,
|
||||||
has_body: bool,
|
has_body: bool,
|
||||||
variadic: bool,
|
variadic: bool,
|
||||||
params: []Param,
|
params: []Param,
|
||||||
@@ -266,7 +266,7 @@ Global :: struct {
|
|||||||
immutable: bool,
|
immutable: bool,
|
||||||
external: bool,
|
external: bool,
|
||||||
writable: bool,
|
writable: bool,
|
||||||
package_hidden: bool,
|
visibility: types.Visibility,
|
||||||
expr: Expr_Id,
|
expr: Expr_Id,
|
||||||
diagnostic: source.Diagnostic_Id,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
@@ -314,7 +314,7 @@ Declaration_Alias :: struct {
|
|||||||
target_pkg: Package_Id,
|
target_pkg: Package_Id,
|
||||||
target: u32,
|
target: u32,
|
||||||
kind: Declaration_Alias_Kind,
|
kind: Declaration_Alias_Kind,
|
||||||
package_hidden: bool,
|
visibility: types.Visibility,
|
||||||
valid: bool,
|
valid: bool,
|
||||||
diagnostic: source.Diagnostic_Id,
|
diagnostic: source.Diagnostic_Id,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,17 +139,19 @@ Build_Ctx :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Function_Index_Entry :: struct {
|
Function_Index_Entry :: struct {
|
||||||
scope: ast.Package_Id,
|
scope: ast.Package_Id,
|
||||||
hidden: bool,
|
file: ast.File_Id,
|
||||||
name: symbol.Id,
|
visibility: types.Visibility,
|
||||||
id: ast.Function_Id,
|
name: symbol.Id,
|
||||||
|
id: ast.Function_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Global_Index_Entry :: struct {
|
Global_Index_Entry :: struct {
|
||||||
scope: ast.Package_Id,
|
scope: ast.Package_Id,
|
||||||
hidden: bool,
|
file: ast.File_Id,
|
||||||
name: symbol.Id,
|
visibility: types.Visibility,
|
||||||
id: ast.Global_Id,
|
name: symbol.Id,
|
||||||
|
id: ast.Global_Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Import_Index_Entry :: struct {
|
Import_Index_Entry :: struct {
|
||||||
@@ -1537,12 +1539,17 @@ find_function_symbol :: proc(index: []Function_Index_Entry, scope: ast.Package_I
|
|||||||
visible := ast.INVALID_FUNCTION
|
visible := ast.INVALID_FUNCTION
|
||||||
for low < len(index) && index[low].scope == scope && index[low].name == name {
|
for low < len(index) && index[low].scope == scope && index[low].name == name {
|
||||||
entry := index[low]
|
entry := index[low]
|
||||||
if entry.hidden {
|
switch entry.visibility {
|
||||||
|
case .Public:
|
||||||
|
visible = entry.id
|
||||||
|
case .Package:
|
||||||
if file != ast.INVALID_FILE {
|
if file != ast.INVALID_FILE {
|
||||||
|
visible = entry.id
|
||||||
|
}
|
||||||
|
case .File:
|
||||||
|
if entry.file == file {
|
||||||
return entry.id
|
return entry.id
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
visible = entry.id
|
|
||||||
}
|
}
|
||||||
low += 1
|
low += 1
|
||||||
}
|
}
|
||||||
@@ -1564,12 +1571,17 @@ find_global_symbol :: proc(index: []Global_Index_Entry, scope: ast.Package_Id, n
|
|||||||
visible := ast.INVALID_GLOBAL
|
visible := ast.INVALID_GLOBAL
|
||||||
for low < len(index) && index[low].scope == scope && index[low].name == name {
|
for low < len(index) && index[low].scope == scope && index[low].name == name {
|
||||||
entry := index[low]
|
entry := index[low]
|
||||||
if entry.hidden {
|
switch entry.visibility {
|
||||||
|
case .Public:
|
||||||
|
visible = entry.id
|
||||||
|
case .Package:
|
||||||
if file != ast.INVALID_FILE {
|
if file != ast.INVALID_FILE {
|
||||||
|
visible = entry.id
|
||||||
|
}
|
||||||
|
case .File:
|
||||||
|
if entry.file == file {
|
||||||
return entry.id
|
return entry.id
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
visible = entry.id
|
|
||||||
}
|
}
|
||||||
low += 1
|
low += 1
|
||||||
}
|
}
|
||||||
@@ -1612,12 +1624,12 @@ build_symbol_indexes :: proc(checker: ^Checker) {
|
|||||||
if function.generated {
|
if function.generated {
|
||||||
continue
|
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
|
function_index += 1
|
||||||
}
|
}
|
||||||
for alias in checker.ast_module.aliases {
|
for alias in checker.ast_module.aliases {
|
||||||
if alias.valid && alias.kind == .Function {
|
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
|
function_index += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1631,12 +1643,12 @@ build_symbol_indexes :: proc(checker: ^Checker) {
|
|||||||
}
|
}
|
||||||
checker.global_index = make([]Global_Index_Entry, global_count, checker.allocator)
|
checker.global_index = make([]Global_Index_Entry, global_count, checker.allocator)
|
||||||
for global, id in checker.ast_module.globals {
|
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)
|
global_index := len(checker.ast_module.globals)
|
||||||
for alias in checker.ast_module.aliases {
|
for alias in checker.ast_module.aliases {
|
||||||
if alias.valid && alias.kind == .Global {
|
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
|
global_index += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1737,7 +1749,7 @@ configure_entry_point :: proc(checker: ^Checker) {
|
|||||||
type_from_syntax(checker, function.result, function.pkg, function.file),
|
type_from_syntax(checker, function.result, function.pkg, function.file),
|
||||||
&checker.module.types,
|
&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) {
|
len(function.params) == 0 && !types.is_valid(function.error) && types.equal(result, io_type) {
|
||||||
provider = ast.function_id(function_id)
|
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 {
|
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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3996,11 +4024,11 @@ validate_declarations :: proc(checker: ^Checker) {
|
|||||||
function.span,
|
function.span,
|
||||||
"inferred error channels are not allowed on 'c_func'",
|
"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.template_diagnostics[function_id] = source.add(
|
||||||
checker.diagnostics,
|
checker.diagnostics,
|
||||||
function.span,
|
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),
|
symbol_text(checker, function.name),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
checker.specs[id].result = types.I64
|
recovery_result := types.fallible_success(spec.result, &checker.module.types) if function.infer_error else types.INVALID
|
||||||
spec.result = types.I64
|
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 {
|
for arg in spec.args {
|
||||||
if !is_runtime_type(checker, arg) {
|
if !is_runtime_type(checker, arg) {
|
||||||
@@ -14810,26 +14843,29 @@ check :: proc(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for previous in ast_module.functions[:index] {
|
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))
|
source.addf(diagnostics, function.span, "duplicate function '%s'", symbol_text(&checker, function.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for global in ast_module.globals {
|
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))
|
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))
|
source.addf(diagnostics, function.span, "function '%s' shadows visible type", symbol_text(&checker, function.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for global, index in ast_module.globals {
|
for global, index in ast_module.globals {
|
||||||
for previous in ast_module.globals[:index] {
|
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))
|
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))
|
source.addf(diagnostics, global.span, "global '%s' shadows visible type", symbol_text(&checker, global.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-19
@@ -1249,14 +1249,29 @@ load_package :: proc(
|
|||||||
return pkg_id
|
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 {
|
declaration_conflicts :: proc(module: ^ast.Module, pkg: ast.Package_Id, file: ast.File_Id, name: symbol.Id) -> bool {
|
||||||
for function in module.functions {
|
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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for global in module.globals {
|
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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1327,7 +1342,9 @@ find_visible_enum_global :: proc(
|
|||||||
public_only := false,
|
public_only := false,
|
||||||
) -> ast.Global_Id {
|
) -> ast.Global_Id {
|
||||||
for global, index in module.globals {
|
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)
|
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 {
|
alias_conflicts_with_declaration :: proc(module: ^ast.Module, alias: ast.Declaration_Alias) -> bool {
|
||||||
for function in module.functions {
|
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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for global in module.globals {
|
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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for item in module.type_store.nodes {
|
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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1473,7 +1493,7 @@ direct_alias_target :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symb
|
|||||||
target: u32
|
target: u32
|
||||||
kinds := 0
|
kinds := 0
|
||||||
for function, index in module.functions {
|
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
|
kind = .Function
|
||||||
target = u32(ast.function_id(index))
|
target = u32(ast.function_id(index))
|
||||||
kinds += 1
|
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 {
|
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
|
kind = .Global
|
||||||
target = u32(ast.global_id(index))
|
target = u32(ast.global_id(index))
|
||||||
kinds += 1
|
kinds += 1
|
||||||
@@ -1498,24 +1518,24 @@ direct_alias_target :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symb
|
|||||||
return kind, target, kinds
|
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 {
|
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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for global in module.globals {
|
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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for item in module.type_store.nodes {
|
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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for alias in module.aliases {
|
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
|
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 {
|
find_public_alias :: proc(module: ^ast.Module, pkg: ast.Package_Id, name: symbol.Id) -> int {
|
||||||
for alias, index in module.aliases {
|
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
|
return index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1581,11 +1601,11 @@ resolve_declaration_alias :: proc(state: ^State, index: int, states: []u8) -> bo
|
|||||||
return false
|
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(
|
alias.diagnostic = source.addf(
|
||||||
state.diagnostics,
|
state.diagnostics,
|
||||||
alias.span,
|
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.qualifier),
|
||||||
symbol.resolve(state.symbols, alias.member),
|
symbol.resolve(state.symbols, alias.member),
|
||||||
)
|
)
|
||||||
@@ -1611,7 +1631,8 @@ validate_declaration_aliases :: proc(state: ^State) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for previous in state.module.aliases[:index] {
|
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.diagnostic = source.addf(state.diagnostics, alias.span, "duplicate declaration alias '%s'", name)
|
||||||
alias.valid = false
|
alias.valid = false
|
||||||
break
|
break
|
||||||
@@ -1661,9 +1682,9 @@ validate_declaration_aliases :: proc(state: ^State) {
|
|||||||
u32(alias.pkg),
|
u32(alias.pkg),
|
||||||
u32(alias.name),
|
u32(alias.name),
|
||||||
file=u32(alias.file),
|
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.diagnostic = source.addf(state.diagnostics, alias.span, "duplicate type declaration '%s'", symbol.resolve(state.symbols, alias.name))
|
||||||
alias.valid = false
|
alias.valid = false
|
||||||
}
|
}
|
||||||
|
|||||||
+111
-56
@@ -31,7 +31,12 @@ Parser :: struct {
|
|||||||
capture_pipe: bool,
|
capture_pipe: bool,
|
||||||
// A parenthesized `if` condition ends before a leading-dot brace-less body.
|
// A parenthesized `if` condition ends before a leading-dot brace-less body.
|
||||||
if_condition: bool,
|
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
|
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)]
|
return parser.source_file.text[int(tok.span.start):int(tok.span.end)]
|
||||||
}
|
}
|
||||||
|
|
||||||
package_hidden_name :: proc(parser: ^Parser, name: symbol.Id) -> bool {
|
declaration_visibility :: proc(parser: ^Parser, name: symbol.Id) -> types.Visibility {
|
||||||
for hidden in parser.hidden_names {
|
for local in parser.local_names {
|
||||||
if hidden == name {
|
if local.name == name {
|
||||||
return true
|
return local.visibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return .Public
|
||||||
}
|
}
|
||||||
|
|
||||||
collect_hidden_names :: proc(parser: ^Parser) {
|
collect_local_names :: proc(parser: ^Parser) {
|
||||||
depth := 0
|
depth := 0
|
||||||
for item, index in parser.tokens.items {
|
for item, index in parser.tokens.items {
|
||||||
#partial switch item.kind {
|
#partial switch item.kind {
|
||||||
@@ -61,9 +66,32 @@ collect_hidden_names :: proc(parser: ^Parser) {
|
|||||||
case .Right_Brace:
|
case .Right_Brace:
|
||||||
depth = max(depth-1, 0)
|
depth = max(depth-1, 0)
|
||||||
case .Keyword_Hide:
|
case .Keyword_Hide:
|
||||||
if depth == 0 && index+1 < len(parser.tokens.items) &&
|
if depth != 0 {
|
||||||
parser.tokens.items[index+1].kind == .Identifier {
|
continue
|
||||||
append(&parser.hidden_names, parser.tokens.items[index+1].symbol)
|
}
|
||||||
|
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:
|
case:
|
||||||
}
|
}
|
||||||
@@ -437,7 +465,7 @@ parse_type_atom :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
|||||||
u32(name.symbol),
|
u32(name.symbol),
|
||||||
u32(qualifier),
|
u32(qualifier),
|
||||||
u32(parser.file),
|
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" &&
|
if token_text(parser, name) == "struct_type" &&
|
||||||
current(parser).kind == .Bang && peek(parser).kind == .Left_Paren {
|
current(parser).kind == .Bang && peek(parser).kind == .Left_Paren {
|
||||||
@@ -2605,7 +2633,7 @@ parse_while :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
|||||||
return 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)
|
advance(parser)
|
||||||
if _, ok := allow(parser, .Left_Paren); !ok {
|
if _, ok := allow(parser, .Left_Paren); !ok {
|
||||||
source.add(parser.diagnostics, current(parser).span, "expected '(' after 'func'")
|
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,
|
pkg=parser.pkg,
|
||||||
file=parser.file,
|
file=parser.file,
|
||||||
c_abi=c_abi,
|
c_abi=c_abi,
|
||||||
package_hidden=package_hidden,
|
visibility=visibility,
|
||||||
has_body=false,
|
has_body=false,
|
||||||
variadic=variadic,
|
variadic=variadic,
|
||||||
params=params,
|
params=params,
|
||||||
@@ -2653,7 +2681,7 @@ parse_function :: proc(parser: ^Parser, name: token.Token, c_abi, package_hidden
|
|||||||
pkg=parser.pkg,
|
pkg=parser.pkg,
|
||||||
file=parser.file,
|
file=parser.file,
|
||||||
c_abi=c_abi,
|
c_abi=c_abi,
|
||||||
package_hidden=package_hidden,
|
visibility=visibility,
|
||||||
has_body=true,
|
has_body=true,
|
||||||
variadic=variadic,
|
variadic=variadic,
|
||||||
params=params,
|
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)
|
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)
|
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
|
// 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.
|
// enum; `union(enum)` synthesizes one from the variant names after the body is parsed.
|
||||||
tag := types.INVALID
|
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",
|
"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))
|
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||||
}
|
}
|
||||||
if !ended_by_newline {
|
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)) {
|
if is_union && (inferred_tag || types.is_valid(declared_tag)) {
|
||||||
tag = synthesize_union_tag(parser, fields[:])
|
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))
|
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||||
} else if !c_layout && !is_union && !tuple {
|
} else if !c_layout && !is_union && !tuple {
|
||||||
for value, index in defaults {
|
for value, index in defaults {
|
||||||
@@ -2930,10 +2958,10 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout, package_hidde
|
|||||||
_ = finish_statement(parser)
|
_ = 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)
|
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)
|
||||||
if !types.define_record(&parser.module.type_store, id, nil, false, true, false, package_hidden=package_hidden) {
|
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))
|
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||||
}
|
}
|
||||||
if current(parser).kind == .Left_Brace {
|
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)
|
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)
|
start := advance(parser)
|
||||||
child := parse_type(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)
|
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, package_hidden) {
|
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))
|
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||||
}
|
}
|
||||||
if !types.is_valid(child) {
|
if !types.is_valid(child) {
|
||||||
@@ -2971,7 +2999,7 @@ parse_distinct :: proc(parser: ^Parser, name: token.Token, package_hidden: bool)
|
|||||||
_ = finish_statement(parser)
|
_ = 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)
|
start := advance(parser)
|
||||||
saved := parser.cursor
|
saved := parser.cursor
|
||||||
if current(parser).kind == .Identifier && peek(parser).kind == .Dot {
|
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,
|
pkg=parser.pkg,
|
||||||
file=parser.file,
|
file=parser.file,
|
||||||
target_pkg=ast.INVALID_PACKAGE,
|
target_pkg=ast.INVALID_PACKAGE,
|
||||||
package_hidden=package_hidden,
|
visibility=visibility,
|
||||||
valid=true,
|
valid=true,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
@@ -2999,8 +3027,8 @@ parse_alias :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) {
|
|||||||
}
|
}
|
||||||
parser.cursor = saved
|
parser.cursor = saved
|
||||||
child := parse_type(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)
|
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, package_hidden) {
|
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))
|
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||||
}
|
}
|
||||||
if !types.is_valid(child) {
|
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)
|
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)
|
start := advance(parser)
|
||||||
explicit_backing := false
|
explicit_backing := false
|
||||||
backing := types.INVALID
|
backing := types.INVALID
|
||||||
@@ -3165,8 +3193,8 @@ parse_enum :: proc(parser: ^Parser, name: token.Token, package_hidden: bool) {
|
|||||||
_ = finish_statement(parser)
|
_ = finish_statement(parser)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
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)
|
||||||
if !types.define_enum(&parser.module.type_store, id, backing, members[:], explicit_backing, package_hidden) {
|
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))
|
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||||
}
|
}
|
||||||
if explicit_backing && deferred {
|
if explicit_backing && deferred {
|
||||||
@@ -3295,10 +3323,37 @@ parse_test :: proc(parser: ^Parser, name: token.Token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parse_top_level :: proc(parser: ^Parser) {
|
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 current(parser).kind == .Keyword_Test && peek(parser).kind == .Keyword_Import {
|
||||||
if package_hidden {
|
if has_modifier {
|
||||||
source.add(parser.diagnostics, hide_token.span, "test imports cannot use 'hide'")
|
source.add(parser.diagnostics, modifier.span, "test imports cannot use a visibility modifier")
|
||||||
}
|
}
|
||||||
start := advance(parser)
|
start := advance(parser)
|
||||||
advance(parser) // consume 'import'
|
advance(parser) // consume 'import'
|
||||||
@@ -3306,15 +3361,15 @@ parse_top_level :: proc(parser: ^Parser) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if current(parser).kind == .Keyword_Import {
|
if current(parser).kind == .Keyword_Import {
|
||||||
if package_hidden {
|
if has_modifier {
|
||||||
source.add(parser.diagnostics, hide_token.span, "imports are already file-local and cannot use 'hide'")
|
source.add(parser.diagnostics, modifier.span, "imports are already file-local and cannot use a visibility modifier")
|
||||||
}
|
}
|
||||||
start := advance(parser)
|
start := advance(parser)
|
||||||
parse_import(parser, token.Token{}, start)
|
parse_import(parser, token.Token{}, start)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if current(parser).kind != .Identifier {
|
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)
|
source.add(parser.diagnostics, current(parser).span, message)
|
||||||
for current(parser).kind != .Newline && current(parser).kind != .Eof {
|
for current(parser).kind != .Newline && current(parser).kind != .Eof {
|
||||||
advance(parser)
|
advance(parser)
|
||||||
@@ -3324,8 +3379,8 @@ parse_top_level :: proc(parser: ^Parser) {
|
|||||||
}
|
}
|
||||||
name := advance(parser)
|
name := advance(parser)
|
||||||
if current(parser).kind == .Keyword_Test {
|
if current(parser).kind == .Keyword_Test {
|
||||||
if package_hidden {
|
if has_modifier {
|
||||||
source.add(parser.diagnostics, hide_token.span, "test declarations cannot use 'hide'")
|
source.add(parser.diagnostics, modifier.span, "test declarations cannot use a visibility modifier")
|
||||||
}
|
}
|
||||||
parse_test(parser, name)
|
parse_test(parser, name)
|
||||||
return
|
return
|
||||||
@@ -3335,8 +3390,8 @@ parse_top_level :: proc(parser: ^Parser) {
|
|||||||
advance(parser)
|
advance(parser)
|
||||||
skip_newlines(parser)
|
skip_newlines(parser)
|
||||||
if current(parser).kind == .Keyword_Import {
|
if current(parser).kind == .Keyword_Import {
|
||||||
if package_hidden {
|
if has_modifier {
|
||||||
source.add(parser.diagnostics, hide_token.span, "imports are already file-local and cannot use 'hide'")
|
source.add(parser.diagnostics, modifier.span, "imports are already file-local and cannot use a visibility modifier")
|
||||||
}
|
}
|
||||||
start := advance(parser)
|
start := advance(parser)
|
||||||
parse_import(parser, name, start)
|
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 {
|
if current(parser).kind == .Keyword_Func || current(parser).kind == .Keyword_C_Func {
|
||||||
c_abi := 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
|
return
|
||||||
}
|
}
|
||||||
type_syntax := types.INVALID
|
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),
|
source.add(parser.diagnostics, span_from(name.span, current(parser).span),
|
||||||
"function declarations do not use '::'; write 'name func(...)' or 'name c_func(...)'")
|
"function declarations do not use '::'; write 'name func(...)' or 'name c_func(...)'")
|
||||||
c_abi := 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
|
return
|
||||||
}
|
}
|
||||||
if operator.kind == .Colon_Colon &&
|
if operator.kind == .Colon_Colon &&
|
||||||
(current(parser).kind == .Keyword_Struct || current(parser).kind == .Keyword_C_Struct) {
|
(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
|
return
|
||||||
}
|
}
|
||||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Opaque {
|
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Opaque {
|
||||||
parse_opaque(parser, name, package_hidden)
|
parse_opaque(parser, name, visibility)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Union {
|
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
|
return
|
||||||
}
|
}
|
||||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Enum {
|
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Enum {
|
||||||
parse_enum(parser, name, package_hidden)
|
parse_enum(parser, name, visibility)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Distinct {
|
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Distinct {
|
||||||
parse_distinct(parser, name, package_hidden)
|
parse_distinct(parser, name, visibility)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Alias {
|
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Alias {
|
||||||
parse_alias(parser, name, package_hidden)
|
parse_alias(parser, name, visibility)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3411,7 +3466,7 @@ parse_top_level :: proc(parser: ^Parser) {
|
|||||||
name=name.symbol,
|
name=name.symbol,
|
||||||
pkg=parser.pkg,
|
pkg=parser.pkg,
|
||||||
file=parser.file,
|
file=parser.file,
|
||||||
package_hidden=package_hidden,
|
visibility=visibility,
|
||||||
type=type_syntax,
|
type=type_syntax,
|
||||||
immutable=operator.kind == .Colon_Colon,
|
immutable=operator.kind == .Colon_Colon,
|
||||||
expr=expr,
|
expr=expr,
|
||||||
@@ -3432,9 +3487,9 @@ parse :: proc(
|
|||||||
diagnostics=diagnostics,
|
diagnostics=diagnostics,
|
||||||
module=ast.init_module(allocator),
|
module=ast.init_module(allocator),
|
||||||
}
|
}
|
||||||
parser.hidden_names.allocator = allocator
|
parser.local_names.allocator = allocator
|
||||||
defer delete(parser.hidden_names)
|
defer delete(parser.local_names)
|
||||||
collect_hidden_names(&parser)
|
collect_local_names(&parser)
|
||||||
skip_newlines(&parser)
|
skip_newlines(&parser)
|
||||||
for current(&parser).kind != .Eof {
|
for current(&parser).kind != .Eof {
|
||||||
parse_top_level(&parser)
|
parse_top_level(&parser)
|
||||||
@@ -3459,9 +3514,9 @@ parse_into :: proc(
|
|||||||
pkg=pkg,
|
pkg=pkg,
|
||||||
file=file,
|
file=file,
|
||||||
}
|
}
|
||||||
parser.hidden_names.allocator = module.allocator
|
parser.local_names.allocator = module.allocator
|
||||||
defer delete(parser.hidden_names)
|
defer delete(parser.local_names)
|
||||||
collect_hidden_names(&parser)
|
collect_local_names(&parser)
|
||||||
skip_newlines(&parser)
|
skip_newlines(&parser)
|
||||||
for current(&parser).kind != .Eof {
|
for current(&parser).kind != .Eof {
|
||||||
parse_top_level(&parser)
|
parse_top_level(&parser)
|
||||||
|
|||||||
+51
-18
@@ -56,6 +56,12 @@ Numeric_Category :: enum u8 {
|
|||||||
Float,
|
Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Visibility :: enum u8 {
|
||||||
|
Public,
|
||||||
|
Package,
|
||||||
|
File,
|
||||||
|
}
|
||||||
|
|
||||||
Kind :: enum u8 {
|
Kind :: enum u8 {
|
||||||
Invalid,
|
Invalid,
|
||||||
Void,
|
Void,
|
||||||
@@ -109,7 +115,7 @@ Node :: struct {
|
|||||||
tuple: bool,
|
tuple: bool,
|
||||||
opaque: bool,
|
opaque: bool,
|
||||||
declared: bool,
|
declared: bool,
|
||||||
package_hidden: bool,
|
visibility: Visibility,
|
||||||
explicit_backing: bool,
|
explicit_backing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,13 +199,21 @@ intern :: proc(store: ^Store, candidate: Node) -> Type {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xffff_ffff, package_hidden := false) -> Type {
|
named :: proc(
|
||||||
normalized_file := file if qualifier != 0 else u32(0)
|
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 {
|
for existing, index in store.nodes {
|
||||||
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Distinct ||
|
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Distinct ||
|
||||||
existing.kind == .Enum || existing.kind == .Struct || existing.kind == .Union) &&
|
existing.kind == .Enum || existing.kind == .Struct || existing.kind == .Union) &&
|
||||||
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier &&
|
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)
|
return DYNAMIC_START+Type(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,22 +223,41 @@ named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xf
|
|||||||
name=name,
|
name=name,
|
||||||
qualifier=qualifier,
|
qualifier=qualifier,
|
||||||
file=normalized_file,
|
file=normalized_file,
|
||||||
|
visibility=visibility,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
find_named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xffff_ffff) -> Type {
|
find_named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xffff_ffff) -> Type {
|
||||||
|
fallback := INVALID
|
||||||
for existing, index in store.nodes {
|
for existing, index in store.nodes {
|
||||||
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Distinct ||
|
if (existing.kind != .Named && existing.kind != .Alias && existing.kind != .Distinct &&
|
||||||
existing.kind == .Enum || existing.kind == .Struct || existing.kind == .Union) &&
|
existing.kind != .Enum && existing.kind != .Struct && existing.kind != .Union) ||
|
||||||
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier &&
|
existing.pkg != pkg || existing.name != name || existing.qualifier != qualifier {
|
||||||
(!existing.package_hidden || file != 0xffff_ffff) {
|
continue
|
||||||
return DYNAMIC_START+Type(index)
|
}
|
||||||
|
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)
|
existing, ok := node(store, id)
|
||||||
if !ok || existing.kind != .Named || existing.declared {
|
if !ok || existing.kind != .Named || existing.declared {
|
||||||
return false
|
return false
|
||||||
@@ -232,12 +265,12 @@ define_alias :: proc(store: ^Store, id, child: Type, package_hidden := false) ->
|
|||||||
index := int(id-DYNAMIC_START)
|
index := int(id-DYNAMIC_START)
|
||||||
store.nodes[index].kind = .Alias
|
store.nodes[index].kind = .Alias
|
||||||
store.nodes[index].child = child
|
store.nodes[index].child = child
|
||||||
store.nodes[index].package_hidden = package_hidden
|
store.nodes[index].visibility = visibility
|
||||||
store.nodes[index].declared = true
|
store.nodes[index].declared = true
|
||||||
return 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)
|
existing, ok := node(store, id)
|
||||||
if !ok || existing.kind != .Named || existing.declared {
|
if !ok || existing.kind != .Named || existing.declared {
|
||||||
return false
|
return false
|
||||||
@@ -245,12 +278,12 @@ define_distinct :: proc(store: ^Store, id, child: Type, package_hidden := false)
|
|||||||
index := int(id-DYNAMIC_START)
|
index := int(id-DYNAMIC_START)
|
||||||
store.nodes[index].kind = .Distinct
|
store.nodes[index].kind = .Distinct
|
||||||
store.nodes[index].child = child
|
store.nodes[index].child = child
|
||||||
store.nodes[index].package_hidden = package_hidden
|
store.nodes[index].visibility = visibility
|
||||||
store.nodes[index].declared = true
|
store.nodes[index].declared = true
|
||||||
return 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)
|
existing, ok := node(store, id)
|
||||||
if !ok || existing.kind != .Named || existing.declared {
|
if !ok || existing.kind != .Named || existing.declared {
|
||||||
return false
|
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_start = u32(len(store.enum_members))
|
||||||
store.nodes[index].field_count = u32(len(members))
|
store.nodes[index].field_count = u32(len(members))
|
||||||
store.nodes[index].explicit_backing = explicit_backing
|
store.nodes[index].explicit_backing = explicit_backing
|
||||||
store.nodes[index].package_hidden = package_hidden
|
store.nodes[index].visibility = visibility
|
||||||
store.nodes[index].declared = true
|
store.nodes[index].declared = true
|
||||||
append(&store.enum_members, ..members)
|
append(&store.enum_members, ..members)
|
||||||
return true
|
return true
|
||||||
@@ -278,7 +311,7 @@ define_record :: proc(
|
|||||||
tag: Type = INVALID,
|
tag: Type = INVALID,
|
||||||
declared_tag: Type = INVALID,
|
declared_tag: Type = INVALID,
|
||||||
tuple := false,
|
tuple := false,
|
||||||
package_hidden := false,
|
visibility := Visibility.Public,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
existing, ok := node(store, id)
|
existing, ok := node(store, id)
|
||||||
if !ok || (existing.kind != .Named && existing.kind != .Struct && existing.kind != .Union) ||
|
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].c_layout = c_layout
|
||||||
store.nodes[index].tuple = tuple
|
store.nodes[index].tuple = tuple
|
||||||
store.nodes[index].opaque = opaque
|
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].declared = true
|
||||||
store.nodes[index].explicit_size = explicit_size
|
store.nodes[index].explicit_size = explicit_size
|
||||||
store.nodes[index].explicit_alignment = explicit_alignment
|
store.nodes[index].explicit_alignment = explicit_alignment
|
||||||
|
|||||||
+40
-8
@@ -3251,6 +3251,7 @@ inferred_error_channels_reject_unstable_or_untyped_contracts :: proc(t: ^testing
|
|||||||
Case :: struct {
|
Case :: struct {
|
||||||
source: string,
|
source: string,
|
||||||
message: string,
|
message: string,
|
||||||
|
absent: string,
|
||||||
}
|
}
|
||||||
cases := [?]Case{
|
cases := [?]Case{
|
||||||
{
|
{
|
||||||
@@ -3259,7 +3260,7 @@ fail func() void ! Error { return .failed }
|
|||||||
visible func() void! { try fail() }
|
visible func() void! { try fail() }
|
||||||
main func() void {}
|
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 }
|
source=`Error :: enum { failed }
|
||||||
@@ -3281,6 +3282,20 @@ main func() void { empty() catch |_| {} }
|
|||||||
`,
|
`,
|
||||||
message="could not infer a named error channel for 'empty'",
|
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 {
|
for test_case in cases {
|
||||||
source_file := source.Source{path="test.bro", text=test_case.source}
|
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)
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
|
unexpected := false
|
||||||
for diagnostic in diagnostics.items {
|
for diagnostic in diagnostics.items {
|
||||||
found = found || strings.contains(diagnostic.message, test_case.message)
|
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, found)
|
||||||
|
testing.expect(t, !unexpected)
|
||||||
|
|
||||||
hir.destroy_module(&hir_module)
|
hir.destroy_module(&hir_module)
|
||||||
ast.destroy_module(&ast_module)
|
ast.destroy_module(&ast_module)
|
||||||
@@ -10345,14 +10364,18 @@ imports_are_file_local :: proc(t: ^testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(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{
|
cases := [?]string{
|
||||||
`hide import "../dep"`,
|
`hide import "../dep"`,
|
||||||
`hide dep :: import "../dep"`,
|
`hide(file) dep :: import "../dep"`,
|
||||||
`hide func() void {}`,
|
`hide func() void {}`,
|
||||||
`main func(hide value i32) void {}`,
|
`main func(hide value i32) void {}`,
|
||||||
`Box :: struct { hide i32 }`,
|
`Box :: struct { hide i32 }`,
|
||||||
`main func() void { hide value i32 := 1 }`,
|
`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 {
|
for text in cases {
|
||||||
source_file := source.Source{path="test.bro", text=text}
|
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)
|
@(test)
|
||||||
hide_declarations_are_package_hidden :: proc(t: ^testing.T) {
|
local_declarations_resolve_at_their_declared_scope :: proc(t: ^testing.T) {
|
||||||
output := "/tmp/brolang-test-hidden-declarations"
|
output := "/tmp/brolang-test-local-declarations"
|
||||||
defer _ = os.remove(output)
|
defer _ = os.remove(output)
|
||||||
status := compiler_core.compile_package("examples/packages/hidden_valid/app", output)
|
status := compiler_core.compile_package("examples/packages/hidden_valid/app", output)
|
||||||
testing.expect_value(t, status, 0)
|
testing.expect_value(t, status, 0)
|
||||||
state := run_executable(output)
|
state := run_executable(output)
|
||||||
testing.expect_value(t, state.exit_code, 9)
|
testing.expect_value(t, state.exit_code, 15)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(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()
|
sources := source.init_store()
|
||||||
defer source.destroy_store(&sources)
|
defer source.destroy_store(&sources)
|
||||||
diagnostics := source.init_store_diagnostics(&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_sibling_type := false
|
||||||
found_import := false
|
found_import := false
|
||||||
found_collision := false
|
found_collision := false
|
||||||
|
found_file_sibling := false
|
||||||
|
found_file_sibling_value := false
|
||||||
|
found_file_sibling_type := false
|
||||||
for diagnostic in diagnostics.items {
|
for diagnostic in diagnostics.items {
|
||||||
found_sibling = found_sibling || strings.contains(diagnostic.message, "unknown symbol 'sibling'")
|
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_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_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_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_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)
|
||||||
testing.expect(t, !found_sibling_value)
|
testing.expect(t, !found_sibling_value)
|
||||||
testing.expect(t, !found_sibling_type)
|
testing.expect(t, !found_sibling_type)
|
||||||
testing.expect(t, found_import)
|
testing.expect(t, found_import)
|
||||||
testing.expect(t, found_collision)
|
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)
|
@(test)
|
||||||
@@ -10704,7 +10736,7 @@ main func() void {}
|
|||||||
testing.expect(t, loaded)
|
testing.expect(t, loaded)
|
||||||
wants := []string{
|
wants := []string{
|
||||||
"has no member 'missing'",
|
"has no member 'missing'",
|
||||||
"is package-hidden",
|
"is not public",
|
||||||
"unknown symbol 'nope'",
|
"unknown symbol 'nope'",
|
||||||
"unavailable imported package 'gone'",
|
"unavailable imported package 'gone'",
|
||||||
"package member 'dep.ambiguous' is ambiguous",
|
"package member 'dep.ambiguous' is ambiguous",
|
||||||
|
|||||||
@@ -8,6 +8,16 @@ hide Sibling :: struct {
|
|||||||
|
|
||||||
hide sibling_value :: 1
|
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 {
|
collision c_func() i32 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ read_sibling_value func() i32 {
|
|||||||
return sibling_value
|
return sibling_value
|
||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
read_file_sibling func(value File_Sibling) i32 {
|
||||||
return sibling() + dep.secret() + read_sibling(Sibling { value = 1 }) + read_sibling_value()
|
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 })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
hide secret c_func() i32 {
|
hide(package) secret c_func() i32 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
hide helper func() i32 {
|
hide(package) helper func() i32 {
|
||||||
thing Thing := Thing { value = value }
|
thing Thing := Thing { value = value }
|
||||||
return thing.value
|
return thing.value
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,18 @@ hide Local_C_Record :: c_struct {
|
|||||||
value c_int
|
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 {
|
from_a func() i32 {
|
||||||
record Local_C_Record := Local_C_Record { value = 0 }
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,21 @@ Box :: struct {
|
|||||||
_value i32
|
_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 {
|
from_b func(_input i32) i32 {
|
||||||
_local Box := Box { _value = _input }
|
_local Box := Box { _value = _input }
|
||||||
record _C_Record := _C_Record { value = 0 }
|
record _C_Record := _C_Record { value = 0 }
|
||||||
thing Thing := Thing { value = value }
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
fd c_int :: c_int(stream)
|
||||||
request usize := bytes.len
|
request usize := bytes.len
|
||||||
maximum usize :: usize(maxval!(c_long))
|
maximum usize :: usize(maxval!(c_long))
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ hide power_of_two func(value usize) bool {
|
|||||||
return true
|
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 {
|
if power_of_two(alignment) == false {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ hide c_alloc func(_ ?*mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
|||||||
return ptrcast!(u8, memory[0])
|
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 {
|
if power_of_two(alignment) == false {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user