extern variables and object-like macro consts
This commit is contained in:
+160
-14
@@ -84,6 +84,8 @@ Checker :: struct {
|
||||
global_index: []Global_Index_Entry,
|
||||
import_index: []Import_Index_Entry,
|
||||
global_types: []types.Type,
|
||||
external_global_canonical: []ast.Global_Id,
|
||||
external_global_diagnostics: []source.Diagnostic_Id,
|
||||
constants: []Constant,
|
||||
template_diagnostics: []source.Diagnostic_Id,
|
||||
constant_stack: [dynamic]Constant_Frame,
|
||||
@@ -600,6 +602,72 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
|
||||
}
|
||||
}
|
||||
|
||||
validate_external_globals :: proc(checker: ^Checker) {
|
||||
for global, global_index in checker.ast_module.globals {
|
||||
checker.external_global_canonical[global_index] = ast.global_id(global_index)
|
||||
if !global.external {
|
||||
continue
|
||||
}
|
||||
switch global.link_name {
|
||||
case "main":
|
||||
checker.external_global_diagnostics[global_index] = source.add(
|
||||
checker.diagnostics,
|
||||
global.span,
|
||||
"external C variable 'main' conflicts with the program entry point",
|
||||
)
|
||||
case "write":
|
||||
checker.external_global_diagnostics[global_index] = source.add(
|
||||
checker.diagnostics,
|
||||
global.span,
|
||||
"external C variable 'write' conflicts with the compiler runtime",
|
||||
)
|
||||
}
|
||||
for previous, previous_index in checker.ast_module.globals[:global_index] {
|
||||
if !previous.external || previous.link_name != global.link_name {
|
||||
continue
|
||||
}
|
||||
canonical := checker.external_global_canonical[previous_index]
|
||||
if canonical == ast.INVALID_GLOBAL {
|
||||
canonical = ast.global_id(previous_index)
|
||||
}
|
||||
canonical_index := int(canonical)
|
||||
if canonical_index < 0 || canonical_index >= len(checker.ast_module.globals) {
|
||||
canonical = ast.global_id(previous_index)
|
||||
canonical_index = previous_index
|
||||
}
|
||||
checker.external_global_canonical[global_index] = canonical
|
||||
canonical_global := checker.ast_module.globals[canonical_index]
|
||||
canonical_type := checker.global_types[canonical_index]
|
||||
if !types.equal(checker.global_types[global_index], canonical_type) ||
|
||||
global.writable != canonical_global.writable {
|
||||
checker.external_global_diagnostics[global_index] = source.addf(
|
||||
checker.diagnostics,
|
||||
global.span,
|
||||
"conflicting external C variable declarations for '%s'",
|
||||
global.link_name,
|
||||
)
|
||||
}
|
||||
checker.global_types[global_index] = canonical_type
|
||||
break
|
||||
}
|
||||
for function in checker.ast_module.functions {
|
||||
if !function.c_abi || function.has_body || len(function.unsupported_reason) > 0 ||
|
||||
symbol_text(checker, function.name) != global.link_name {
|
||||
continue
|
||||
}
|
||||
if checker.external_global_diagnostics[global_index] == source.INVALID_DIAGNOSTIC {
|
||||
checker.external_global_diagnostics[global_index] = source.addf(
|
||||
checker.diagnostics,
|
||||
global.span,
|
||||
"external C variable '%s' conflicts with a C function declaration",
|
||||
global.link_name,
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validate_declarations :: proc(checker: ^Checker) {
|
||||
for function, function_id in checker.ast_module.functions {
|
||||
if len(function.unsupported_reason) > 0 {
|
||||
@@ -1362,6 +1430,9 @@ infer_all :: proc(checker: ^Checker) {
|
||||
changed := false
|
||||
spec_count := len(checker.specs)
|
||||
for global, index in checker.ast_module.globals {
|
||||
if global.external {
|
||||
continue
|
||||
}
|
||||
inferred := infer_expr(checker, global.expr, nil, global.pkg, global.file)
|
||||
if is_runtime_type(checker, type_from_syntax(global.type)) {
|
||||
continue
|
||||
@@ -1392,6 +1463,9 @@ prune_specs :: proc(checker: ^Checker) {
|
||||
mark_spec_demanded(checker, find_spec(checker, main_template, nil), &stack)
|
||||
}
|
||||
for global in checker.ast_module.globals {
|
||||
if global.external {
|
||||
continue
|
||||
}
|
||||
_ = infer_expr(checker, global.expr, nil, global.pkg, global.file, &stack)
|
||||
}
|
||||
for len(stack) > 0 {
|
||||
@@ -1449,6 +1523,27 @@ add_unique_global :: proc(values: ^[dynamic]hir.Global_Id, value: hir.Global_Id)
|
||||
append(values, value)
|
||||
}
|
||||
|
||||
build_global_reference :: proc(
|
||||
checker: ^Checker,
|
||||
global: ast.Global_Id,
|
||||
span: source.Span,
|
||||
global_reads: ^[dynamic]hir.Global_Id,
|
||||
) -> hir.Expr_Id {
|
||||
if int(global) < len(checker.external_global_diagnostics) {
|
||||
diagnostic := checker.external_global_diagnostics[global]
|
||||
if diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
return invalid_hir_expr(checker, span, diagnostic, checker.global_types[global])
|
||||
}
|
||||
}
|
||||
hir_global := hir.Global_Id(global)
|
||||
add_unique_global(global_reads, hir_global)
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Global, span=span, type=checker.global_types[global],
|
||||
target=hir.global_ref(hir_global), left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
|
||||
add_unique_function :: proc(values: ^[dynamic]hir.Function_Id, value: hir.Function_Id) {
|
||||
for existing in values {
|
||||
if existing == value {
|
||||
@@ -1689,13 +1784,23 @@ hir_location_writable :: proc(checker: ^Checker, expr_id: hir.Expr_Id, locals: [
|
||||
return local.mutable
|
||||
}
|
||||
}
|
||||
case .Global:
|
||||
id := hir.as_global(expr.target)
|
||||
return id != hir.INVALID_GLOBAL && int(id) < len(checker.module.globals) &&
|
||||
checker.module.globals[id].writable
|
||||
case .Deref:
|
||||
pointer_type := checker.module.exprs[expr.left].type
|
||||
return types.is_mutable(pointer_type, &checker.module.types)
|
||||
case .Index:
|
||||
container_type := checker.module.exprs[expr.left].type
|
||||
item, ok := types.container(container_type, &checker.module.types)
|
||||
return ok && item.mutable
|
||||
if !ok || !item.mutable {
|
||||
return false
|
||||
}
|
||||
if types.is_array(container_type, &checker.module.types) {
|
||||
return hir_location_writable(checker, expr.left, locals)
|
||||
}
|
||||
return true
|
||||
case .Field:
|
||||
base_type := checker.module.exprs[expr.left].type
|
||||
if types.is_pointer(base_type, &checker.module.types) {
|
||||
@@ -2188,12 +2293,7 @@ build_expr :: proc(
|
||||
id := add_package_resolution_diagnostic(checker, expr, file)
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
} else if global := find_global(checker, expr.name, target_pkg); global != ast.INVALID_GLOBAL {
|
||||
hir_global := hir.Global_Id(global)
|
||||
add_unique_global(global_reads, hir_global)
|
||||
last = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Global, span=expr.span, type=checker.global_types[global],
|
||||
target=hir.global_ref(hir_global), left = hir.INVALID_EXPR, right = hir.INVALID_EXPR, diagnostic = source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
last = build_global_reference(checker, global, expr.span, global_reads)
|
||||
} else {
|
||||
template := find_template(checker, expr.name, target_pkg)
|
||||
if template != ast.INVALID_FUNCTION && checker.ast_module.functions[template].c_abi {
|
||||
@@ -2241,13 +2341,7 @@ build_expr :: proc(
|
||||
}
|
||||
if callee == hir.INVALID_EXPR {
|
||||
if global := find_global(checker, expr.name, target_pkg); global != ast.INVALID_GLOBAL {
|
||||
hir_global := hir.Global_Id(global)
|
||||
add_unique_global(global_reads, hir_global)
|
||||
callee = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Global, span=expr.span, type=checker.global_types[global],
|
||||
target=hir.global_ref(hir_global), left=hir.INVALID_EXPR,
|
||||
right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
callee = build_global_reference(checker, global, expr.span, global_reads)
|
||||
callee_from_global = true
|
||||
}
|
||||
}
|
||||
@@ -2561,6 +2655,9 @@ make_link_name :: proc(checker: ^Checker, id: Spec_Id) -> string {
|
||||
return fmt.aprintf("main", allocator = checker.allocator)
|
||||
}
|
||||
if !function.has_body && function.c_abi {
|
||||
if len(function.link_name) > 0 {
|
||||
return strings.clone(function.link_name, checker.allocator)
|
||||
}
|
||||
return fmt.aprintf("%s", symbol_text(checker, function.name), allocator = checker.allocator)
|
||||
}
|
||||
builder := strings.builder_make(checker.allocator)
|
||||
@@ -3085,6 +3182,41 @@ expr_problematic :: proc(checker: ^Checker, expr_id: hir.Expr_Id) -> bool {
|
||||
|
||||
build_globals :: proc(checker: ^Checker) {
|
||||
for global, global_index in checker.ast_module.globals {
|
||||
if global.external {
|
||||
global_type := checker.global_types[global_index]
|
||||
writable := global.writable
|
||||
canonical := checker.external_global_canonical[global_index]
|
||||
if canonical != ast.INVALID_GLOBAL && int(canonical) < len(checker.ast_module.globals) {
|
||||
canonical_global := checker.ast_module.globals[canonical]
|
||||
writable = canonical_global.writable
|
||||
global_type = checker.global_types[canonical]
|
||||
}
|
||||
diagnostic := checker.external_global_diagnostics[global_index]
|
||||
if !is_runtime_type(checker, global_type) {
|
||||
if diagnostic == source.INVALID_DIAGNOSTIC {
|
||||
diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
global.span,
|
||||
"could not resolve a concrete type for external global '%s'",
|
||||
symbol_text(checker, global.name),
|
||||
)
|
||||
}
|
||||
global_type = types.I64
|
||||
}
|
||||
_ = hir.global_id(len(checker.module.globals))
|
||||
append(&checker.module.globals, hir.Global{
|
||||
name=global.name,
|
||||
link_name=strings.clone(global.link_name, checker.allocator),
|
||||
type=global_type,
|
||||
expr=hir.INVALID_EXPR,
|
||||
external=true,
|
||||
writable=writable,
|
||||
direct_problem=diagnostic != source.INVALID_DIAGNOSTIC,
|
||||
problematic=diagnostic != source.INVALID_DIAGNOSTIC,
|
||||
diagnostic=diagnostic,
|
||||
})
|
||||
continue
|
||||
}
|
||||
dependencies: [dynamic]hir.Global_Id
|
||||
dependencies.allocator = checker.allocator
|
||||
calls: [dynamic]hir.Function_Id
|
||||
@@ -3139,10 +3271,13 @@ build_globals :: proc(checker: ^Checker) {
|
||||
&checker.module.globals,
|
||||
hir.Global {
|
||||
name = global.name,
|
||||
link_name = strings.clone(global.link_name, checker.allocator),
|
||||
type = global_type,
|
||||
expr = expr,
|
||||
static_value = static_value,
|
||||
is_static = is_static,
|
||||
external = false,
|
||||
writable = false,
|
||||
dependencies = dependencies,
|
||||
calls = calls[:],
|
||||
direct_problem = expr_problematic(checker, expr),
|
||||
@@ -3386,6 +3521,14 @@ check :: proc(
|
||||
checker.cycle_stack.allocator = allocator
|
||||
build_symbol_indexes(&checker)
|
||||
checker.global_types = make([]types.Type, len(ast_module.globals), allocator)
|
||||
checker.external_global_canonical = make([]ast.Global_Id, len(ast_module.globals), allocator)
|
||||
checker.external_global_diagnostics = make([]source.Diagnostic_Id, len(ast_module.globals), allocator)
|
||||
for &canonical in checker.external_global_canonical {
|
||||
canonical = ast.INVALID_GLOBAL
|
||||
}
|
||||
for &diagnostic in checker.external_global_diagnostics {
|
||||
diagnostic = source.INVALID_DIAGNOSTIC
|
||||
}
|
||||
checker.constants = make([]Constant, len(ast_module.exprs), allocator)
|
||||
checker.template_diagnostics = make([]source.Diagnostic_Id, len(ast_module.functions), allocator)
|
||||
for &diagnostic in checker.template_diagnostics {
|
||||
@@ -3400,6 +3543,8 @@ check :: proc(
|
||||
delete(checker.global_index, allocator)
|
||||
delete(checker.import_index, allocator)
|
||||
delete(checker.global_types, allocator)
|
||||
delete(checker.external_global_canonical, allocator)
|
||||
delete(checker.external_global_diagnostics, allocator)
|
||||
delete(checker.constants, allocator)
|
||||
delete(checker.template_diagnostics, allocator)
|
||||
delete(checker.constant_stack)
|
||||
@@ -3433,6 +3578,7 @@ check :: proc(
|
||||
validate_type_nodes(&checker)
|
||||
validate_declarations(&checker)
|
||||
infer_all(&checker)
|
||||
validate_external_globals(&checker)
|
||||
prune_specs(&checker)
|
||||
build_globals(&checker)
|
||||
for index := 0; index < len(checker.specs); index += 1 {
|
||||
|
||||
Reference in New Issue
Block a user