package-type imports
This commit is contained in:
+302
-176
@@ -7,6 +7,7 @@ import "../types"
|
||||
import "base:intrinsics"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:slice"
|
||||
import "core:strings"
|
||||
|
||||
Spec :: struct {
|
||||
@@ -40,14 +41,23 @@ Constant :: struct {
|
||||
value: i128,
|
||||
}
|
||||
|
||||
Symbol_Index_Entry :: struct {
|
||||
scope: int,
|
||||
name: string,
|
||||
id: int,
|
||||
}
|
||||
|
||||
Checker :: struct {
|
||||
ast_module: ^ast.Module,
|
||||
diagnostics: ^source.Diagnostics,
|
||||
module: hir.Module,
|
||||
specs: [dynamic]Spec,
|
||||
global_types: []types.Type,
|
||||
constants: []Constant,
|
||||
allocator: mem.Allocator,
|
||||
ast_module: ^ast.Module,
|
||||
diagnostics: ^source.Diagnostics,
|
||||
module: hir.Module,
|
||||
specs: [dynamic]Spec,
|
||||
function_index: []Symbol_Index_Entry,
|
||||
global_index: []Symbol_Index_Entry,
|
||||
import_index: []Symbol_Index_Entry,
|
||||
global_types: []types.Type,
|
||||
constants: []Constant,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
eval_constant :: proc(checker: ^Checker, expr_id: int) -> Constant {
|
||||
@@ -116,22 +126,123 @@ type_from_syntax :: proc(value: ast.Type_Syntax) -> types.Type {
|
||||
return types.INVALID
|
||||
}
|
||||
|
||||
find_template :: proc(checker: ^Checker, name: string) -> int {
|
||||
for function, index in checker.ast_module.functions {
|
||||
if function.name == name {
|
||||
return index
|
||||
symbol_index_less :: proc(left, right: Symbol_Index_Entry) -> bool {
|
||||
if left.scope != right.scope {
|
||||
return left.scope < right.scope
|
||||
}
|
||||
if left.name != right.name {
|
||||
return left.name < right.name
|
||||
}
|
||||
return left.id < right.id
|
||||
}
|
||||
|
||||
find_symbol :: proc(index: []Symbol_Index_Entry, scope: int, name: string) -> int {
|
||||
low := 0
|
||||
high := len(index)
|
||||
for low < high {
|
||||
middle := low + (high-low)/2
|
||||
entry := index[middle]
|
||||
if entry.scope < scope || entry.scope == scope && entry.name < name {
|
||||
low = middle + 1
|
||||
} else {
|
||||
high = middle
|
||||
}
|
||||
}
|
||||
if low < len(index) && index[low].scope == scope && index[low].name == name {
|
||||
return index[low].id
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
find_global :: proc(checker: ^Checker, name: string) -> int {
|
||||
for global, index in checker.ast_module.globals {
|
||||
if global.name == name {
|
||||
return index
|
||||
}
|
||||
build_symbol_indexes :: proc(checker: ^Checker) {
|
||||
checker.function_index = make([]Symbol_Index_Entry, len(checker.ast_module.functions), checker.allocator)
|
||||
for function, id in checker.ast_module.functions {
|
||||
checker.function_index[id] = Symbol_Index_Entry{scope=function.pkg, name=function.name, id=id}
|
||||
}
|
||||
return -1
|
||||
slice.sort_by(checker.function_index, symbol_index_less)
|
||||
|
||||
checker.global_index = make([]Symbol_Index_Entry, len(checker.ast_module.globals), checker.allocator)
|
||||
for global, id in checker.ast_module.globals {
|
||||
checker.global_index[id] = Symbol_Index_Entry{scope=global.pkg, name=global.name, id=id}
|
||||
}
|
||||
slice.sort_by(checker.global_index, symbol_index_less)
|
||||
|
||||
checker.import_index = make([]Symbol_Index_Entry, len(checker.ast_module.imports), checker.allocator)
|
||||
for import_item, id in checker.ast_module.imports {
|
||||
checker.import_index[id] = Symbol_Index_Entry{scope=import_item.file, name=import_item.alias, id=id}
|
||||
}
|
||||
slice.sort_by(checker.import_index, symbol_index_less)
|
||||
}
|
||||
|
||||
find_template :: proc(checker: ^Checker, name: string, pkg := 0) -> int {
|
||||
return find_symbol(checker.function_index, pkg, name)
|
||||
}
|
||||
|
||||
find_global :: proc(checker: ^Checker, name: string, pkg := 0) -> int {
|
||||
return find_symbol(checker.global_index, pkg, name)
|
||||
}
|
||||
|
||||
find_import :: proc(checker: ^Checker, file: int, alias: string, mark_used := false) -> int {
|
||||
id := find_symbol(checker.import_index, file, alias)
|
||||
if id >= 0 && mark_used {
|
||||
checker.ast_module.imports[id].used = true
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
expr_package :: proc(checker: ^Checker, expr: ast.Expr, pkg, file: int, mark_used := false) -> (int, bool) {
|
||||
if expr.qualifier == "" {
|
||||
return pkg, true
|
||||
}
|
||||
import_id := find_import(checker, file, expr.qualifier, mark_used)
|
||||
if import_id < 0 {
|
||||
return -1, false
|
||||
}
|
||||
import_item := checker.ast_module.imports[import_id]
|
||||
if import_item.target < 0 || import_item.target >= len(checker.ast_module.packages) ||
|
||||
!checker.ast_module.packages[import_item.target].available {
|
||||
return import_item.target, false
|
||||
}
|
||||
return import_item.target, true
|
||||
}
|
||||
|
||||
add_package_resolution_diagnostic :: proc(checker: ^Checker, expr: ast.Expr, file: int) -> int {
|
||||
if find_import(checker, file, expr.qualifier) < 0 {
|
||||
return source.addf(checker.diagnostics, expr.span, "unknown package alias '%s'", expr.qualifier)
|
||||
}
|
||||
return source.addf(checker.diagnostics, expr.span, "unavailable imported package '%s'", expr.qualifier)
|
||||
}
|
||||
|
||||
add_name_resolution_diagnostic :: proc(checker: ^Checker, expr: ast.Expr, target_pkg: int) -> int {
|
||||
if find_template(checker, expr.text, target_pkg) >= 0 {
|
||||
return source.addf(checker.diagnostics, expr.span, "'%s' is a function, not a global value", expr.text)
|
||||
}
|
||||
if expr.qualifier != "" {
|
||||
return source.addf(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
"package '%s' has no member '%s'",
|
||||
expr.qualifier,
|
||||
expr.text,
|
||||
)
|
||||
}
|
||||
return source.addf(checker.diagnostics, expr.span, "unresolved global '%s'", expr.text)
|
||||
}
|
||||
|
||||
add_call_resolution_diagnostic :: proc(checker: ^Checker, expr: ast.Expr, target_pkg: int) -> int {
|
||||
if find_global(checker, expr.text, target_pkg) >= 0 {
|
||||
return source.addf(checker.diagnostics, expr.span, "'%s' is a global, not a function", expr.text)
|
||||
}
|
||||
if expr.qualifier != "" {
|
||||
return source.addf(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
"package '%s' has no member '%s'",
|
||||
expr.qualifier,
|
||||
expr.text,
|
||||
)
|
||||
}
|
||||
return source.addf(checker.diagnostics, expr.span, "unresolved function '%s'", expr.text)
|
||||
}
|
||||
|
||||
contains_name :: proc(names: []string, name: string) -> bool {
|
||||
@@ -143,41 +254,31 @@ contains_name :: proc(names: []string, name: string) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
validate_expr_names :: proc(checker: ^Checker, expr_id: int, locals: []string) {
|
||||
mark_expr_imports_used :: proc(checker: ^Checker, expr_id, file: int) {
|
||||
if expr_id < 0 || expr_id >= len(checker.ast_module.exprs) {
|
||||
return
|
||||
}
|
||||
expr := checker.ast_module.exprs[expr_id]
|
||||
switch expr.kind {
|
||||
case .Name:
|
||||
if !contains_name(locals, expr.text) && find_global(checker, expr.text) < 0 {
|
||||
source.addf(checker.diagnostics, expr.span, "unresolved name '%s'", expr.text)
|
||||
if expr.qualifier != "" {
|
||||
_ = find_import(checker, file, expr.qualifier, true)
|
||||
}
|
||||
case .Call:
|
||||
if find_template(checker, expr.text) < 0 {
|
||||
source.addf(checker.diagnostics, expr.span, "unresolved function '%s'", expr.text)
|
||||
if expr.qualifier != "" {
|
||||
_ = find_import(checker, file, expr.qualifier, true)
|
||||
}
|
||||
for arg in expr.args {
|
||||
validate_expr_names(checker, arg, locals)
|
||||
mark_expr_imports_used(checker, arg, file)
|
||||
}
|
||||
case .Add:
|
||||
validate_expr_names(checker, expr.left, locals)
|
||||
validate_expr_names(checker, expr.right, locals)
|
||||
mark_expr_imports_used(checker, expr.left, file)
|
||||
mark_expr_imports_used(checker, expr.right, file)
|
||||
case .Invalid, .Integer:
|
||||
}
|
||||
}
|
||||
|
||||
validate_templates :: proc(checker: ^Checker) {
|
||||
for global in checker.ast_module.globals {
|
||||
if global.type == .Void {
|
||||
source.add(
|
||||
checker.diagnostics,
|
||||
global.span,
|
||||
"void is only valid as a function result type",
|
||||
)
|
||||
}
|
||||
validate_expr_names(checker, global.expr, nil)
|
||||
}
|
||||
validate_declarations :: proc(checker: ^Checker) {
|
||||
for function in checker.ast_module.functions {
|
||||
locals: [dynamic]string
|
||||
locals.allocator = checker.allocator
|
||||
@@ -202,36 +303,8 @@ validate_templates :: proc(checker: ^Checker) {
|
||||
for statement_id in function.body {
|
||||
statement := checker.ast_module.statements[statement_id]
|
||||
switch statement.kind {
|
||||
case .Declaration:
|
||||
validate_expr_names(checker, statement.expr, locals[:])
|
||||
if statement.type == .Void {
|
||||
source.add(
|
||||
checker.diagnostics,
|
||||
statement.span,
|
||||
"void is only valid as a function result type",
|
||||
)
|
||||
}
|
||||
if contains_name(locals[:], statement.name) {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
statement.span,
|
||||
"duplicate local '%s'",
|
||||
statement.name,
|
||||
)
|
||||
}
|
||||
append(&locals, statement.name)
|
||||
case .Assignment:
|
||||
validate_expr_names(checker, statement.expr, locals[:])
|
||||
if statement.name != "_" && !contains_name(locals[:], statement.name) {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
statement.span,
|
||||
"cannot assign unresolved local '%s'",
|
||||
statement.name,
|
||||
)
|
||||
}
|
||||
case .Return, .Expression:
|
||||
validate_expr_names(checker, statement.expr, locals[:])
|
||||
case .Declaration, .Assignment, .Return, .Expression:
|
||||
mark_expr_imports_used(checker, statement.expr, function.file)
|
||||
case .Invalid:
|
||||
}
|
||||
}
|
||||
@@ -268,6 +341,19 @@ specialized_param_type :: proc(syntax: ast.Type_Syntax, actual: types.Type) -> t
|
||||
return declared
|
||||
}
|
||||
|
||||
can_specialize :: proc(function: ast.Function, actual_args: []types.Type) -> bool {
|
||||
for param, index in function.params {
|
||||
actual := types.INVALID
|
||||
if index < len(actual_args) {
|
||||
actual = actual_args[index]
|
||||
}
|
||||
if !types.is_concrete_integer(specialized_param_type(param.type, actual)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
ensure_spec :: proc(checker: ^Checker, template: int, actual_args: []types.Type) -> int {
|
||||
function := checker.ast_module.functions[template]
|
||||
signature: [dynamic]types.Type
|
||||
@@ -286,7 +372,7 @@ ensure_spec :: proc(checker: ^Checker, template: int, actual_args: []types.Type)
|
||||
}
|
||||
}
|
||||
result := type_from_syntax(function.result)
|
||||
if function.name == "main" && function.result == .Int {
|
||||
if function.pkg == 0 && function.name == "main" && function.result == .Int {
|
||||
result = types.I32
|
||||
}
|
||||
index := len(checker.specs)
|
||||
@@ -297,7 +383,7 @@ ensure_spec :: proc(checker: ^Checker, template: int, actual_args: []types.Type)
|
||||
return index
|
||||
}
|
||||
|
||||
infer_expr :: proc(checker: ^Checker, expr_id: int, locals: []Infer_Local) -> types.Type {
|
||||
infer_expr :: proc(checker: ^Checker, expr_id: int, locals: []Infer_Local, pkg := 0, file := 0) -> types.Type {
|
||||
if expr_id < 0 || expr_id >= len(checker.ast_module.exprs) {
|
||||
return types.INVALID
|
||||
}
|
||||
@@ -315,27 +401,49 @@ infer_expr :: proc(checker: ^Checker, expr_id: int, locals: []Infer_Local) -> ty
|
||||
case .Integer:
|
||||
return types.smallest_signed_for_literal(expr.integer)
|
||||
case .Name:
|
||||
local_type := find_infer_local(locals, expr.text)
|
||||
if types.is_valid(local_type) {
|
||||
return local_type
|
||||
if expr.qualifier == "" {
|
||||
local_type := find_infer_local(locals, expr.text)
|
||||
if types.is_valid(local_type) {
|
||||
return local_type
|
||||
}
|
||||
}
|
||||
global := find_global(checker, expr.text)
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file)
|
||||
if !available {
|
||||
return types.INVALID
|
||||
}
|
||||
global := find_global(checker, expr.text, target_pkg)
|
||||
if global >= 0 {
|
||||
return checker.global_types[global]
|
||||
}
|
||||
return types.INVALID
|
||||
case .Add:
|
||||
left := infer_expr(checker, expr.left, locals)
|
||||
right := infer_expr(checker, expr.right, locals)
|
||||
left := infer_expr(checker, expr.left, locals, pkg, file)
|
||||
right := infer_expr(checker, expr.right, locals, pkg, file)
|
||||
return types.widest(left, right)
|
||||
case .Call:
|
||||
template := find_template(checker, expr.text)
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file)
|
||||
if !available {
|
||||
return types.INVALID
|
||||
}
|
||||
template := find_template(checker, expr.text, target_pkg)
|
||||
if template < 0 {
|
||||
return types.INVALID
|
||||
}
|
||||
args := make([]types.Type, len(expr.args), checker.allocator)
|
||||
for arg, index in expr.args {
|
||||
args[index] = infer_expr(checker, arg, locals)
|
||||
args[index] = infer_expr(checker, arg, locals, pkg, file)
|
||||
}
|
||||
function := checker.ast_module.functions[template]
|
||||
if !can_specialize(function, args) {
|
||||
delete(args, checker.allocator)
|
||||
declared := type_from_syntax(function.result)
|
||||
if function.pkg == 0 && function.name == "main" && function.result == .Int {
|
||||
return types.I32
|
||||
}
|
||||
if declared.kind == .Concrete || declared.kind == .Void {
|
||||
return declared
|
||||
}
|
||||
return types.INVALID
|
||||
}
|
||||
spec := ensure_spec(checker, template, args)
|
||||
delete(args, checker.allocator)
|
||||
@@ -348,7 +456,7 @@ infer_spec_result :: proc(checker: ^Checker, spec_id: int) -> types.Type {
|
||||
spec := checker.specs[spec_id]
|
||||
function := checker.ast_module.functions[spec.template]
|
||||
declared := type_from_syntax(function.result)
|
||||
if function.name == "main" && function.result == .Int {
|
||||
if function.pkg == 0 && function.name == "main" && function.result == .Int {
|
||||
declared = types.I32
|
||||
}
|
||||
|
||||
@@ -368,17 +476,17 @@ infer_spec_result :: proc(checker: ^Checker, spec_id: int) -> types.Type {
|
||||
statement := checker.ast_module.statements[statement_id]
|
||||
#partial switch statement.kind {
|
||||
case .Declaration:
|
||||
value_type := infer_expr(checker, statement.expr, locals[:])
|
||||
value_type := infer_expr(checker, statement.expr, locals[:], function.pkg, function.file)
|
||||
declared_local := type_from_syntax(statement.type)
|
||||
if declared_local.kind == .Concrete {
|
||||
value_type = declared_local
|
||||
}
|
||||
append(&locals, Infer_Local{name = statement.name, type = value_type})
|
||||
case .Assignment, .Expression:
|
||||
_ = infer_expr(checker, statement.expr, locals[:])
|
||||
_ = infer_expr(checker, statement.expr, locals[:], function.pkg, function.file)
|
||||
case .Return:
|
||||
if statement.expr >= 0 {
|
||||
returned := infer_expr(checker, statement.expr, locals[:])
|
||||
returned := infer_expr(checker, statement.expr, locals[:], function.pkg, function.file)
|
||||
if !types.is_valid(result) {
|
||||
result = returned
|
||||
} else {
|
||||
@@ -393,6 +501,22 @@ infer_spec_result :: proc(checker: ^Checker, spec_id: int) -> types.Type {
|
||||
return declared
|
||||
}
|
||||
|
||||
merge_inferred_type :: proc(current: ^types.Type, inferred: types.Type) -> bool {
|
||||
if !types.is_concrete_integer(inferred) {
|
||||
return false
|
||||
}
|
||||
if !types.is_concrete_integer(current^) {
|
||||
current^ = inferred
|
||||
return true
|
||||
}
|
||||
merged := types.widest(current^, inferred)
|
||||
if types.is_concrete_integer(merged) && !types.equal(current^, merged) {
|
||||
current^ = merged
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
infer_all :: proc(checker: ^Checker) {
|
||||
for global, index in checker.ast_module.globals {
|
||||
declared := type_from_syntax(global.type)
|
||||
@@ -400,40 +524,25 @@ infer_all :: proc(checker: ^Checker) {
|
||||
checker.global_types[index] = declared
|
||||
}
|
||||
}
|
||||
for _ in 0 ..< max(4, len(checker.ast_module.globals) + 1) {
|
||||
changed := false
|
||||
for global, index in checker.ast_module.globals {
|
||||
if types.is_valid(checker.global_types[index]) {
|
||||
continue
|
||||
}
|
||||
inferred := infer_expr(checker, global.expr, nil)
|
||||
if types.is_valid(inferred) {
|
||||
checker.global_types[index] = inferred
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if !changed {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
main_template := find_template(checker, "main")
|
||||
main_template := find_template(checker, "main", 0)
|
||||
if main_template >= 0 {
|
||||
ensure_spec(checker, main_template, nil)
|
||||
}
|
||||
for global in checker.ast_module.globals {
|
||||
_ = infer_expr(checker, global.expr, nil)
|
||||
}
|
||||
|
||||
for _ in 0 ..< 64 {
|
||||
for {
|
||||
changed := false
|
||||
spec_count := len(checker.specs)
|
||||
for spec_id in 0 ..< spec_count {
|
||||
inferred := infer_spec_result(checker, spec_id)
|
||||
if types.is_valid(inferred) && !types.equal(checker.specs[spec_id].result, inferred) {
|
||||
checker.specs[spec_id].result = inferred
|
||||
changed = true
|
||||
for global, index in checker.ast_module.globals {
|
||||
if type_from_syntax(global.type).kind == .Concrete {
|
||||
continue
|
||||
}
|
||||
inferred := infer_expr(checker, global.expr, nil, global.pkg, global.file)
|
||||
changed = merge_inferred_type(&checker.global_types[index], inferred) || changed
|
||||
}
|
||||
for spec_id := 0; spec_id < len(checker.specs); spec_id += 1 {
|
||||
inferred := infer_spec_result(checker, spec_id)
|
||||
changed = merge_inferred_type(&checker.specs[spec_id].result, inferred) || changed
|
||||
}
|
||||
if len(checker.specs) != spec_count {
|
||||
changed = true
|
||||
@@ -442,14 +551,6 @@ infer_all :: proc(checker: ^Checker) {
|
||||
break
|
||||
}
|
||||
}
|
||||
for global, index in checker.ast_module.globals {
|
||||
if type_from_syntax(global.type).kind != .Concrete {
|
||||
inferred := infer_expr(checker, global.expr, nil)
|
||||
if types.is_concrete_integer(inferred) {
|
||||
checker.global_types[index] = inferred
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_hir_expr :: proc(checker: ^Checker, expr: hir.Expr) -> int {
|
||||
@@ -589,6 +690,8 @@ build_expr :: proc(
|
||||
global_reads: ^[dynamic]int,
|
||||
calls: ^[dynamic]int,
|
||||
expected := types.INVALID,
|
||||
pkg := 0,
|
||||
file := 0,
|
||||
) -> int {
|
||||
if expr_id < 0 || expr_id >= len(checker.ast_module.exprs) {
|
||||
id := source.add(checker.diagnostics, source.Span{}, "missing expression")
|
||||
@@ -605,21 +708,28 @@ build_expr :: proc(
|
||||
case .Integer:
|
||||
unreachable()
|
||||
case .Name:
|
||||
if local, ok := find_build_local(locals, expr.text); ok {
|
||||
return add_hir_expr(
|
||||
checker,
|
||||
hir.Expr {
|
||||
kind = .Local,
|
||||
span = expr.span,
|
||||
type = local.type,
|
||||
target = local.id,
|
||||
left = -1,
|
||||
right = -1,
|
||||
diagnostic = -1,
|
||||
},
|
||||
)
|
||||
if expr.qualifier == "" {
|
||||
if local, ok := find_build_local(locals, expr.text); ok {
|
||||
return add_hir_expr(
|
||||
checker,
|
||||
hir.Expr {
|
||||
kind = .Local,
|
||||
span = expr.span,
|
||||
type = local.type,
|
||||
target = local.id,
|
||||
left = -1,
|
||||
right = -1,
|
||||
diagnostic = -1,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
global := find_global(checker, expr.text)
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
if !available {
|
||||
id := add_package_resolution_diagnostic(checker, expr, file)
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
global := find_global(checker, expr.text, target_pkg)
|
||||
if global >= 0 {
|
||||
add_unique(global_reads, global)
|
||||
return add_hir_expr(
|
||||
@@ -635,11 +745,11 @@ build_expr :: proc(
|
||||
},
|
||||
)
|
||||
}
|
||||
id := source.addf(checker.diagnostics, expr.span, "unresolved name '%s'", expr.text)
|
||||
id := add_name_resolution_diagnostic(checker, expr, target_pkg)
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
case .Add:
|
||||
left := build_expr(checker, expr.left, locals, global_reads, calls)
|
||||
right := build_expr(checker, expr.right, locals, global_reads, calls)
|
||||
left := build_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
right := build_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
left_type := checker.module.exprs[left].type
|
||||
right_type := checker.module.exprs[right].type
|
||||
result := types.widest(left_type, right_type)
|
||||
@@ -666,14 +776,14 @@ build_expr :: proc(
|
||||
},
|
||||
)
|
||||
case .Call:
|
||||
template := find_template(checker, expr.text)
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
if !available {
|
||||
id := add_package_resolution_diagnostic(checker, expr, file)
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
template := find_template(checker, expr.text, target_pkg)
|
||||
if template < 0 {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
"unresolved function '%s'",
|
||||
expr.text,
|
||||
)
|
||||
id := add_call_resolution_diagnostic(checker, expr, target_pkg)
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
if len(expr.args) != len(checker.ast_module.functions[template].params) {
|
||||
@@ -701,6 +811,8 @@ build_expr :: proc(
|
||||
global_reads,
|
||||
calls,
|
||||
arg_expected,
|
||||
pkg,
|
||||
file,
|
||||
)
|
||||
arg_types[index] = checker.module.exprs[built_args[index]].type
|
||||
}
|
||||
@@ -750,24 +862,13 @@ build_expr :: proc(
|
||||
make_link_name :: proc(checker: ^Checker, spec_id: int) -> string {
|
||||
spec := checker.specs[spec_id]
|
||||
function := checker.ast_module.functions[spec.template]
|
||||
if function.name == "main" {
|
||||
if function.pkg == 0 && function.name == "main" {
|
||||
return fmt.aprintf("main", allocator = checker.allocator)
|
||||
}
|
||||
has_generic_params := false
|
||||
for param in function.params {
|
||||
if param.type == .Int {
|
||||
has_generic_params = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if function.c_abi && !has_generic_params {
|
||||
return fmt.aprintf("%s", function.name, allocator = checker.allocator)
|
||||
}
|
||||
builder := strings.builder_make(checker.allocator)
|
||||
defer strings.builder_destroy(&builder)
|
||||
if !function.c_abi {
|
||||
strings.write_string(&builder, "bro__")
|
||||
}
|
||||
strings.write_string(&builder, "bro_c__" if function.c_abi else "bro__")
|
||||
fmt.sbprintf(&builder, "p%d__", function.pkg)
|
||||
strings.write_string(&builder, function.name)
|
||||
for arg in spec.args {
|
||||
strings.write_string(&builder, "__")
|
||||
@@ -783,7 +884,7 @@ build_function :: proc(checker: ^Checker, spec_id: int) {
|
||||
spec := checker.specs[spec_id]
|
||||
function := checker.ast_module.functions[spec.template]
|
||||
signature_diagnostic := -1
|
||||
if !types.is_valid(spec.result) {
|
||||
if spec.result.kind != .Void && !types.is_concrete_integer(spec.result) {
|
||||
checker.specs[spec_id].result = types.I64
|
||||
spec.result = types.I64
|
||||
signature_diagnostic = source.addf(
|
||||
@@ -862,6 +963,8 @@ build_function :: proc(checker: ^Checker, spec_id: int) {
|
||||
&global_reads,
|
||||
&calls,
|
||||
expected,
|
||||
function.pkg,
|
||||
function.file,
|
||||
)
|
||||
value_type := checker.module.exprs[value].type
|
||||
if declared.kind == .Concrete {
|
||||
@@ -929,7 +1032,7 @@ build_function :: proc(checker: ^Checker, spec_id: int) {
|
||||
problematic = problematic || checker.module.exprs[value].kind == .Invalid
|
||||
case .Assignment:
|
||||
if statement.name == "_" {
|
||||
value := build_expr(checker, statement.expr, locals[:], &global_reads, &calls)
|
||||
value := build_expr(checker, statement.expr, locals[:], &global_reads, &calls, types.INVALID, function.pkg, function.file)
|
||||
if checker.module.exprs[value].type.kind == .Void {
|
||||
id := source.add(
|
||||
checker.diagnostics,
|
||||
@@ -1013,6 +1116,8 @@ build_function :: proc(checker: ^Checker, spec_id: int) {
|
||||
&global_reads,
|
||||
&calls,
|
||||
local.type,
|
||||
function.pkg,
|
||||
function.file,
|
||||
)
|
||||
value = coerce_expr(checker, value, local.type, statement.span)
|
||||
append(&body, len(checker.module.statements))
|
||||
@@ -1090,6 +1195,8 @@ build_function :: proc(checker: ^Checker, spec_id: int) {
|
||||
&global_reads,
|
||||
&calls,
|
||||
spec.result,
|
||||
function.pkg,
|
||||
function.file,
|
||||
)
|
||||
value = coerce_expr(checker, value, spec.result, statement.span)
|
||||
append(&body, len(checker.module.statements))
|
||||
@@ -1105,7 +1212,7 @@ build_function :: proc(checker: ^Checker, spec_id: int) {
|
||||
)
|
||||
problematic = problematic || checker.module.exprs[value].kind == .Invalid
|
||||
case .Expression:
|
||||
value := build_expr(checker, statement.expr, locals[:], &global_reads, &calls)
|
||||
value := build_expr(checker, statement.expr, locals[:], &global_reads, &calls, types.INVALID, function.pkg, function.file)
|
||||
if checker.module.exprs[value].type.kind != .Void {
|
||||
id := source.add(
|
||||
checker.diagnostics,
|
||||
@@ -1173,8 +1280,8 @@ build_function :: proc(checker: ^Checker, spec_id: int) {
|
||||
hir.Function {
|
||||
name = function.name,
|
||||
link_name = make_link_name(checker, spec_id),
|
||||
c_abi = function.c_abi || function.name == "main",
|
||||
is_main = function.name == "main",
|
||||
c_abi = function.c_abi || (function.pkg == 0 && function.name == "main"),
|
||||
is_main = function.pkg == 0 && function.name == "main",
|
||||
params = params[:],
|
||||
result = spec.result,
|
||||
locals = hir_locals[:],
|
||||
@@ -1221,7 +1328,7 @@ build_globals :: proc(checker: ^Checker) {
|
||||
if declared.kind == .Concrete {
|
||||
expected = declared
|
||||
}
|
||||
expr := build_expr(checker, global.expr, nil, &dependencies, &calls, expected)
|
||||
expr := build_expr(checker, global.expr, nil, &dependencies, &calls, expected, global.pkg, global.file)
|
||||
global_type := checker.global_types[global_id]
|
||||
if declared.kind == .Concrete {
|
||||
expr = coerce_expr(checker, expr, declared, global.span)
|
||||
@@ -1229,10 +1336,17 @@ build_globals :: proc(checker: ^Checker) {
|
||||
} else if types.is_concrete_integer(checker.module.exprs[expr].type) {
|
||||
global_type = checker.module.exprs[expr].type
|
||||
}
|
||||
if !types.is_concrete_integer(global_type) {
|
||||
global_type = types.I64
|
||||
}
|
||||
diagnostic := -1
|
||||
if !types.is_concrete_integer(global_type) {
|
||||
diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
global.span,
|
||||
"could not resolve a concrete type for global '%s'",
|
||||
global.name,
|
||||
)
|
||||
global_type = types.I64
|
||||
expr = invalid_hir_expr(checker, global.span, diagnostic, global_type)
|
||||
}
|
||||
if global.type == .Void {
|
||||
diagnostic = source.add(
|
||||
checker.diagnostics,
|
||||
@@ -1389,16 +1503,14 @@ detect_global_cycles_visit :: proc(checker: ^Checker, global_id: int, states: []
|
||||
return
|
||||
}
|
||||
if states[global_id] == 1 {
|
||||
if !checker.module.globals[global_id].problematic {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
checker.ast_module.globals[global_id].span,
|
||||
"global initialization cycle involving '%s'",
|
||||
checker.module.globals[global_id].name,
|
||||
)
|
||||
checker.module.globals[global_id].diagnostic = id
|
||||
checker.module.globals[global_id].problematic = true
|
||||
}
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
checker.ast_module.globals[global_id].span,
|
||||
"global initialization cycle involving '%s'",
|
||||
checker.module.globals[global_id].name,
|
||||
)
|
||||
checker.module.globals[global_id].diagnostic = id
|
||||
checker.module.globals[global_id].problematic = true
|
||||
return
|
||||
}
|
||||
states[global_id] = 1
|
||||
@@ -1478,6 +1590,7 @@ check :: proc(
|
||||
allocator = allocator,
|
||||
}
|
||||
checker.specs.allocator = allocator
|
||||
build_symbol_indexes(&checker)
|
||||
checker.global_types = make([]types.Type, len(ast_module.globals), allocator)
|
||||
checker.constants = make([]Constant, len(ast_module.exprs), allocator)
|
||||
defer {
|
||||
@@ -1485,26 +1598,34 @@ check :: proc(
|
||||
delete(spec.args, allocator)
|
||||
}
|
||||
delete(checker.specs)
|
||||
delete(checker.function_index, allocator)
|
||||
delete(checker.global_index, allocator)
|
||||
delete(checker.import_index, allocator)
|
||||
delete(checker.global_types, allocator)
|
||||
delete(checker.constants, allocator)
|
||||
}
|
||||
|
||||
for function, index in ast_module.functions {
|
||||
for previous in ast_module.functions[:index] {
|
||||
if previous.name == function.name {
|
||||
if previous.pkg == function.pkg && previous.name == function.name {
|
||||
source.addf(diagnostics, function.span, "duplicate function '%s'", function.name)
|
||||
}
|
||||
}
|
||||
for global in ast_module.globals {
|
||||
if global.pkg == function.pkg && global.name == function.name {
|
||||
source.addf(diagnostics, function.span, "package declaration '%s' conflicts with a global", function.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
for global, index in ast_module.globals {
|
||||
for previous in ast_module.globals[:index] {
|
||||
if previous.name == global.name {
|
||||
if previous.pkg == global.pkg && previous.name == global.name {
|
||||
source.addf(diagnostics, global.span, "duplicate global '%s'", global.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validate_templates(&checker)
|
||||
validate_declarations(&checker)
|
||||
infer_all(&checker)
|
||||
build_globals(&checker)
|
||||
for spec_id := 0; spec_id < len(checker.specs); spec_id += 1 {
|
||||
@@ -1513,10 +1634,10 @@ check :: proc(
|
||||
resolve_call_targets(&checker)
|
||||
propagate_global_reads(&checker)
|
||||
|
||||
main_template := find_template(&checker, "main")
|
||||
main_template := find_template(&checker, "main", 0)
|
||||
main_declarations := 0
|
||||
for function in ast_module.functions {
|
||||
if function.name == "main" {
|
||||
if function.pkg == 0 && function.name == "main" {
|
||||
main_declarations += 1
|
||||
}
|
||||
}
|
||||
@@ -1543,5 +1664,10 @@ check :: proc(
|
||||
}
|
||||
delete(states, allocator)
|
||||
propagate_problems(&checker)
|
||||
for import_item in ast_module.imports {
|
||||
if import_item.valid && !import_item.used {
|
||||
source.addf(diagnostics, import_item.span, "unused import '%s'", import_item.alias)
|
||||
}
|
||||
}
|
||||
return checker.module
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user