io interface (first pass)
This commit is contained in:
@@ -183,6 +183,8 @@ Checker :: struct {
|
||||
// build_globals, so the 1:1 module.globals <-> ast.globals index identity holds.
|
||||
anon_globals: [dynamic]hir.Global,
|
||||
main_symbol: symbol.Id,
|
||||
io_main: bool,
|
||||
io_provider_template: ast.Function_Id,
|
||||
sink_symbol: symbol.Id,
|
||||
type_symbol: symbol.Id,
|
||||
current_result: types.Type,
|
||||
@@ -894,6 +896,66 @@ find_template :: proc(checker: ^Checker, name: symbol.Id, pkg := ast.Package_Id(
|
||||
return find_function_symbol(checker.function_index, pkg, name, file)
|
||||
}
|
||||
|
||||
configure_io_main :: proc(checker: ^Checker) {
|
||||
main_template := find_template(checker, checker.main_symbol, 0)
|
||||
if main_template == ast.INVALID_FUNCTION {
|
||||
return
|
||||
}
|
||||
main := checker.ast_module.functions[main_template]
|
||||
if len(main.params) != 1 || main.params[0].comptime_value {
|
||||
return
|
||||
}
|
||||
|
||||
parameter_type := types.resolve_alias(
|
||||
type_from_syntax(checker, main.params[0].type, main.pkg, main.file),
|
||||
&checker.module.types,
|
||||
)
|
||||
io_name := symbol.intern(checker.symbols, "Io")
|
||||
io_package := ast.INVALID_PACKAGE
|
||||
io_type := types.INVALID
|
||||
for import_item in checker.ast_module.imports {
|
||||
if import_item.valid && import_item.path == "@std/io" {
|
||||
candidate := types.find_named(&checker.module.types, u32(import_item.target), u32(io_name))
|
||||
if types.equal(parameter_type, candidate) {
|
||||
io_package = import_item.target
|
||||
io_type = candidate
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if io_package == ast.INVALID_PACKAGE {
|
||||
return
|
||||
}
|
||||
|
||||
provider_name := symbol.intern(checker.symbols, "_system")
|
||||
provider := ast.INVALID_FUNCTION
|
||||
for function, function_id in checker.ast_module.functions {
|
||||
if function.pkg != io_package || function.name != provider_name {
|
||||
continue
|
||||
}
|
||||
result := types.resolve_alias(
|
||||
type_from_syntax(checker, function.result, function.pkg, function.file),
|
||||
&checker.module.types,
|
||||
)
|
||||
if 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)
|
||||
break
|
||||
}
|
||||
}
|
||||
if provider == ast.INVALID_FUNCTION {
|
||||
checker.template_diagnostics[main_template] = source.add(
|
||||
checker.diagnostics,
|
||||
main.span,
|
||||
"@std/io does not provide the required '_system func() Io' startup implementation",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
checker.io_main = true
|
||||
checker.io_provider_template = provider
|
||||
}
|
||||
|
||||
find_global :: proc(checker: ^Checker, name: symbol.Id, pkg := ast.Package_Id(0), file := ast.INVALID_FILE) -> ast.Global_Id {
|
||||
return find_global_symbol(checker.global_index, pkg, name, file)
|
||||
}
|
||||
@@ -2095,6 +2157,18 @@ validate_external_globals :: proc(checker: ^Checker) {
|
||||
}
|
||||
}
|
||||
|
||||
runtime_write_declaration_matches :: proc(checker: ^Checker, function: ast.Function) -> bool {
|
||||
if function.variadic || len(function.params) != 3 || types.is_valid(function.error) {
|
||||
return false
|
||||
}
|
||||
store := &checker.module.types
|
||||
buffer := types.optional(store, types.pointer(store, types.ANYOPAQUE, false, true))
|
||||
return type_from_syntax(checker, function.params[0].type, function.pkg, function.file) == types.C_INT &&
|
||||
type_from_syntax(checker, function.params[1].type, function.pkg, function.file) == buffer &&
|
||||
type_from_syntax(checker, function.params[2].type, function.pkg, function.file) == types.C_ULONG &&
|
||||
type_from_syntax(checker, function.result, function.pkg, function.file) == types.C_LONG
|
||||
}
|
||||
|
||||
validate_declarations :: proc(checker: ^Checker) {
|
||||
for function, function_id in checker.ast_module.functions {
|
||||
if len(function.unsupported_reason) > 0 {
|
||||
@@ -2261,6 +2335,14 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
"main must have a body",
|
||||
)
|
||||
}
|
||||
external_name := function.link_name if len(function.link_name) > 0 else symbol_text(checker, function.name)
|
||||
if external_name == "write" && !runtime_write_declaration_matches(checker, function) {
|
||||
checker.template_diagnostics[function_id] = source.add(
|
||||
checker.diagnostics,
|
||||
function.span,
|
||||
"external C function 'write' conflicts with the compiler runtime declaration",
|
||||
)
|
||||
}
|
||||
}
|
||||
mark_block_imports_used(checker, function.body, function.file)
|
||||
delete(locals)
|
||||
@@ -4070,6 +4152,9 @@ infer_all :: proc(checker: ^Checker) {
|
||||
if main_template != ast.INVALID_FUNCTION {
|
||||
ensure_spec(checker, main_template, nil)
|
||||
}
|
||||
if checker.io_main {
|
||||
ensure_spec(checker, checker.io_provider_template, nil)
|
||||
}
|
||||
|
||||
defaults_applied := false
|
||||
for {
|
||||
@@ -4196,6 +4281,9 @@ prune_specs :: proc(checker: ^Checker) {
|
||||
if main_template != ast.INVALID_FUNCTION {
|
||||
mark_spec_demanded(checker, find_spec(checker, main_template, nil), &stack)
|
||||
}
|
||||
if checker.io_main {
|
||||
mark_spec_demanded(checker, find_spec(checker, checker.io_provider_template, nil), &stack)
|
||||
}
|
||||
for global in checker.ast_module.globals {
|
||||
if global.external {
|
||||
continue
|
||||
@@ -5192,7 +5280,11 @@ build_compound_expr :: proc(
|
||||
value := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
actual := checker.module.exprs[value].type
|
||||
valid_target := types.is_concrete_scalar(target) && !types.is_bool(target)
|
||||
valid_actual := types.is_concrete_scalar(actual) && !types.is_bool(actual)
|
||||
actual_repr := types.runtime_representation(actual, store)
|
||||
actual_item, actual_item_ok := types.node(store, actual)
|
||||
explicit_enum := actual_item_ok && actual_item.kind == .Enum && actual_item.explicit_backing
|
||||
valid_actual := (types.is_concrete_scalar(actual) || explicit_enum) &&
|
||||
types.is_concrete_scalar(actual_repr) && !types.is_bool(actual_repr)
|
||||
if !valid_target || !valid_actual {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
@@ -6536,7 +6628,7 @@ build_expr :: proc(
|
||||
make_link_name :: proc(checker: ^Checker, id: Spec_Id) -> string {
|
||||
spec := checker.specs[id]
|
||||
function := checker.ast_module.functions[spec.template]
|
||||
if function.pkg == 0 && function.name == checker.main_symbol {
|
||||
if function.pkg == 0 && function.name == checker.main_symbol && !checker.io_main {
|
||||
return fmt.aprintf("main", allocator = checker.allocator)
|
||||
}
|
||||
if function.generated {
|
||||
@@ -9151,6 +9243,7 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
|
||||
|
||||
problematic := signature_diagnostic != source.INVALID_DIAGNOSTIC ||
|
||||
checker.template_diagnostics[spec.template] != source.INVALID_DIAGNOSTIC
|
||||
native_main := function.pkg == 0 && function.name == checker.main_symbol && !checker.io_main
|
||||
if !function.has_body {
|
||||
assert(spec.hir_id == hir.function_id(len(checker.module.functions)))
|
||||
append(
|
||||
@@ -9161,7 +9254,7 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
|
||||
calling_convention = .C if function.c_abi else .Brolang,
|
||||
implementation = .Declaration,
|
||||
linkage = .External if function.c_abi else .Internal,
|
||||
is_main = function.pkg == 0 && function.name == checker.main_symbol,
|
||||
is_main = native_main,
|
||||
variadic = function.variadic,
|
||||
params = params[:],
|
||||
result = spec.result,
|
||||
@@ -9259,10 +9352,10 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
|
||||
hir.Function {
|
||||
name = function.name,
|
||||
link_name = make_link_name(checker, id),
|
||||
calling_convention = .C if function.c_abi || (function.pkg == 0 && function.name == checker.main_symbol) else .Brolang,
|
||||
calling_convention = .C if function.c_abi || native_main else .Brolang,
|
||||
implementation = .Definition,
|
||||
linkage = .External if function.c_abi || (function.pkg == 0 && function.name == checker.main_symbol) else .Internal,
|
||||
is_main = function.pkg == 0 && function.name == checker.main_symbol,
|
||||
linkage = .External if function.c_abi || native_main else .Internal,
|
||||
is_main = native_main,
|
||||
variadic = function.variadic,
|
||||
params = params[:],
|
||||
result = spec.result,
|
||||
@@ -9710,6 +9803,7 @@ check :: proc(
|
||||
symbols = symbols,
|
||||
module = hir.init_module(selected, allocator),
|
||||
main_symbol = symbol.intern(symbols, "main"),
|
||||
io_provider_template = ast.INVALID_FUNCTION,
|
||||
sink_symbol = symbol.intern(symbols, "_"),
|
||||
type_symbol = symbol.intern(symbols, "type"),
|
||||
target = selected,
|
||||
@@ -9823,6 +9917,7 @@ check :: proc(
|
||||
|
||||
validate_type_nodes(&checker)
|
||||
validate_declarations(&checker)
|
||||
configure_io_main(&checker)
|
||||
infer_all(&checker)
|
||||
validate_external_globals(&checker)
|
||||
prune_specs(&checker)
|
||||
@@ -9849,19 +9944,29 @@ check :: proc(
|
||||
synthesize_trap_main(&checker)
|
||||
} else {
|
||||
template := ast_module.functions[main_template]
|
||||
valid_params := len(template.params) == 0 || checker.io_main && len(template.params) == 1
|
||||
if main_declarations != 1 ||
|
||||
!template.has_body ||
|
||||
len(template.params) != 0 ||
|
||||
!valid_params ||
|
||||
!(template.result == types.VOID || template.result == types.I32 || template.result == types.INT) {
|
||||
id := checker.template_diagnostics[main_template]
|
||||
if id == source.INVALID_DIAGNOSTIC {
|
||||
id = source.add(
|
||||
diagnostics,
|
||||
template.span,
|
||||
"main must be unique, have a body, take no parameters, and return void, i32, or int",
|
||||
"main must be unique, have a body, take no parameters or one @std/io Io, and return void, i32, or int",
|
||||
)
|
||||
}
|
||||
checker.module.injected_main = hir.INVALID_FUNCTION
|
||||
checker.module.io_provider = hir.INVALID_FUNCTION
|
||||
replace_main_with_trap(&checker, id)
|
||||
} else if checker.io_main {
|
||||
main_spec := find_spec(&checker, main_template, nil)
|
||||
provider_spec := find_spec(&checker, checker.io_provider_template, nil)
|
||||
if main_spec != INVALID_SPEC && provider_spec != INVALID_SPEC {
|
||||
checker.module.injected_main = checker.specs[main_spec].hir_id
|
||||
checker.module.io_provider = checker.specs[provider_spec].hir_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user