|
|
|
@@ -33,8 +33,11 @@ Spec :: struct {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Infer_Local :: struct {
|
|
|
|
|
name: symbol.Id,
|
|
|
|
|
type: types.Type,
|
|
|
|
|
name: symbol.Id,
|
|
|
|
|
type: types.Type,
|
|
|
|
|
declared: types.Type,
|
|
|
|
|
statement: ast.Stmt_Id,
|
|
|
|
|
mutable: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Build_Local :: struct {
|
|
|
|
@@ -53,6 +56,7 @@ Build_Ctx :: struct {
|
|
|
|
|
pkg: ast.Package_Id,
|
|
|
|
|
file: ast.File_Id,
|
|
|
|
|
result: types.Type,
|
|
|
|
|
local_types: []types.Type,
|
|
|
|
|
locals: ^[dynamic]Build_Local,
|
|
|
|
|
hir_locals: ^[dynamic]hir.Local,
|
|
|
|
|
global_reads: ^[dynamic]hir.Global_Id,
|
|
|
|
@@ -262,6 +266,13 @@ is_runtime_type :: proc(checker: ^Checker, value: types.Type) -> bool {
|
|
|
|
|
return types.is_runtime_value(value, &checker.module.types)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
is_undefined_expr :: proc(checker: ^Checker, expr_id: ast.Expr_Id) -> bool {
|
|
|
|
|
if expr_id == ast.INVALID_EXPR || int(expr_id) >= len(checker.ast_module.exprs) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return checker.ast_module.exprs[expr_id].kind == .Undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string_literal_type :: proc(checker: ^Checker, string_id: u64) -> types.Type {
|
|
|
|
|
length: u64
|
|
|
|
|
if string_id < u64(len(checker.ast_module.strings)) {
|
|
|
|
@@ -636,7 +647,7 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
|
|
|
|
|
append(&stack, expr.left)
|
|
|
|
|
case .Add, .Sub, .Mul, .Div, .Index, .Orelse, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
|
|
|
|
|
append(&stack, expr.left, expr.right)
|
|
|
|
|
case .Invalid, .Integer, .Float, .String, .Bool, .None, .Name, .Enum_Literal:
|
|
|
|
|
case .Invalid, .Integer, .Float, .String, .Bool, .None, .Undefined, .Name, .Enum_Literal:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -956,14 +967,21 @@ validate_type_nodes :: proc(checker: ^Checker) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
find_infer_local :: proc(locals: []Infer_Local, name: symbol.Id) -> types.Type {
|
|
|
|
|
for index := len(locals) - 1; index >= 0; index -= 1 {
|
|
|
|
|
if locals[index].name == name {
|
|
|
|
|
return locals[index].type
|
|
|
|
|
}
|
|
|
|
|
if index, ok := find_infer_local_index(locals, name); ok {
|
|
|
|
|
return locals[index].type
|
|
|
|
|
}
|
|
|
|
|
return types.INVALID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
find_infer_local_index :: proc(locals: []Infer_Local, name: symbol.Id) -> (int, bool) {
|
|
|
|
|
for index := len(locals) - 1; index >= 0; index -= 1 {
|
|
|
|
|
if locals[index].name == name {
|
|
|
|
|
return index, true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1, false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
find_spec :: proc(checker: ^Checker, template: ast.Function_Id, actual_args: []types.Type) -> Spec_Id {
|
|
|
|
|
function := checker.ast_module.functions[template]
|
|
|
|
|
for spec, index in checker.specs {
|
|
|
|
@@ -1123,6 +1141,8 @@ infer_compound_expr :: proc(
|
|
|
|
|
return types.array(store, element, u64(len(expr.args)), false)
|
|
|
|
|
case .None:
|
|
|
|
|
return types.INVALID
|
|
|
|
|
case .Undefined:
|
|
|
|
|
return types.INVALID
|
|
|
|
|
case .Enum_Literal:
|
|
|
|
|
return types.INVALID
|
|
|
|
|
case .Address:
|
|
|
|
@@ -1247,7 +1267,7 @@ infer_expr :: proc(
|
|
|
|
|
case .Float:
|
|
|
|
|
last = types.F64
|
|
|
|
|
_ = pop(&stack)
|
|
|
|
|
case .String, .Array, .None, .Address, .Deref, .Index, .Slice,
|
|
|
|
|
case .String, .Array, .None, .Undefined, .Address, .Deref, .Index, .Slice,
|
|
|
|
|
.Field, .Unwrap, .Orelse, .Struct_Literal, .Keyed, .Enum_Literal,
|
|
|
|
|
.Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
|
|
|
|
|
last = infer_compound_expr(checker, expr, locals, pkg, file, demanded)
|
|
|
|
@@ -1505,34 +1525,127 @@ flatten_conditional_unwrap_operands :: proc(
|
|
|
|
|
append(operands, expr_id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
record_infer_local_type :: proc(local: Infer_Local, local_types: []types.Type) {
|
|
|
|
|
if local.statement != ast.INVALID_STMT && int(local.statement) < len(local_types) {
|
|
|
|
|
local_types[local.statement] = local.type
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
merge_infer_local_type :: proc(
|
|
|
|
|
checker: ^Checker,
|
|
|
|
|
local: ^Infer_Local,
|
|
|
|
|
inferred: types.Type,
|
|
|
|
|
local_types: []types.Type,
|
|
|
|
|
) -> bool {
|
|
|
|
|
if !is_runtime_type(checker, inferred) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if types.is_constraint(local.declared) {
|
|
|
|
|
if !types.is_concrete_integer(inferred) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if !is_runtime_type(checker, local.type) {
|
|
|
|
|
local.type = inferred
|
|
|
|
|
record_infer_local_type(local^, local_types)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if types.equal(local.type, inferred) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
merged := types.widest(local.type, inferred)
|
|
|
|
|
if types.is_concrete_integer(merged) {
|
|
|
|
|
local.type = merged
|
|
|
|
|
record_infer_local_type(local^, local_types)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if is_runtime_type(checker, local.declared) {
|
|
|
|
|
local.type = local.declared
|
|
|
|
|
record_infer_local_type(local^, local_types)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if !is_runtime_type(checker, local.type) {
|
|
|
|
|
local.type = inferred
|
|
|
|
|
record_infer_local_type(local^, local_types)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if types.equal(local.type, inferred) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
merged := types.widest(local.type, inferred)
|
|
|
|
|
if types.is_concrete_scalar(merged) {
|
|
|
|
|
local.type = merged
|
|
|
|
|
record_infer_local_type(local^, local_types)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
infer_statements :: proc(
|
|
|
|
|
checker: ^Checker,
|
|
|
|
|
statements: []ast.Stmt_Id,
|
|
|
|
|
locals: ^[dynamic]Infer_Local,
|
|
|
|
|
local_types: []types.Type,
|
|
|
|
|
pkg: ast.Package_Id,
|
|
|
|
|
file: ast.File_Id,
|
|
|
|
|
demanded: ^[dynamic]Spec_Id,
|
|
|
|
|
result: ^types.Type,
|
|
|
|
|
result_hint := types.INVALID,
|
|
|
|
|
) {
|
|
|
|
|
scope_start := len(locals^)
|
|
|
|
|
for statement_id in statements {
|
|
|
|
|
statement := checker.ast_module.statements[statement_id]
|
|
|
|
|
#partial switch statement.kind {
|
|
|
|
|
case .Declaration:
|
|
|
|
|
value_type := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded)
|
|
|
|
|
declared_local := type_from_syntax(statement.type)
|
|
|
|
|
declared_local := resolve_inferred_array(checker, type_from_syntax(statement.type), statement.expr)
|
|
|
|
|
value_type := types.INVALID
|
|
|
|
|
if !is_undefined_expr(checker, statement.expr) {
|
|
|
|
|
value_type = infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded)
|
|
|
|
|
}
|
|
|
|
|
if is_runtime_type(checker, declared_local) {
|
|
|
|
|
value_type = declared_local
|
|
|
|
|
}
|
|
|
|
|
append(locals, Infer_Local{name = statement.name, type = value_type})
|
|
|
|
|
case .Assignment, .Expression:
|
|
|
|
|
local := Infer_Local{
|
|
|
|
|
name=statement.name,
|
|
|
|
|
type=value_type,
|
|
|
|
|
declared=declared_local,
|
|
|
|
|
statement=statement_id,
|
|
|
|
|
mutable=!statement.immutable,
|
|
|
|
|
}
|
|
|
|
|
append(locals, local)
|
|
|
|
|
record_infer_local_type(local, local_types)
|
|
|
|
|
case .Assignment:
|
|
|
|
|
value_type := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded)
|
|
|
|
|
if statement.target != ast.INVALID_EXPR {
|
|
|
|
|
_ = infer_expr(checker, statement.target, locals^[:], pkg, file, demanded)
|
|
|
|
|
target_expr := checker.ast_module.exprs[statement.target]
|
|
|
|
|
if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) {
|
|
|
|
|
if local_index, ok := find_infer_local_index(locals^[:], target_expr.name); ok &&
|
|
|
|
|
locals^[local_index].mutable {
|
|
|
|
|
_ = merge_infer_local_type(checker, &locals^[local_index], value_type, local_types)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if statement.name != checker.sink_symbol {
|
|
|
|
|
if local_index, ok := find_infer_local_index(locals^[:], statement.name); ok &&
|
|
|
|
|
locals^[local_index].mutable {
|
|
|
|
|
_ = merge_infer_local_type(checker, &locals^[local_index], value_type, local_types)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case .Expression:
|
|
|
|
|
_ = infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded)
|
|
|
|
|
case .Return:
|
|
|
|
|
if statement.expr != ast.INVALID_EXPR {
|
|
|
|
|
returned := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded)
|
|
|
|
|
if is_runtime_type(checker, result_hint) {
|
|
|
|
|
expr := checker.ast_module.exprs[statement.expr]
|
|
|
|
|
if expr.kind == .Name && !symbol.is_valid(expr.qualifier) {
|
|
|
|
|
if local_index, ok := find_infer_local_index(locals^[:], expr.name); ok {
|
|
|
|
|
_ = merge_infer_local_type(checker, &locals^[local_index], result_hint, local_types)
|
|
|
|
|
returned = result_hint
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !types.is_valid(result^) {
|
|
|
|
|
result^ = returned
|
|
|
|
|
} else if !types.equal(result^, returned) {
|
|
|
|
@@ -1558,27 +1671,27 @@ infer_statements :: proc(
|
|
|
|
|
types.is_optional(operand_types[index], &checker.module.types) {
|
|
|
|
|
capture_type = types.child_type(operand_types[index], &checker.module.types)
|
|
|
|
|
}
|
|
|
|
|
append(locals, Infer_Local{name=capture, type=capture_type})
|
|
|
|
|
append(locals, Infer_Local{name=capture, type=capture_type, declared=capture_type, statement=ast.INVALID_STMT})
|
|
|
|
|
}
|
|
|
|
|
if statement.guard != ast.INVALID_EXPR {
|
|
|
|
|
_ = infer_expr(checker, statement.guard, locals^[:], pkg, file, demanded)
|
|
|
|
|
}
|
|
|
|
|
infer_statements(checker, statement.body, locals, pkg, file, demanded, result)
|
|
|
|
|
infer_statements(checker, statement.body, locals, local_types, pkg, file, demanded, result, result_hint)
|
|
|
|
|
resize(locals, capture_start)
|
|
|
|
|
infer_statements(checker, statement.else_body, locals, pkg, file, demanded, result)
|
|
|
|
|
infer_statements(checker, statement.else_body, locals, local_types, pkg, file, demanded, result, result_hint)
|
|
|
|
|
delete(operand_types, checker.allocator)
|
|
|
|
|
delete(operands)
|
|
|
|
|
} else {
|
|
|
|
|
_ = infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded)
|
|
|
|
|
infer_statements(checker, statement.body, locals, pkg, file, demanded, result)
|
|
|
|
|
infer_statements(checker, statement.else_body, locals, pkg, file, demanded, result)
|
|
|
|
|
infer_statements(checker, statement.body, locals, local_types, pkg, file, demanded, result, result_hint)
|
|
|
|
|
infer_statements(checker, statement.else_body, locals, local_types, pkg, file, demanded, result, result_hint)
|
|
|
|
|
}
|
|
|
|
|
case .While:
|
|
|
|
|
_ = infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded)
|
|
|
|
|
infer_statements(checker, statement.body, locals, pkg, file, demanded, result)
|
|
|
|
|
infer_statements(checker, statement.body, locals, local_types, pkg, file, demanded, result, result_hint)
|
|
|
|
|
if statement.update != ast.INVALID_STMT {
|
|
|
|
|
update := [1]ast.Stmt_Id{statement.update}
|
|
|
|
|
infer_statements(checker, update[:], locals, pkg, file, demanded, result)
|
|
|
|
|
infer_statements(checker, update[:], locals, local_types, pkg, file, demanded, result, result_hint)
|
|
|
|
|
}
|
|
|
|
|
case .For:
|
|
|
|
|
iterable_type := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded)
|
|
|
|
@@ -1596,43 +1709,55 @@ infer_statements :: proc(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if symbol.is_valid(statement.name) {
|
|
|
|
|
append(locals, Infer_Local{name=statement.name, type=capture_type})
|
|
|
|
|
append(locals, Infer_Local{name=statement.name, type=capture_type, declared=capture_type, statement=ast.INVALID_STMT})
|
|
|
|
|
}
|
|
|
|
|
if symbol.is_valid(statement.index_name) {
|
|
|
|
|
append(locals, Infer_Local{name=statement.index_name, type=types.USIZE})
|
|
|
|
|
append(locals, Infer_Local{name=statement.index_name, type=types.USIZE, declared=types.USIZE, statement=ast.INVALID_STMT})
|
|
|
|
|
}
|
|
|
|
|
infer_statements(checker, statement.body, locals, pkg, file, demanded, result)
|
|
|
|
|
infer_statements(checker, statement.body, locals, local_types, pkg, file, demanded, result, result_hint)
|
|
|
|
|
resize(locals, capture_start)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
resize(locals, scope_start)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
infer_spec_result :: proc(checker: ^Checker, id: Spec_Id, demanded: ^[dynamic]Spec_Id = nil) -> types.Type {
|
|
|
|
|
infer_spec_locals_and_result :: proc(
|
|
|
|
|
checker: ^Checker,
|
|
|
|
|
id: Spec_Id,
|
|
|
|
|
demanded: ^[dynamic]Spec_Id = nil,
|
|
|
|
|
) -> ([]types.Type, types.Type) {
|
|
|
|
|
spec := checker.specs[id]
|
|
|
|
|
function := checker.ast_module.functions[spec.template]
|
|
|
|
|
declared := type_from_syntax(function.result)
|
|
|
|
|
if function.pkg == 0 && function.name == checker.main_symbol && function.result == types.INT {
|
|
|
|
|
declared = types.I32
|
|
|
|
|
}
|
|
|
|
|
result_hint := declared if is_runtime_type(checker, declared) else types.INVALID
|
|
|
|
|
|
|
|
|
|
locals: [dynamic]Infer_Local
|
|
|
|
|
locals.allocator = checker.allocator
|
|
|
|
|
defer delete(locals)
|
|
|
|
|
local_types := make([]types.Type, len(checker.ast_module.statements), checker.allocator)
|
|
|
|
|
for param, index in function.params {
|
|
|
|
|
param_type := types.INVALID
|
|
|
|
|
if index < len(spec.args) {
|
|
|
|
|
param_type = spec.args[index]
|
|
|
|
|
}
|
|
|
|
|
append(&locals, Infer_Local{name = param.name, type = param_type})
|
|
|
|
|
append(&locals, Infer_Local{name=param.name, type=param_type, declared=param_type, statement=ast.INVALID_STMT})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := types.INVALID
|
|
|
|
|
infer_statements(checker, function.body, &locals, function.pkg, function.file, demanded, &result)
|
|
|
|
|
infer_statements(checker, function.body, &locals, local_types, function.pkg, function.file, demanded, &result, result_hint)
|
|
|
|
|
if types.is_constraint(declared) {
|
|
|
|
|
return result
|
|
|
|
|
return local_types, result
|
|
|
|
|
}
|
|
|
|
|
return declared
|
|
|
|
|
return local_types, declared
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
infer_spec_result :: proc(checker: ^Checker, id: Spec_Id, demanded: ^[dynamic]Spec_Id = nil) -> types.Type {
|
|
|
|
|
local_types, result := infer_spec_locals_and_result(checker, id, demanded)
|
|
|
|
|
delete(local_types, checker.allocator)
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
merge_inferred_type :: proc(store: ^types.Store, current: ^types.Type, inferred: types.Type) -> bool {
|
|
|
|
@@ -2296,7 +2421,7 @@ build_compound_expr :: proc(
|
|
|
|
|
infer_locals := make([]Infer_Local, len(locals), checker.allocator)
|
|
|
|
|
defer delete(infer_locals, checker.allocator)
|
|
|
|
|
for local, index in locals {
|
|
|
|
|
infer_locals[index] = Infer_Local{name=local.name, type=local.type}
|
|
|
|
|
infer_locals[index] = Infer_Local{name=local.name, type=local.type, declared=local.type, statement=ast.INVALID_STMT}
|
|
|
|
|
}
|
|
|
|
|
for arg in expr.args {
|
|
|
|
|
actual := infer_nested_expr(checker, arg, infer_locals, pkg, file, nil)
|
|
|
|
@@ -2332,6 +2457,13 @@ build_compound_expr :: proc(
|
|
|
|
|
kind=.None, span=expr.span, type=expected, target=hir.INVALID_REF,
|
|
|
|
|
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
|
|
|
|
})
|
|
|
|
|
case .Undefined:
|
|
|
|
|
id := source.add(
|
|
|
|
|
checker.diagnostics,
|
|
|
|
|
expr.span,
|
|
|
|
|
"'undefined' is only valid as a mutable local declaration initializer",
|
|
|
|
|
)
|
|
|
|
|
return invalid_hir_expr(checker, expr.span, id, expected)
|
|
|
|
|
case .Enum_Literal:
|
|
|
|
|
if !types.is_enum(expected, store) {
|
|
|
|
|
id := source.addf(
|
|
|
|
@@ -2762,7 +2894,7 @@ build_expr :: proc(
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
switch expr.kind {
|
|
|
|
|
case .String, .Array, .None, .Address, .Deref, .Index, .Slice,
|
|
|
|
|
case .String, .Array, .None, .Undefined, .Address, .Deref, .Index, .Slice,
|
|
|
|
|
.Field, .Unwrap, .Orelse, .Struct_Literal, .Keyed,
|
|
|
|
|
.Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range,
|
|
|
|
|
.Enum_Literal:
|
|
|
|
@@ -3290,22 +3422,62 @@ build_block :: proc(
|
|
|
|
|
switch statement.kind {
|
|
|
|
|
case .Declaration:
|
|
|
|
|
declared := resolve_inferred_array(checker, type_from_syntax(statement.type), statement.expr)
|
|
|
|
|
expected := types.INVALID
|
|
|
|
|
if is_runtime_type(checker, declared) {
|
|
|
|
|
expected = declared
|
|
|
|
|
if statement_id != ast.INVALID_STMT && int(statement_id) < len(ctx.local_types) &&
|
|
|
|
|
is_runtime_type(checker, ctx.local_types[statement_id]) &&
|
|
|
|
|
(types.is_constraint(declared) || is_undefined_expr(checker, statement.expr)) {
|
|
|
|
|
declared = ctx.local_types[statement_id]
|
|
|
|
|
}
|
|
|
|
|
value := build_expr(
|
|
|
|
|
checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
|
|
|
|
expected, ctx.pkg, ctx.file,
|
|
|
|
|
)
|
|
|
|
|
value_type := checker.module.exprs[value].type
|
|
|
|
|
if is_runtime_type(checker, declared) {
|
|
|
|
|
value = coerce_expr(checker, value, declared, statement.span)
|
|
|
|
|
expected := types.INVALID
|
|
|
|
|
value := hir.INVALID_EXPR
|
|
|
|
|
value_type := types.INVALID
|
|
|
|
|
if is_undefined_expr(checker, statement.expr) {
|
|
|
|
|
if statement.immutable {
|
|
|
|
|
id := source.add(
|
|
|
|
|
checker.diagnostics,
|
|
|
|
|
statement.span,
|
|
|
|
|
"'undefined' requires a mutable local declaration",
|
|
|
|
|
)
|
|
|
|
|
append(&body, hir.stmt_id(len(checker.module.statements)))
|
|
|
|
|
append(&checker.module.statements, hir.Stmt{
|
|
|
|
|
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
|
|
|
|
local = hir.INVALID_LOCAL, diagnostic = id,
|
|
|
|
|
})
|
|
|
|
|
ctx.problematic^ = true
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if !is_runtime_type(checker, declared) {
|
|
|
|
|
id := source.addf(
|
|
|
|
|
checker.diagnostics,
|
|
|
|
|
statement.span,
|
|
|
|
|
"could not infer a concrete type for local '%s'",
|
|
|
|
|
symbol_text(checker, statement.name),
|
|
|
|
|
)
|
|
|
|
|
append(&body, hir.stmt_id(len(checker.module.statements)))
|
|
|
|
|
append(&checker.module.statements, hir.Stmt{
|
|
|
|
|
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
|
|
|
|
local = hir.INVALID_LOCAL, diagnostic = id,
|
|
|
|
|
})
|
|
|
|
|
ctx.problematic^ = true
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
value_type = declared
|
|
|
|
|
} else {
|
|
|
|
|
if is_runtime_type(checker, declared) {
|
|
|
|
|
expected = declared
|
|
|
|
|
}
|
|
|
|
|
value = build_expr(
|
|
|
|
|
checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
|
|
|
|
expected, ctx.pkg, ctx.file,
|
|
|
|
|
)
|
|
|
|
|
value_type = checker.module.exprs[value].type
|
|
|
|
|
} else if types.is_void(declared) {
|
|
|
|
|
id := source.add(checker.diagnostics, statement.span, "locals cannot have type void")
|
|
|
|
|
value = invalid_hir_expr(checker, statement.span, id)
|
|
|
|
|
value_type = types.INVALID
|
|
|
|
|
if is_runtime_type(checker, declared) {
|
|
|
|
|
value = coerce_expr(checker, value, declared, statement.span)
|
|
|
|
|
value_type = checker.module.exprs[value].type
|
|
|
|
|
} else if types.is_void(declared) {
|
|
|
|
|
id := source.add(checker.diagnostics, statement.span, "locals cannot have type void")
|
|
|
|
|
value = invalid_hir_expr(checker, statement.span, id)
|
|
|
|
|
value_type = types.INVALID
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if _, found := find_build_local(ctx.locals^[duplicate_start:], statement.name); found {
|
|
|
|
|
id := source.addf(
|
|
|
|
@@ -3332,7 +3504,9 @@ build_block :: proc(
|
|
|
|
|
kind = .Declaration, span = statement.span, local = local_id, expr = value,
|
|
|
|
|
diagnostic = source.INVALID_DIAGNOSTIC,
|
|
|
|
|
})
|
|
|
|
|
ctx.problematic^ = ctx.problematic^ || checker.module.exprs[value].kind == .Invalid
|
|
|
|
|
if value != hir.INVALID_EXPR {
|
|
|
|
|
ctx.problematic^ = ctx.problematic^ || checker.module.exprs[value].kind == .Invalid
|
|
|
|
|
}
|
|
|
|
|
case .Assignment:
|
|
|
|
|
if statement.target != ast.INVALID_EXPR {
|
|
|
|
|
target_expr := build_expr(
|
|
|
|
@@ -3888,6 +4062,13 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
|
|
|
|
|
global_reads.allocator = checker.allocator
|
|
|
|
|
calls: [dynamic]hir.Function_Id
|
|
|
|
|
calls.allocator = checker.allocator
|
|
|
|
|
demanded: [dynamic]Spec_Id
|
|
|
|
|
demanded.allocator = checker.allocator
|
|
|
|
|
local_types, _ := infer_spec_locals_and_result(checker, id, &demanded)
|
|
|
|
|
defer {
|
|
|
|
|
delete(local_types, checker.allocator)
|
|
|
|
|
delete(demanded)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for param, index in function.params {
|
|
|
|
|
local_id := hir.local_id(len(hir_locals))
|
|
|
|
@@ -3946,6 +4127,7 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
|
|
|
|
|
pkg = function.pkg,
|
|
|
|
|
file = function.file,
|
|
|
|
|
result = spec.result,
|
|
|
|
|
local_types = local_types,
|
|
|
|
|
locals = &locals,
|
|
|
|
|
hir_locals = &hir_locals,
|
|
|
|
|
global_reads = &global_reads,
|
|
|
|
|