fix interop and indexing oversights
This commit is contained in:
+290
-58
@@ -296,6 +296,73 @@ eval_constant :: proc(checker: ^Checker, expr_id: ast.Expr_Id) -> Constant {
|
||||
return checker.constants[expr_id]
|
||||
}
|
||||
|
||||
eval_integer_constant_in_context :: proc(
|
||||
checker: ^Checker,
|
||||
expr_id: ast.Expr_Id,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
depth := 0,
|
||||
) -> Constant {
|
||||
if depth > 64 || expr_id == ast.INVALID_EXPR || int(expr_id) >= len(checker.ast_module.exprs) {
|
||||
return Constant{kind = .Not_Constant}
|
||||
}
|
||||
expr := checker.ast_module.exprs[expr_id]
|
||||
#partial switch expr.kind {
|
||||
case .Integer:
|
||||
return Constant{kind = .Value, value = i128(expr.integer)}
|
||||
case .Name:
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, false)
|
||||
if !available {
|
||||
return Constant{kind = .Not_Constant}
|
||||
}
|
||||
global := find_global(checker, expr.name, target_pkg)
|
||||
if global == ast.INVALID_GLOBAL || int(global) >= len(checker.ast_module.globals) {
|
||||
return Constant{kind = .Not_Constant}
|
||||
}
|
||||
g := checker.ast_module.globals[global]
|
||||
if g.external || !g.immutable {
|
||||
return Constant{kind = .Not_Constant}
|
||||
}
|
||||
return eval_integer_constant_in_context(checker, g.expr, g.pkg, g.file, depth+1)
|
||||
case .Negate:
|
||||
operand := eval_integer_constant_in_context(checker, expr.left, pkg, file, depth+1)
|
||||
if operand.kind == .Value {
|
||||
value, overflow := intrinsics.overflow_sub(i128(0), operand.value)
|
||||
return Constant{kind = .Overflow} if overflow else Constant{kind = .Value, value = value}
|
||||
}
|
||||
return operand
|
||||
case .Add, .Sub, .Mul, .Div:
|
||||
left := eval_integer_constant_in_context(checker, expr.left, pkg, file, depth+1)
|
||||
right := eval_integer_constant_in_context(checker, expr.right, pkg, file, depth+1)
|
||||
if left.kind == .Div_By_Zero || right.kind == .Div_By_Zero {
|
||||
return Constant{kind = .Div_By_Zero}
|
||||
}
|
||||
if left.kind == .Overflow || right.kind == .Overflow {
|
||||
return Constant{kind = .Overflow}
|
||||
}
|
||||
if left.kind != .Value || right.kind != .Value {
|
||||
return Constant{kind = .Not_Constant}
|
||||
}
|
||||
value: i128
|
||||
overflow: bool
|
||||
#partial switch expr.kind {
|
||||
case .Sub:
|
||||
value, overflow = intrinsics.overflow_sub(left.value, right.value)
|
||||
case .Mul:
|
||||
value, overflow = intrinsics.overflow_mul(left.value, right.value)
|
||||
case .Div:
|
||||
if right.value == 0 {
|
||||
return Constant{kind = .Div_By_Zero}
|
||||
}
|
||||
value = left.value / right.value
|
||||
case:
|
||||
value, overflow = intrinsics.overflow_add(left.value, right.value)
|
||||
}
|
||||
return Constant{kind = .Overflow} if overflow else Constant{kind = .Value, value = value}
|
||||
}
|
||||
return Constant{kind = .Not_Constant}
|
||||
}
|
||||
|
||||
fits_signed_type :: proc(value: i128, value_type: types.Type, selected := target.DEFAULT) -> bool {
|
||||
if !types.is_signed(value_type, selected) {
|
||||
return false
|
||||
@@ -320,14 +387,83 @@ fits_i64 :: proc(value: i128) -> bool {
|
||||
return fits_signed_type(value, types.I64)
|
||||
}
|
||||
|
||||
type_from_syntax :: proc(value: ast.Type_Syntax) -> types.Type {
|
||||
type_from_syntax :: proc(
|
||||
checker: ^Checker,
|
||||
value: ast.Type_Syntax,
|
||||
pkg := ast.Package_Id(0),
|
||||
file := ast.File_Id(0),
|
||||
depth := 0,
|
||||
) -> types.Type {
|
||||
if depth > 64 {
|
||||
return types.INVALID
|
||||
}
|
||||
item, ok := types.node(&checker.module.types, value)
|
||||
if !ok {
|
||||
return value
|
||||
}
|
||||
store := &checker.module.types
|
||||
changed := false
|
||||
#partial switch item.kind {
|
||||
case .Array:
|
||||
child := type_from_syntax(checker, item.child, pkg, file, depth+1)
|
||||
changed = changed || child != item.child
|
||||
item.child = child
|
||||
if item.unresolved_count {
|
||||
expr_id := ast.Expr_Id(item.count_expr)
|
||||
span := source.Span{}
|
||||
if expr_id != ast.INVALID_EXPR && int(expr_id) < len(checker.ast_module.exprs) {
|
||||
span = checker.ast_module.exprs[expr_id].span
|
||||
}
|
||||
constant := eval_integer_constant_in_context(checker, expr_id, pkg, file)
|
||||
if constant.kind == .Value {
|
||||
switch {
|
||||
case constant.value < 0:
|
||||
source.add(checker.diagnostics, span, "array count must be non-negative")
|
||||
return types.INVALID
|
||||
case constant.value > i128(0xffff_ffff_ffff_ffff):
|
||||
source.add(checker.diagnostics, span, "array count does not fit in u64")
|
||||
return types.INVALID
|
||||
case:
|
||||
item.count = u64(constant.value)
|
||||
item.unresolved_count = false
|
||||
item.count_expr = 0
|
||||
changed = true
|
||||
}
|
||||
} else {
|
||||
source.add(checker.diagnostics, span, "array count must be a compile-time integer expression")
|
||||
return types.INVALID
|
||||
}
|
||||
}
|
||||
case .Pointer, .Slice, .Optional, .Range, .Distinct, .Enum, .Fallible:
|
||||
child := type_from_syntax(checker, item.child, pkg, file, depth+1)
|
||||
extra := type_from_syntax(checker, item.extra, pkg, file, depth+1)
|
||||
changed = child != item.child || extra != item.extra
|
||||
item.child = child
|
||||
item.extra = extra
|
||||
case .Function:
|
||||
params := types.params_for(store, value)
|
||||
resolved_params := make([]types.Type, len(params), checker.allocator)
|
||||
defer delete(resolved_params, checker.allocator)
|
||||
params_changed := false
|
||||
for param, index in params {
|
||||
resolved_params[index] = type_from_syntax(checker, param.type, pkg, file, depth+1)
|
||||
params_changed = params_changed || resolved_params[index] != param.type
|
||||
}
|
||||
result := type_from_syntax(checker, item.child, pkg, file, depth+1)
|
||||
if params_changed || result != item.child {
|
||||
return types.function(store, resolved_params, result, item.c_abi, item.variadic)
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
return types.intern(store, item)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function_channel_type :: proc(checker: ^Checker, function: ast.Function) -> types.Type {
|
||||
result := type_from_syntax(function.result)
|
||||
result := type_from_syntax(checker, function.result, function.pkg, function.file)
|
||||
if types.is_valid(function.error) {
|
||||
return types.fallible(&checker.module.types, result, type_from_syntax(function.error))
|
||||
return types.fallible(&checker.module.types, result, type_from_syntax(checker, function.error, function.pkg, function.file))
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -642,11 +778,11 @@ valid_call_arity :: proc(function: ast.Function, count: int) -> bool {
|
||||
return count >= len(function.params) if function.variadic else count == len(function.params)
|
||||
}
|
||||
|
||||
call_arg_expected :: proc(function: ast.Function, index: int) -> types.Type {
|
||||
call_arg_expected :: proc(checker: ^Checker, function: ast.Function, index: int) -> types.Type {
|
||||
if index < 0 || index >= len(function.params) {
|
||||
return types.INVALID
|
||||
}
|
||||
declared := type_from_syntax(function.params[index].type)
|
||||
declared := type_from_syntax(checker, function.params[index].type, function.pkg, function.file)
|
||||
// A `float` param defaults to f64 so an integer-literal argument builds as a
|
||||
// float constant (e.g. `f(3)` -> 3.0), mirroring `pi float = 3` for locals.
|
||||
// `int`/`range` constraints have no single default and keep building naturally.
|
||||
@@ -682,13 +818,13 @@ function_value_signature :: proc(
|
||||
if !function.c_abi || types.is_valid(function.error) {
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
result = type_from_syntax(function.result)
|
||||
result = type_from_syntax(checker, function.result, function.pkg, function.file)
|
||||
if !types.is_void(result) && !is_runtime_type(checker, result) {
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
params = make([]types.Type, len(function.params), checker.allocator)
|
||||
for param, index in function.params {
|
||||
param_type := type_from_syntax(param.type)
|
||||
param_type := type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
if !is_runtime_type(checker, param_type) {
|
||||
delete(params, checker.allocator)
|
||||
return nil, types.INVALID, false
|
||||
@@ -758,7 +894,7 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
append(&stack, expr.left)
|
||||
}
|
||||
case .Negate, .Not, .Address, .Deref, .Field, .Unwrap, .Try, .Keyed, .Enum_Literal:
|
||||
case .Negate, .Not, .Address, .Deref, .Field, .Unwrap, .Try, .Keyed, .Enum_Literal, .Cast:
|
||||
append(&stack, expr.left)
|
||||
case .Catch:
|
||||
append(&stack, expr.left)
|
||||
@@ -894,7 +1030,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
locals: [dynamic]symbol.Id
|
||||
locals.allocator = checker.allocator
|
||||
for param in function.params {
|
||||
if diagnostic := add_unsupported_type_diagnostic(checker, param.span, type_from_syntax(param.type));
|
||||
if diagnostic := add_unsupported_type_diagnostic(checker, param.span, type_from_syntax(checker, param.type, function.pkg, function.file));
|
||||
diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
checker.template_diagnostics[function_id] = diagnostic
|
||||
continue
|
||||
@@ -915,7 +1051,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
append(&locals, param.name)
|
||||
if types.contains_c_struct_by_value(type_from_syntax(param.type), &checker.module.types) {
|
||||
if types.contains_c_struct_by_value(type_from_syntax(checker, param.type, function.pkg, function.file), &checker.module.types) {
|
||||
checker.template_diagnostics[function_id] = source.addf(
|
||||
checker.diagnostics,
|
||||
param.span,
|
||||
@@ -924,11 +1060,11 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
}
|
||||
if diagnostic := add_unsupported_type_diagnostic(checker, function.span, type_from_syntax(function.result));
|
||||
if diagnostic := add_unsupported_type_diagnostic(checker, function.span, type_from_syntax(checker, function.result, function.pkg, function.file));
|
||||
diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
checker.template_diagnostics[function_id] = diagnostic
|
||||
}
|
||||
if types.contains_c_struct_by_value(type_from_syntax(function.result), &checker.module.types) {
|
||||
if types.contains_c_struct_by_value(type_from_syntax(checker, function.result, function.pkg, function.file), &checker.module.types) {
|
||||
checker.template_diagnostics[function_id] = source.addf(
|
||||
checker.diagnostics,
|
||||
function.span,
|
||||
@@ -937,7 +1073,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
if types.is_valid(function.error) {
|
||||
error_type := type_from_syntax(function.error)
|
||||
error_type := type_from_syntax(checker, function.error, function.pkg, function.file)
|
||||
error_sum := types.is_enum(error_type, &checker.module.types) ||
|
||||
types.is_tagged_union(error_type, &checker.module.types)
|
||||
if function.c_abi {
|
||||
@@ -972,7 +1108,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
}
|
||||
if !function.has_body && function.c_abi {
|
||||
for param in function.params {
|
||||
param_type := type_from_syntax(param.type)
|
||||
param_type := type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
if add_unsupported_type_diagnostic(checker, param.span, param_type) !=
|
||||
source.INVALID_DIAGNOSTIC {
|
||||
continue
|
||||
@@ -989,7 +1125,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
}
|
||||
result := type_from_syntax(function.result)
|
||||
result := type_from_syntax(checker, function.result, function.pkg, function.file)
|
||||
if add_unsupported_type_diagnostic(checker, function.span, result) == source.INVALID_DIAGNOSTIC &&
|
||||
!types.contains_c_struct_by_value(result, &checker.module.types) &&
|
||||
!types.is_c_signature_type(result, &checker.module.types, true) {
|
||||
@@ -1174,7 +1310,7 @@ find_spec :: proc(checker: ^Checker, template: ast.Function_Id, actual_args: []t
|
||||
if param_index < len(actual_args) {
|
||||
actual = actual_args[param_index]
|
||||
}
|
||||
if !types.equal(spec.args[param_index], specialized_param_type(checker, param.type, actual)) {
|
||||
if !types.equal(spec.args[param_index], specialized_param_type(checker, param.type, actual, function.pkg, function.file)) {
|
||||
matches = false
|
||||
break
|
||||
}
|
||||
@@ -1190,8 +1326,14 @@ find_spec :: proc(checker: ^Checker, template: ast.Function_Id, actual_args: []t
|
||||
// type for a given actual argument. A constraint param (`int`/`float`/`range`)
|
||||
// resolves to the actual's family member (INVALID if out of family), so a call
|
||||
// passing an out-of-family argument fails to specialize and is rejected.
|
||||
specialized_param_type :: proc(checker: ^Checker, syntax: ast.Type_Syntax, actual: types.Type) -> types.Type {
|
||||
declared := type_from_syntax(syntax)
|
||||
specialized_param_type :: proc(
|
||||
checker: ^Checker,
|
||||
syntax: ast.Type_Syntax,
|
||||
actual: types.Type,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
) -> types.Type {
|
||||
declared := type_from_syntax(checker, syntax, pkg, file)
|
||||
if types.is_constraint(declared) {
|
||||
return types.constraint_target(declared, actual, &checker.module.types)
|
||||
}
|
||||
@@ -1204,7 +1346,7 @@ can_specialize :: proc(checker: ^Checker, function: ast.Function, actual_args: [
|
||||
if index < len(actual_args) {
|
||||
actual = actual_args[index]
|
||||
}
|
||||
if !is_runtime_type(checker, specialized_param_type(checker, param.type, actual)) {
|
||||
if !is_runtime_type(checker, specialized_param_type(checker, param.type, actual, function.pkg, function.file)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1223,7 +1365,7 @@ ensure_spec :: proc(checker: ^Checker, template: ast.Function_Id, actual_args: [
|
||||
if index < len(actual_args) {
|
||||
actual = actual_args[index]
|
||||
}
|
||||
append(&signature, specialized_param_type(checker, param.type, actual))
|
||||
append(&signature, specialized_param_type(checker, param.type, actual, function.pkg, function.file))
|
||||
}
|
||||
result := function_channel_type(checker, function)
|
||||
if function.pkg == 0 && function.name == checker.main_symbol && function.result == types.INT &&
|
||||
@@ -1335,6 +1477,9 @@ infer_compound_expr :: proc(
|
||||
_ = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
}
|
||||
return types.INVALID
|
||||
case .Cast:
|
||||
_ = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
return type_from_syntax(checker, expr.type, pkg, file)
|
||||
case .Address:
|
||||
child := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
return types.pointer(store, child, false, false)
|
||||
@@ -1487,7 +1632,7 @@ infer_expr :: proc(
|
||||
last = types.F64
|
||||
_ = pop(&stack)
|
||||
case .String, .Array, .None, .Undefined, .Address, .Deref, .Index, .Slice,
|
||||
.Field, .Unwrap, .Orelse, .Try, .Catch, .Struct_Literal, .Keyed, .Enum_Literal,
|
||||
.Field, .Unwrap, .Orelse, .Try, .Catch, .Struct_Literal, .Keyed, .Enum_Literal, .Cast,
|
||||
.Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
|
||||
last = infer_compound_expr(checker, expr, locals, pkg, file, demanded, local_types)
|
||||
_ = pop(&stack)
|
||||
@@ -1701,7 +1846,7 @@ infer_expr :: proc(
|
||||
// it (e.g. `take_u16(a)` resolves `a` to u16). Constraint params have no single
|
||||
// type to demand; the callee's result flowing back is milestone 14.5.
|
||||
for arg_index in 0..<len(expr.args) {
|
||||
record_demand(checker, expr.args[arg_index], call_arg_expected(function, arg_index), locals, local_types, pkg, file)
|
||||
record_demand(checker, expr.args[arg_index], call_arg_expected(checker, function, arg_index), locals, local_types, pkg, file)
|
||||
}
|
||||
// Deferred defaulting leaves an undemanded open constant typeless; give such an
|
||||
// argument its default so the call can still monomorphize (the default feeds only
|
||||
@@ -1865,7 +2010,7 @@ infer_statements :: proc(
|
||||
// binding (its declared type when annotated, else left open) and walk
|
||||
// the block body. The build pass resolves the yielded value's type
|
||||
// independently — value blocks don't join the demand fixpoint.
|
||||
declared_block := type_from_syntax(statement.type)
|
||||
declared_block := type_from_syntax(checker, statement.type, pkg, file)
|
||||
block_type := declared_block if is_runtime_type(checker, declared_block) else types.INVALID
|
||||
local := Infer_Local{
|
||||
name=statement.name, type=block_type, declared=declared_block,
|
||||
@@ -1876,7 +2021,7 @@ infer_statements :: proc(
|
||||
infer_statements(checker, statement.body, locals, local_types, pkg, file, demanded, result, result_hint)
|
||||
continue
|
||||
}
|
||||
declared_local := resolve_inferred_array(checker, type_from_syntax(statement.type), statement.expr)
|
||||
declared_local := resolve_inferred_array(checker, type_from_syntax(checker, statement.type, pkg, file), statement.expr)
|
||||
value_type := types.INVALID
|
||||
if !is_undefined_expr(checker, statement.expr) {
|
||||
value_type = infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types)
|
||||
@@ -2107,7 +2252,7 @@ infer_spec_locals_and_result :: proc(
|
||||
) -> ([]types.Type, types.Type) {
|
||||
spec := checker.specs[id]
|
||||
function := checker.ast_module.functions[spec.template]
|
||||
declared := type_from_syntax(function.result)
|
||||
declared := type_from_syntax(checker, function.result, function.pkg, function.file)
|
||||
if function.pkg == 0 && function.name == checker.main_symbol && function.result == types.INT {
|
||||
declared = types.I32
|
||||
}
|
||||
@@ -2446,7 +2591,7 @@ infer_all :: proc(checker: ^Checker) {
|
||||
// Demands accumulate in global_demands so the default never blocks a later
|
||||
// cross-family demand (e.g. integer literal -> unsigned or float).
|
||||
for global, index in checker.ast_module.globals {
|
||||
declared := type_from_syntax(global.type)
|
||||
declared := type_from_syntax(checker, global.type, global.pkg, global.file)
|
||||
if is_runtime_type(checker, declared) {
|
||||
checker.global_types[index] = declared
|
||||
continue
|
||||
@@ -2494,7 +2639,7 @@ infer_all :: proc(checker: ^Checker) {
|
||||
continue
|
||||
}
|
||||
inferred := infer_expr(checker, global.expr, nil, global.pkg, global.file)
|
||||
if is_runtime_type(checker, type_from_syntax(global.type)) {
|
||||
if is_runtime_type(checker, type_from_syntax(checker, global.type, global.pkg, global.file)) {
|
||||
continue
|
||||
}
|
||||
if is_runtime_type(checker, checker.global_demands[index]) {
|
||||
@@ -2660,9 +2805,11 @@ can_implicitly_convert_type :: proc(checker: ^Checker, actual, expected: types.T
|
||||
store := &checker.module.types
|
||||
if types.equal(actual, expected) ||
|
||||
types.can_widen(actual, expected) ||
|
||||
types.can_coerce_c_integer(actual, expected) ||
|
||||
types.can_coerce_c_integer(actual, expected, checker.target) ||
|
||||
types.can_coerce_c_scalar(actual, expected, checker.target) ||
|
||||
types.can_weaken_pointer(actual, expected, store) ||
|
||||
types.can_weaken_slice(actual, expected, store) ||
|
||||
types.can_decay_slice_c_string(actual, expected, store) ||
|
||||
types.can_decay_array_pointer(actual, expected, store) ||
|
||||
types.can_sum_widen(actual, expected, store) {
|
||||
return true
|
||||
@@ -2719,6 +2866,17 @@ coerce_expr :: proc(
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
if types.can_decay_slice_c_string(actual, expected, &checker.module.types) {
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Slice_Ptr,
|
||||
span=span,
|
||||
type=expected,
|
||||
left=expr_id,
|
||||
target=hir.INVALID_REF,
|
||||
right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
if types.can_sum_widen(actual, expected, &checker.module.types) {
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Sum_Widen,
|
||||
@@ -2734,8 +2892,11 @@ coerce_expr :: proc(
|
||||
child := types.child_type(expected, &checker.module.types)
|
||||
if types.equal(actual, child) ||
|
||||
types.can_widen(actual, child) ||
|
||||
types.can_coerce_c_integer(actual, child, checker.target) ||
|
||||
types.can_coerce_c_scalar(actual, child, checker.target) ||
|
||||
types.can_weaken_pointer(actual, child, &checker.module.types) ||
|
||||
types.can_weaken_slice(actual, child, &checker.module.types) ||
|
||||
types.can_decay_slice_c_string(actual, child, &checker.module.types) ||
|
||||
types.can_decay_array_pointer(actual, child, &checker.module.types) {
|
||||
value := coerce_expr(checker, expr_id, child, span)
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
@@ -2763,7 +2924,8 @@ coerce_expr :: proc(
|
||||
},
|
||||
)
|
||||
}
|
||||
if types.can_coerce_c_integer(actual, expected) {
|
||||
if types.can_coerce_c_integer(actual, expected, checker.target) ||
|
||||
types.can_coerce_c_scalar(actual, expected, checker.target) {
|
||||
return add_hir_expr(
|
||||
checker,
|
||||
hir.Expr {
|
||||
@@ -3311,6 +3473,31 @@ build_compound_expr :: proc(
|
||||
return invalid_hir_expr(checker, expr.span, id, expected)
|
||||
}
|
||||
return enum_member_hir(checker, expected, expr.name, expr.span)
|
||||
case .Cast:
|
||||
target := type_from_syntax(checker, expr.type, pkg, file)
|
||||
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)
|
||||
if !valid_target || !valid_actual {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
"scalar cast requires numeric scalar types, got %s to %s",
|
||||
types.name(actual),
|
||||
types.name(target),
|
||||
)
|
||||
return invalid_hir_expr(checker, expr.span, id, target)
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Scalar_Cast,
|
||||
span=expr.span,
|
||||
type=target,
|
||||
left=value,
|
||||
target=hir.INVALID_REF,
|
||||
right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Address:
|
||||
value := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
if !hir_is_location(checker, value) {
|
||||
@@ -3343,6 +3530,7 @@ build_compound_expr :: proc(
|
||||
case .Index:
|
||||
container := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
index := build_nested_expr(checker, expr.right, locals, global_reads, calls, types.USIZE, pkg, file)
|
||||
index = coerce_expr(checker, index, types.USIZE, expr.span)
|
||||
container_type := checker.module.exprs[container].type
|
||||
item, ok := types.container(container_type, store)
|
||||
if !ok {
|
||||
@@ -3367,6 +3555,7 @@ build_compound_expr :: proc(
|
||||
for bound, index in expr.args {
|
||||
if bound != ast.INVALID_EXPR {
|
||||
bounds[index] = build_nested_expr(checker, bound, locals, global_reads, calls, types.USIZE, pkg, file)
|
||||
bounds[index] = coerce_expr(checker, bounds[index], types.USIZE, checker.ast_module.exprs[bound].span)
|
||||
}
|
||||
}
|
||||
preserve_sentinel := item.has_sentinel && expr.args[1] == ast.INVALID_EXPR
|
||||
@@ -3603,17 +3792,19 @@ build_compound_expr :: proc(
|
||||
left, right: hir.Expr_Id
|
||||
left_expr := checker.ast_module.exprs[expr.left]
|
||||
right_expr := checker.ast_module.exprs[expr.right]
|
||||
left_numeric_const := left_const.kind == .Value || is_float_constant_expr(checker, expr.left)
|
||||
right_numeric_const := right_const.kind == .Value || is_float_constant_expr(checker, expr.right)
|
||||
if right_expr.kind == .Enum_Literal && left_expr.kind != .Enum_Literal {
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, checker.module.exprs[left].type, pkg, file)
|
||||
} else if left_expr.kind == .Enum_Literal && right_expr.kind != .Enum_Literal {
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, checker.module.exprs[right].type, pkg, file)
|
||||
} else if right_const.kind == .Value && left_const.kind != .Value {
|
||||
} else if right_numeric_const && !left_numeric_const {
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
hint := checker.module.exprs[left].type
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, hint, pkg, file)
|
||||
} else if left_const.kind == .Value && right_const.kind != .Value {
|
||||
} else if left_numeric_const && !right_numeric_const {
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
hint := checker.module.exprs[right].type
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, hint, pkg, file)
|
||||
@@ -3841,7 +4032,7 @@ build_expr :: proc(
|
||||
switch expr.kind {
|
||||
case .String, .Array, .None, .Undefined, .Address, .Deref, .Index, .Slice,
|
||||
.Field, .Unwrap, .Orelse, .Try, .Catch, .Struct_Literal, .Keyed,
|
||||
.Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range,
|
||||
.Bool, .Cast, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range,
|
||||
.Enum_Literal:
|
||||
last = build_compound_expr(
|
||||
checker, expr, locals, global_reads, calls, frame.expected, pkg, file,
|
||||
@@ -4086,7 +4277,7 @@ build_expr :: proc(
|
||||
stack[frame_index].arg_types = make([]types.Type, len(expr.args), checker.allocator)
|
||||
stack[frame_index].stage = 3
|
||||
if len(expr.args) > 0 {
|
||||
arg_expected := call_arg_expected(function, 0)
|
||||
arg_expected := call_arg_expected(checker, function, 0)
|
||||
if !is_runtime_type(checker, arg_expected) {
|
||||
arg_expected = types.INVALID
|
||||
}
|
||||
@@ -4141,7 +4332,7 @@ build_expr :: proc(
|
||||
stack[frame_index].arg_index += 1
|
||||
if frame.arg_index+1 < len(expr.args) {
|
||||
next := frame.arg_index+1
|
||||
arg_expected := call_arg_expected(checker.ast_module.functions[frame.template], next)
|
||||
arg_expected := call_arg_expected(checker, checker.ast_module.functions[frame.template], next)
|
||||
if !is_runtime_type(checker, arg_expected) {
|
||||
arg_expected = types.INVALID
|
||||
}
|
||||
@@ -4156,7 +4347,7 @@ build_expr :: proc(
|
||||
if index >= len(stack[frame_index].arg_types) {
|
||||
break
|
||||
}
|
||||
declared := type_from_syntax(params[index].type)
|
||||
declared := type_from_syntax(checker, params[index].type, checker.ast_module.functions[frame.template].pkg, checker.ast_module.functions[frame.template].file)
|
||||
actual := stack[frame_index].arg_types[index]
|
||||
if types.is_constraint(declared) && types.is_valid(actual) &&
|
||||
!types.is_valid(types.constraint_target(declared, actual, &checker.module.types)) {
|
||||
@@ -4413,9 +4604,9 @@ build_block :: proc(
|
||||
// declare the local from the yielded value (its type for an untyped `::`).
|
||||
if statement.expr == ast.INVALID_EXPR {
|
||||
expected := types.INVALID
|
||||
typed := is_runtime_type(checker, type_from_syntax(statement.type))
|
||||
typed := is_runtime_type(checker, type_from_syntax(checker, statement.type, ctx.pkg, ctx.file))
|
||||
if typed {
|
||||
expected = type_from_syntax(statement.type)
|
||||
expected = type_from_syntax(checker, statement.type, ctx.pkg, ctx.file)
|
||||
}
|
||||
value, value_type := build_value_source(ctx, &body, statement.body, expected, statement.span, statement.label, statement.value_control_flow)
|
||||
if _, found := find_build_local(ctx.locals^[duplicate_start:], statement.name); found {
|
||||
@@ -4445,7 +4636,7 @@ build_block :: proc(
|
||||
})
|
||||
continue
|
||||
}
|
||||
declared := resolve_inferred_array(checker, type_from_syntax(statement.type), statement.expr)
|
||||
declared := resolve_inferred_array(checker, type_from_syntax(checker, statement.type, ctx.pkg, ctx.file), statement.expr)
|
||||
// Adopt the type inference resolved for this local when the declaration has no
|
||||
// concrete annotation and inference carried useful numeric context: constraints,
|
||||
// `undefined`, open numeric constants, or arithmetic expressions.
|
||||
@@ -4717,7 +4908,7 @@ build_block :: proc(
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
if statement.expr == ast.INVALID_EXPR {
|
||||
if statement.expr == ast.INVALID_EXPR && !statement.value_control_flow {
|
||||
if types.kind(ctx.result, store) == .Fallible &&
|
||||
types.is_void(types.fallible_success(ctx.result, store)) {
|
||||
flush_defers(ctx, &body, 0)
|
||||
@@ -4756,7 +4947,17 @@ build_block :: proc(
|
||||
continue
|
||||
}
|
||||
value := hir.INVALID_EXPR
|
||||
if types.kind(ctx.result, store) == .Fallible {
|
||||
if statement.value_control_flow {
|
||||
if types.kind(ctx.result, store) == .Fallible {
|
||||
success := types.fallible_success(ctx.result, store)
|
||||
value, _ = build_value_source(ctx, &body, statement.body, success, statement.span, symbol.INVALID, statement.value_control_flow)
|
||||
value = coerce_expr(checker, value, success, statement.span)
|
||||
value = fallible_aggregate(checker, statement.span, ctx.result, value, false)
|
||||
} else {
|
||||
value, _ = build_value_source(ctx, &body, statement.body, ctx.result, statement.span, symbol.INVALID, statement.value_control_flow)
|
||||
value = coerce_expr(checker, value, ctx.result, statement.span)
|
||||
}
|
||||
} else if types.kind(ctx.result, store) == .Fallible {
|
||||
success := types.fallible_success(ctx.result, store)
|
||||
error_type := types.fallible_error(ctx.result, store)
|
||||
error_path := false
|
||||
@@ -5249,7 +5450,12 @@ build_block :: proc(
|
||||
continue
|
||||
}
|
||||
target := &ctx.yield_targets^[target_index]
|
||||
yielded := build_expr(checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls, target.slot_type, ctx.pkg, ctx.file)
|
||||
yielded := hir.INVALID_EXPR
|
||||
if statement.value_control_flow {
|
||||
yielded, _ = build_value_source(ctx, &body, statement.body, target.slot_type, statement.span, symbol.INVALID, statement.value_control_flow)
|
||||
} else {
|
||||
yielded = build_expr(checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls, target.slot_type, ctx.pkg, ctx.file)
|
||||
}
|
||||
yielded = resolve_loop_slot(ctx, target, yielded, checker.module.exprs[yielded].type if yielded != hir.INVALID_EXPR else types.INVALID, statement.span)
|
||||
if target.slot == hir.INVALID_LOCAL || yielded == hir.INVALID_EXPR {
|
||||
id := source.add(checker.diagnostics, statement.span,
|
||||
@@ -5391,10 +5597,14 @@ build_value_block :: proc(
|
||||
delete(leading, checker.allocator)
|
||||
|
||||
yield_stmt := checker.ast_module.statements[body_stmts[n - 1]]
|
||||
value = build_expr(
|
||||
checker, yield_stmt.expr, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
expected, ctx.pkg, ctx.file,
|
||||
)
|
||||
if yield_stmt.value_control_flow {
|
||||
value, value_type = build_value_source(ctx, body, yield_stmt.body, expected, yield_stmt.span, symbol.INVALID, yield_stmt.value_control_flow)
|
||||
} else {
|
||||
value = build_expr(
|
||||
checker, yield_stmt.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, expected) {
|
||||
value = coerce_expr(checker, value, expected, yield_stmt.span)
|
||||
@@ -5486,6 +5696,38 @@ emit_slot_assign :: proc(checker: ^Checker, out: ^[dynamic]hir.Stmt_Id, slot: hi
|
||||
})
|
||||
}
|
||||
|
||||
string_peer_slice_type :: proc(checker: ^Checker, value: types.Type) -> types.Type {
|
||||
pointer, array, ok := types.array_pointer(value, &checker.module.types)
|
||||
if ok && !pointer.mutable && !array.mutable &&
|
||||
array.child == types.U8 && array.has_sentinel && array.sentinel == 0 {
|
||||
return types.slice(&checker.module.types, types.U8, false, true, 0)
|
||||
}
|
||||
return types.INVALID
|
||||
}
|
||||
|
||||
adopt_value_slot :: proc(
|
||||
ctx: ^Build_Ctx,
|
||||
slot: ^hir.Local_Id,
|
||||
slot_type: ^types.Type,
|
||||
value: hir.Expr_Id,
|
||||
vtype: types.Type,
|
||||
span: source.Span,
|
||||
) -> hir.Expr_Id {
|
||||
checker := ctx.checker
|
||||
if slot^ == hir.INVALID_LOCAL {
|
||||
peer := string_peer_slice_type(checker, vtype)
|
||||
if types.is_valid(peer) {
|
||||
slot_type^ = peer
|
||||
slot^ = new_value_slot(ctx, slot_type^)
|
||||
return coerce_expr(checker, value, slot_type^, span)
|
||||
}
|
||||
slot_type^ = vtype
|
||||
slot^ = new_value_slot(ctx, slot_type^)
|
||||
return value
|
||||
}
|
||||
return coerce_expr(checker, value, slot_type^, span)
|
||||
}
|
||||
|
||||
// build_value_if turns `if c { … yield A } else { … yield B }` into a result slot
|
||||
// each branch assigns, read after the if. Every path must yield: a mandatory `else`,
|
||||
// each branch ends in `yield`, and all branches share a type (the first establishes it
|
||||
@@ -5703,12 +5945,7 @@ emit_value_branch :: proc(
|
||||
if checker.module.exprs[value].kind == .Invalid {
|
||||
return false
|
||||
}
|
||||
if slot^ == hir.INVALID_LOCAL {
|
||||
slot_type^ = vtype
|
||||
slot^ = new_value_slot(ctx, slot_type^)
|
||||
} else {
|
||||
value = coerce_expr(checker, value, slot_type^, span)
|
||||
}
|
||||
value = adopt_value_slot(ctx, slot, slot_type, value, vtype, span)
|
||||
emit_slot_assign(checker, out, slot^, value, span)
|
||||
return true
|
||||
}
|
||||
@@ -6192,12 +6429,7 @@ build_value_arm :: proc(
|
||||
return false
|
||||
}
|
||||
vtype := checker.module.exprs[value].type
|
||||
if slot^ == hir.INVALID_LOCAL {
|
||||
slot_type^ = vtype
|
||||
slot^ = new_value_slot(ctx, slot_type^)
|
||||
} else {
|
||||
value = coerce_expr(checker, value, slot_type^, span)
|
||||
}
|
||||
value = adopt_value_slot(ctx, slot, slot_type, value, vtype, span)
|
||||
emit_slot_assign(checker, out, slot^, value, span)
|
||||
return true
|
||||
}
|
||||
@@ -6937,7 +7169,7 @@ build_globals :: proc(checker: ^Checker) {
|
||||
dependencies.allocator = checker.allocator
|
||||
calls: [dynamic]hir.Function_Id
|
||||
calls.allocator = checker.allocator
|
||||
declared := resolve_inferred_array(checker, type_from_syntax(global.type), global.expr)
|
||||
declared := resolve_inferred_array(checker, type_from_syntax(checker, global.type, global.pkg, global.file), global.expr)
|
||||
expected := types.INVALID
|
||||
if is_runtime_type(checker, declared) {
|
||||
expected = declared
|
||||
|
||||
Reference in New Issue
Block a user