package-private visibility
This commit is contained in:
+128
-53
@@ -139,7 +139,6 @@ Build_Ctx :: struct {
|
||||
|
||||
Function_Index_Entry :: struct {
|
||||
scope: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
hidden: bool,
|
||||
name: symbol.Id,
|
||||
id: ast.Function_Id,
|
||||
@@ -147,7 +146,6 @@ Function_Index_Entry :: struct {
|
||||
|
||||
Global_Index_Entry :: struct {
|
||||
scope: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
hidden: bool,
|
||||
name: symbol.Id,
|
||||
id: ast.Global_Id,
|
||||
@@ -1508,7 +1506,7 @@ find_function_symbol :: proc(index: []Function_Index_Entry, scope: ast.Package_I
|
||||
for low < len(index) && index[low].scope == scope && index[low].name == name {
|
||||
entry := index[low]
|
||||
if entry.hidden {
|
||||
if entry.file == file {
|
||||
if file != ast.INVALID_FILE {
|
||||
return entry.id
|
||||
}
|
||||
} else {
|
||||
@@ -1535,7 +1533,7 @@ find_global_symbol :: proc(index: []Global_Index_Entry, scope: ast.Package_Id, n
|
||||
for low < len(index) && index[low].scope == scope && index[low].name == name {
|
||||
entry := index[low]
|
||||
if entry.hidden {
|
||||
if entry.file == file {
|
||||
if file != ast.INVALID_FILE {
|
||||
return entry.id
|
||||
}
|
||||
} else {
|
||||
@@ -1582,12 +1580,12 @@ build_symbol_indexes :: proc(checker: ^Checker) {
|
||||
if function.generated {
|
||||
continue
|
||||
}
|
||||
checker.function_index[function_index] = Function_Index_Entry{scope=function.pkg, file=function.file, hidden=function.file_hidden, name=function.name, id=ast.function_id(id)}
|
||||
checker.function_index[function_index] = Function_Index_Entry{scope=function.pkg, hidden=function.package_hidden, 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, file=alias.file, hidden=alias.file_hidden, name=alias.name, id=ast.Function_Id(alias.target)}
|
||||
checker.function_index[function_index] = Function_Index_Entry{scope=alias.pkg, hidden=alias.package_hidden, name=alias.name, id=ast.Function_Id(alias.target)}
|
||||
function_index += 1
|
||||
}
|
||||
}
|
||||
@@ -1601,12 +1599,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, file=global.file, hidden=global.file_hidden, name=global.name, id=ast.global_id(id)}
|
||||
checker.global_index[id] = Global_Index_Entry{scope=global.pkg, hidden=global.package_hidden, 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, file=alias.file, hidden=alias.file_hidden, name=alias.name, id=ast.Global_Id(alias.target)}
|
||||
checker.global_index[global_index] = Global_Index_Entry{scope=alias.pkg, hidden=alias.package_hidden, name=alias.name, id=ast.Global_Id(alias.target)}
|
||||
global_index += 1
|
||||
}
|
||||
}
|
||||
@@ -1707,7 +1705,7 @@ configure_entry_point :: proc(checker: ^Checker) {
|
||||
type_from_syntax(checker, function.result, function.pkg, function.file),
|
||||
&checker.module.types,
|
||||
)
|
||||
if function.file_hidden && function.has_body && !function.c_abi && len(function.params) == 0 &&
|
||||
if function.package_hidden && function.has_body && !function.c_abi && len(function.params) == 0 &&
|
||||
!types.is_valid(function.error) && types.equal(result, io_type) {
|
||||
provider = ast.function_id(function_id)
|
||||
}
|
||||
@@ -1743,14 +1741,10 @@ declared_type_named :: proc(checker: ^Checker, pkg: ast.Package_Id, name: symbol
|
||||
return ok && item.declared
|
||||
}
|
||||
|
||||
declarations_conflict :: proc(left_file: ast.File_Id, left_hidden: bool, right_file: ast.File_Id, right_hidden: bool) -> bool {
|
||||
return left_file == right_file if left_hidden && right_hidden else true
|
||||
}
|
||||
|
||||
type_declaration_conflicts :: proc(checker: ^Checker, pkg: ast.Package_Id, name: symbol.Id, file: ast.File_Id, hidden: bool) -> bool {
|
||||
type_declaration_conflicts :: proc(checker: ^Checker, pkg: ast.Package_Id, name: symbol.Id) -> bool {
|
||||
for item in checker.module.types.nodes {
|
||||
if item.declared && item.pkg == u32(pkg) && item.name == u32(name) &&
|
||||
declarations_conflict(file, hidden, ast.File_Id(item.file), item.file_hidden) {
|
||||
if item.declared && item.pkg == u32(pkg) && item.name == u32(name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -2386,6 +2380,28 @@ call_argument_mapping :: proc(
|
||||
return selected, .Explicit if identity else .Inferred, comptime_param_count(function^), ""
|
||||
}
|
||||
|
||||
inferred_comptime_value_equal :: proc(checker: ^Checker, left, right: Comptime_Value) -> bool {
|
||||
if left.kind != right.kind {
|
||||
return false
|
||||
}
|
||||
if left.kind == .Type {
|
||||
return types.equal(
|
||||
types.resolve_alias(left.type, &checker.module.types),
|
||||
types.resolve_alias(right.type, &checker.module.types),
|
||||
)
|
||||
}
|
||||
if !types.equal(left.type, right.type) {
|
||||
return false
|
||||
}
|
||||
if left.kind == .Static {
|
||||
return left.fingerprint == right.fingerprint && left.key == right.key
|
||||
}
|
||||
if left.kind == .String {
|
||||
return left.text == right.text
|
||||
}
|
||||
return left.value == right.value
|
||||
}
|
||||
|
||||
bind_inferred_comptime :: proc(
|
||||
checker: ^Checker,
|
||||
function: ast.Function,
|
||||
@@ -2409,16 +2425,7 @@ bind_inferred_comptime :: proc(
|
||||
return true
|
||||
}
|
||||
existing := values[index]
|
||||
matches := existing.kind == value.kind
|
||||
if matches {
|
||||
if existing.kind == .Type {
|
||||
matches = types.equal(types.resolve_alias(existing.type, &checker.module.types), types.resolve_alias(value.type, &checker.module.types))
|
||||
} else if existing.kind == .Static {
|
||||
matches = existing.fingerprint == value.fingerprint && existing.key == value.key && types.equal(existing.type, value.type)
|
||||
} else {
|
||||
matches = existing.value == value.value && types.equal(existing.type, value.type)
|
||||
}
|
||||
}
|
||||
matches := inferred_comptime_value_equal(checker, existing, value)
|
||||
if !matches && diagnose {
|
||||
left := type_label(checker, existing.type) if existing.kind == .Type else fmt.aprintf("<comptime value>", allocator=checker.allocator) if existing.kind == .Static else fmt.aprintf("%d", existing.value, allocator=checker.allocator)
|
||||
right := type_label(checker, value.type) if value.kind == .Type else fmt.aprintf("<comptime value>", allocator=checker.allocator) if value.kind == .Static else fmt.aprintf("%d", value.value, allocator=checker.allocator)
|
||||
@@ -2514,6 +2521,33 @@ type_pattern_mentions_comptime :: proc(
|
||||
return false
|
||||
}
|
||||
|
||||
type_pattern_contains_type_call :: proc(checker: ^Checker, pattern: types.Type, depth := 0) -> bool {
|
||||
if depth > 64 {
|
||||
return false
|
||||
}
|
||||
item, ok := types.node(&checker.module.types, pattern)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if item.kind == .Type_Call {
|
||||
return true
|
||||
}
|
||||
if types.is_valid(item.child) && type_pattern_contains_type_call(checker, item.child, depth+1) {
|
||||
return true
|
||||
}
|
||||
if types.is_valid(item.extra) && type_pattern_contains_type_call(checker, item.extra, depth+1) {
|
||||
return true
|
||||
}
|
||||
if item.kind == .Function {
|
||||
for field in types.params_for(&checker.module.types, pattern) {
|
||||
if type_pattern_contains_type_call(checker, field.type, depth+1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
match_inferred_type_pattern :: proc(
|
||||
checker: ^Checker,
|
||||
function: ast.Function,
|
||||
@@ -2563,22 +2597,52 @@ match_inferred_type_pattern :: proc(
|
||||
template := find_template(checker, expr.name, target_pkg, expr_lookup_file(expr, function.file))
|
||||
origin: Type_Factory_Origin
|
||||
found := false
|
||||
ambiguous := false
|
||||
for candidate in checker.type_factory_origins {
|
||||
if candidate.template == template && types.equal(candidate.result, actual_type) {
|
||||
if candidate.template != template || !types.equal(candidate.result, actual_type) ||
|
||||
len(expr.args) != len(candidate.values) {
|
||||
continue
|
||||
}
|
||||
compatible := true
|
||||
for arg_id, index in expr.args {
|
||||
if arg_id == ast.INVALID_EXPR || int(arg_id) >= len(checker.ast_module.exprs) {
|
||||
compatible = false
|
||||
break
|
||||
}
|
||||
arg := checker.ast_module.exprs[arg_id]
|
||||
if arg.kind == .Name && !symbol.is_valid(arg.qualifier) {
|
||||
if binding_index, is_binding := comptime_binding_index(function, prefix, arg.name);
|
||||
is_binding && bound[binding_index] &&
|
||||
!inferred_comptime_value_equal(checker, values[binding_index], candidate.values[index]) {
|
||||
compatible = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !compatible {
|
||||
continue
|
||||
}
|
||||
if !found {
|
||||
origin = candidate
|
||||
found = true
|
||||
break
|
||||
continue
|
||||
}
|
||||
for arg_id, index in expr.args {
|
||||
arg := checker.ast_module.exprs[arg_id]
|
||||
if arg.kind == .Name && !symbol.is_valid(arg.qualifier) {
|
||||
if _, is_binding := comptime_binding_index(function, prefix, arg.name);
|
||||
is_binding && !inferred_comptime_value_equal(checker, origin.values[index], candidate.values[index]) {
|
||||
ambiguous = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found || len(expr.args) != len(origin.values) {
|
||||
if !found || ambiguous {
|
||||
return false
|
||||
}
|
||||
matched := true
|
||||
for arg_id, index in expr.args {
|
||||
if arg_id == ast.INVALID_EXPR || int(arg_id) >= len(checker.ast_module.exprs) {
|
||||
matched = false
|
||||
continue
|
||||
}
|
||||
arg := checker.ast_module.exprs[arg_id]
|
||||
if arg.kind == .Name && !symbol.is_valid(arg.qualifier) {
|
||||
if _, is_binding := comptime_binding_index(function, prefix, arg.name); is_binding {
|
||||
@@ -2893,11 +2957,21 @@ infer_call_comptime_values :: proc(
|
||||
for value_bound in bound {
|
||||
all_bound = all_bound && value_bound
|
||||
}
|
||||
// Concrete arguments bind first. Numeric constants, string literals, and `null`
|
||||
// are contextual and therefore only contribute after stronger evidence has had a
|
||||
// chance to bind the parameter type.
|
||||
weak_passes := [2]bool{false, true}
|
||||
for weak in weak_passes {
|
||||
// Factory provenance is authoritative when unique, so try it before direct
|
||||
// arguments. Strong and then weak direct evidence can disambiguate a factory
|
||||
// result; retry factory patterns once after both evidence passes.
|
||||
Evidence_Pass :: struct {
|
||||
type_call: bool,
|
||||
weak: bool,
|
||||
final: bool,
|
||||
}
|
||||
evidence_passes := [?]Evidence_Pass{
|
||||
{type_call=true},
|
||||
{final=true},
|
||||
{weak=true, final=true},
|
||||
{type_call=true, final=true},
|
||||
}
|
||||
for pass in evidence_passes {
|
||||
for arg_id, source_index in args {
|
||||
param_index := call_param_index(mapping, source_index)
|
||||
if param_index >= len(function.params) || param_index >= len(actual_args) {
|
||||
@@ -2910,7 +2984,11 @@ infer_call_comptime_values :: proc(
|
||||
is_null := arg_expr.kind == .Null
|
||||
is_weak := is_numeric_constant_expr(checker, arg_id) ||
|
||||
arg_expr.kind == .String || is_null
|
||||
if is_weak != weak {
|
||||
contains_type_call := type_pattern_contains_type_call(
|
||||
checker, function.params[param_index].type,
|
||||
)
|
||||
if contains_type_call != pass.type_call ||
|
||||
!pass.type_call && is_weak != pass.weak {
|
||||
continue
|
||||
}
|
||||
if !type_pattern_mentions_comptime(checker, function, prefix, function.params[param_index].type) {
|
||||
@@ -2923,7 +3001,7 @@ infer_call_comptime_values :: proc(
|
||||
if all_bound && arg_expr.kind == .Struct_Literal && !arg_expr.tuple && !symbol.is_valid(arg_expr.name) {
|
||||
continue
|
||||
}
|
||||
if weak {
|
||||
if pass.weak {
|
||||
if item, ok := types.node(&checker.module.types, function.params[param_index].type); ok &&
|
||||
item.qualifier == 0 && item.name != 0 {
|
||||
if binding_index, is_binding := comptime_binding_index(function, prefix, symbol.Id(item.name));
|
||||
@@ -2945,11 +3023,14 @@ infer_call_comptime_values :: proc(
|
||||
actual_args[param_index] = contextual
|
||||
}
|
||||
}
|
||||
matched = match_inferred_type_pattern(
|
||||
pass_matched := match_inferred_type_pattern(
|
||||
checker, function, prefix, function.params[param_index].type,
|
||||
actual, values, bound,
|
||||
checker.ast_module.exprs[arg_id].span, diagnose,
|
||||
) && matched
|
||||
checker.ast_module.exprs[arg_id].span, diagnose && pass.final,
|
||||
)
|
||||
if pass.final {
|
||||
matched = pass_matched && matched
|
||||
}
|
||||
}
|
||||
}
|
||||
ordinal = 0
|
||||
@@ -10063,9 +10144,6 @@ make_link_name :: proc(checker: ^Checker, id: Spec_Id) -> string {
|
||||
defer strings.builder_destroy(&builder)
|
||||
strings.write_string(&builder, "bro_c__" if function.c_abi else "bro__")
|
||||
fmt.sbprintf(&builder, "p%d__", function.pkg)
|
||||
if function.file_hidden {
|
||||
fmt.sbprintf(&builder, "f%d__", function.file)
|
||||
}
|
||||
strings.write_string(&builder, symbol_text(checker, function.name))
|
||||
for arg in spec.args {
|
||||
strings.write_string(&builder, "__")
|
||||
@@ -14381,29 +14459,26 @@ check :: proc(
|
||||
continue
|
||||
}
|
||||
for previous in ast_module.functions[:index] {
|
||||
if !previous.generated && previous.pkg == function.pkg && previous.name == function.name &&
|
||||
declarations_conflict(function.file, function.file_hidden, previous.file, previous.file_hidden) {
|
||||
if !previous.generated && previous.pkg == function.pkg && previous.name == function.name {
|
||||
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 &&
|
||||
declarations_conflict(function.file, function.file_hidden, global.file, global.file_hidden) {
|
||||
if global.pkg == function.pkg && global.name == 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, function.file, function.file_hidden) {
|
||||
if type_declaration_conflicts(&checker, function.pkg, 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 previous in ast_module.globals[:index] {
|
||||
if previous.pkg == global.pkg && previous.name == global.name &&
|
||||
declarations_conflict(global.file, global.file_hidden, previous.file, previous.file_hidden) {
|
||||
if previous.pkg == global.pkg && previous.name == global.name {
|
||||
source.addf(diagnostics, global.span, "duplicate global '%s'", symbol_text(&checker, global.name))
|
||||
}
|
||||
}
|
||||
if type_declaration_conflicts(&checker, global.pkg, global.name, global.file, global.file_hidden) {
|
||||
if type_declaration_conflicts(&checker, global.pkg, global.name) {
|
||||
source.addf(diagnostics, global.span, "global '%s' shadows visible type", symbol_text(&checker, global.name))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user