|
|
|
@@ -143,6 +143,12 @@ Generated_Type_Entry :: struct {
|
|
|
|
|
result: types.Type,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Type_Factory_Origin :: struct {
|
|
|
|
|
result: types.Type,
|
|
|
|
|
template: ast.Function_Id,
|
|
|
|
|
values: []Comptime_Value,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Checker :: struct {
|
|
|
|
|
ast_module: ^ast.Module,
|
|
|
|
|
diagnostics: ^source.Diagnostics,
|
|
|
|
@@ -184,6 +190,7 @@ Checker :: struct {
|
|
|
|
|
current_comptime_values: []Comptime_Value,
|
|
|
|
|
type_factories: [dynamic]Type_Factory_Entry,
|
|
|
|
|
generated_types: [dynamic]Generated_Type_Entry,
|
|
|
|
|
type_factory_origins: [dynamic]Type_Factory_Origin,
|
|
|
|
|
target: target.Target,
|
|
|
|
|
allocator: mem.Allocator,
|
|
|
|
|
}
|
|
|
|
@@ -1105,6 +1112,396 @@ valid_call_arity :: proc(function: ast.Function, count: int) -> bool {
|
|
|
|
|
return count >= len(function.params) if function.variadic else count == len(function.params)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Call_Argument_Mode :: enum u8 {
|
|
|
|
|
Invalid,
|
|
|
|
|
Explicit,
|
|
|
|
|
Inferred,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
comptime_prefix_count :: proc(function: ast.Function) -> (int, bool) {
|
|
|
|
|
count := 0
|
|
|
|
|
for param in function.params {
|
|
|
|
|
if param.comptime_value {
|
|
|
|
|
count += 1
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
for param in function.params[count:] {
|
|
|
|
|
if param.comptime_value {
|
|
|
|
|
return count, false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return count, count > 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
call_argument_mode :: proc(function: ast.Function, count: int) -> (Call_Argument_Mode, int) {
|
|
|
|
|
if valid_call_arity(function, count) {
|
|
|
|
|
return .Explicit, 0
|
|
|
|
|
}
|
|
|
|
|
prefix, inferable := comptime_prefix_count(function)
|
|
|
|
|
if inferable && !function.c_abi && !function.variadic && count == len(function.params)-prefix {
|
|
|
|
|
return .Inferred, prefix
|
|
|
|
|
}
|
|
|
|
|
return .Invalid, prefix
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
call_param_index :: proc(mode: Call_Argument_Mode, prefix, source_index: int) -> int {
|
|
|
|
|
return source_index+prefix if mode == .Inferred else source_index
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_runtime_call_arg :: proc(function: ast.Function, mode: Call_Argument_Mode, prefix, start: int, source_count: int) -> int {
|
|
|
|
|
index := start
|
|
|
|
|
for index < source_count {
|
|
|
|
|
param_index := call_param_index(mode, prefix, index)
|
|
|
|
|
if param_index >= len(function.params) || !function.params[param_index].comptime_value {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
index += 1
|
|
|
|
|
}
|
|
|
|
|
return index
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
comptime_binding_index :: proc(function: ast.Function, prefix: int, name: symbol.Id) -> (int, bool) {
|
|
|
|
|
for param, index in function.params[:prefix] {
|
|
|
|
|
if param.name == name {
|
|
|
|
|
return index, true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1, false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bind_inferred_comptime :: proc(
|
|
|
|
|
checker: ^Checker,
|
|
|
|
|
function: ast.Function,
|
|
|
|
|
prefix: int,
|
|
|
|
|
values: []Comptime_Value,
|
|
|
|
|
bound: []bool,
|
|
|
|
|
name: symbol.Id,
|
|
|
|
|
candidate: Comptime_Value,
|
|
|
|
|
span: source.Span,
|
|
|
|
|
diagnose: bool,
|
|
|
|
|
) -> bool {
|
|
|
|
|
index, ok := comptime_binding_index(function, prefix, name)
|
|
|
|
|
if !ok {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
value := candidate
|
|
|
|
|
value.name = name
|
|
|
|
|
if !bound[index] {
|
|
|
|
|
values[index] = value
|
|
|
|
|
bound[index] = true
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
existing := values[index]
|
|
|
|
|
matches := existing.kind == value.kind
|
|
|
|
|
if matches {
|
|
|
|
|
if existing.kind == .Type {
|
|
|
|
|
matches = types.equal(types.resolve_alias(existing.type, &checker.module.types), types.resolve_alias(value.type, &checker.module.types))
|
|
|
|
|
} else {
|
|
|
|
|
matches = existing.value == value.value && types.equal(existing.type, value.type)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !matches && diagnose {
|
|
|
|
|
left := type_label(checker, existing.type) if existing.kind == .Type else fmt.aprintf("%d", existing.value, allocator=checker.allocator)
|
|
|
|
|
right := type_label(checker, value.type) if value.kind == .Type else fmt.aprintf("%d", value.value, allocator=checker.allocator)
|
|
|
|
|
source.addf(checker.diagnostics, span, "conflicting inference for comptime parameter '%s': %s and %s", symbol_text(checker, name), left, right)
|
|
|
|
|
if existing.kind != .Type {
|
|
|
|
|
delete(left, checker.allocator)
|
|
|
|
|
}
|
|
|
|
|
if value.kind != .Type {
|
|
|
|
|
delete(right, checker.allocator)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return matches
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type_pattern_mentions_comptime :: proc(
|
|
|
|
|
checker: ^Checker,
|
|
|
|
|
function: ast.Function,
|
|
|
|
|
prefix: int,
|
|
|
|
|
pattern: types.Type,
|
|
|
|
|
depth := 0,
|
|
|
|
|
) -> bool {
|
|
|
|
|
if depth > 64 {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
item, ok := types.node(&checker.module.types, pattern)
|
|
|
|
|
if !ok {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if item.qualifier == 0 && item.name != 0 {
|
|
|
|
|
if _, found := comptime_binding_index(function, prefix, symbol.Id(item.name)); found {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if item.kind == .Array && item.unresolved_count {
|
|
|
|
|
expr_id := ast.Expr_Id(item.count_expr)
|
|
|
|
|
if expr_id != ast.INVALID_EXPR && int(expr_id) < len(checker.ast_module.exprs) {
|
|
|
|
|
expr := checker.ast_module.exprs[expr_id]
|
|
|
|
|
if expr.kind == .Name && !symbol.is_valid(expr.qualifier) {
|
|
|
|
|
if _, found := comptime_binding_index(function, prefix, expr.name); found {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if item.kind == .Type_Call {
|
|
|
|
|
expr_id := ast.Expr_Id(item.count_expr)
|
|
|
|
|
if expr_id != ast.INVALID_EXPR && int(expr_id) < len(checker.ast_module.exprs) {
|
|
|
|
|
for arg_id in checker.ast_module.exprs[expr_id].args {
|
|
|
|
|
if arg_id == ast.INVALID_EXPR || int(arg_id) >= len(checker.ast_module.exprs) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
arg := checker.ast_module.exprs[arg_id]
|
|
|
|
|
if arg.kind == .Name && !symbol.is_valid(arg.qualifier) {
|
|
|
|
|
if _, found := comptime_binding_index(function, prefix, arg.name); found {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if types.is_valid(item.child) && type_pattern_mentions_comptime(checker, function, prefix, item.child, depth+1) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if types.is_valid(item.extra) && type_pattern_mentions_comptime(checker, function, prefix, item.extra, depth+1) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if item.kind == .Function {
|
|
|
|
|
for field in types.params_for(&checker.module.types, pattern) {
|
|
|
|
|
if type_pattern_mentions_comptime(checker, function, prefix, field.type, depth+1) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match_inferred_type_pattern :: proc(
|
|
|
|
|
checker: ^Checker,
|
|
|
|
|
function: ast.Function,
|
|
|
|
|
prefix: int,
|
|
|
|
|
pattern, actual: types.Type,
|
|
|
|
|
values: []Comptime_Value,
|
|
|
|
|
bound: []bool,
|
|
|
|
|
span: source.Span,
|
|
|
|
|
diagnose: bool,
|
|
|
|
|
depth := 0,
|
|
|
|
|
) -> bool {
|
|
|
|
|
if depth > 64 || !types.is_valid(actual) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
store := &checker.module.types
|
|
|
|
|
actual_type := types.resolve_alias(actual, store)
|
|
|
|
|
pattern_item, pattern_ok := types.node(store, pattern)
|
|
|
|
|
if pattern_ok && pattern_item.qualifier == 0 && pattern_item.name != 0 {
|
|
|
|
|
name := symbol.Id(pattern_item.name)
|
|
|
|
|
if index, is_binding := comptime_binding_index(function, prefix, name); is_binding &&
|
|
|
|
|
is_comptime_type_param(checker, function.params[index]) {
|
|
|
|
|
return bind_inferred_comptime(
|
|
|
|
|
checker, function, prefix, values, bound, name,
|
|
|
|
|
Comptime_Value{type=actual_type, kind=.Type}, span, diagnose,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !pattern_ok {
|
|
|
|
|
return types.equal(pattern, actual_type)
|
|
|
|
|
}
|
|
|
|
|
if pattern_item.kind == .Type_Call {
|
|
|
|
|
expr_id := ast.Expr_Id(pattern_item.count_expr)
|
|
|
|
|
if expr_id == ast.INVALID_EXPR || int(expr_id) >= len(checker.ast_module.exprs) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
expr := checker.ast_module.exprs[expr_id]
|
|
|
|
|
target_pkg, available := expr_package(checker, expr, function.pkg, function.file, true)
|
|
|
|
|
if !available {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
template := find_template(checker, expr.name, target_pkg, expr_lookup_file(expr, function.file))
|
|
|
|
|
origin: Type_Factory_Origin
|
|
|
|
|
found := false
|
|
|
|
|
for candidate in checker.type_factory_origins {
|
|
|
|
|
if candidate.template == template && types.equal(candidate.result, actual_type) {
|
|
|
|
|
origin = candidate
|
|
|
|
|
found = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !found || len(expr.args) != len(origin.values) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
matched := true
|
|
|
|
|
for arg_id, index in expr.args {
|
|
|
|
|
if arg_id == ast.INVALID_EXPR || int(arg_id) >= len(checker.ast_module.exprs) {
|
|
|
|
|
matched = false
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
arg := checker.ast_module.exprs[arg_id]
|
|
|
|
|
if arg.kind == .Name && !symbol.is_valid(arg.qualifier) {
|
|
|
|
|
if _, is_binding := comptime_binding_index(function, prefix, arg.name); is_binding {
|
|
|
|
|
matched = bind_inferred_comptime(
|
|
|
|
|
checker, function, prefix, values, bound, arg.name,
|
|
|
|
|
origin.values[index], span, diagnose,
|
|
|
|
|
) && matched
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return matched
|
|
|
|
|
}
|
|
|
|
|
actual_item, actual_ok := types.node(store, actual_type)
|
|
|
|
|
if !actual_ok || pattern_item.kind != actual_item.kind {
|
|
|
|
|
resolved := type_from_syntax(checker, pattern, function.pkg, function.file)
|
|
|
|
|
return types.is_valid(resolved) && types.equal(types.resolve_alias(resolved, store), actual_type)
|
|
|
|
|
}
|
|
|
|
|
if pattern_item.many != actual_item.many || pattern_item.has_sentinel != actual_item.has_sentinel ||
|
|
|
|
|
pattern_item.has_sentinel && pattern_item.sentinel != actual_item.sentinel {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
matched := true
|
|
|
|
|
if pattern_item.kind == .Array {
|
|
|
|
|
if pattern_item.unresolved_count {
|
|
|
|
|
count_expr := ast.Expr_Id(pattern_item.count_expr)
|
|
|
|
|
if count_expr != ast.INVALID_EXPR && int(count_expr) < len(checker.ast_module.exprs) {
|
|
|
|
|
expr := checker.ast_module.exprs[count_expr]
|
|
|
|
|
if expr.kind == .Name && !symbol.is_valid(expr.qualifier) {
|
|
|
|
|
if binding_index, is_binding := comptime_binding_index(function, prefix, expr.name); is_binding &&
|
|
|
|
|
!is_comptime_type_param(checker, function.params[binding_index]) {
|
|
|
|
|
matched = bind_inferred_comptime(
|
|
|
|
|
checker, function, prefix, values, bound, expr.name,
|
|
|
|
|
Comptime_Value{type=values[binding_index].type, value=i128(actual_item.count), kind=.Integer},
|
|
|
|
|
span, diagnose,
|
|
|
|
|
) && matched
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if pattern_item.count != actual_item.count {
|
|
|
|
|
matched = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if types.is_valid(pattern_item.child) {
|
|
|
|
|
matched = match_inferred_type_pattern(
|
|
|
|
|
checker, function, prefix, pattern_item.child, actual_item.child,
|
|
|
|
|
values, bound, span, diagnose, depth+1,
|
|
|
|
|
) && matched
|
|
|
|
|
}
|
|
|
|
|
if types.is_valid(pattern_item.extra) {
|
|
|
|
|
matched = match_inferred_type_pattern(
|
|
|
|
|
checker, function, prefix, pattern_item.extra, actual_item.extra,
|
|
|
|
|
values, bound, span, diagnose, depth+1,
|
|
|
|
|
) && matched
|
|
|
|
|
}
|
|
|
|
|
if pattern_item.kind == .Function {
|
|
|
|
|
pattern_params := types.params_for(store, pattern)
|
|
|
|
|
actual_params := types.params_for(store, actual_type)
|
|
|
|
|
if len(pattern_params) != len(actual_params) || pattern_item.c_abi != actual_item.c_abi || pattern_item.variadic != actual_item.variadic {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
for field, index in pattern_params {
|
|
|
|
|
matched = match_inferred_type_pattern(
|
|
|
|
|
checker, function, prefix, field.type, actual_params[index].type,
|
|
|
|
|
values, bound, span, diagnose, depth+1,
|
|
|
|
|
) && matched
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return matched
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
infer_call_comptime_values :: proc(
|
|
|
|
|
checker: ^Checker,
|
|
|
|
|
function: ast.Function,
|
|
|
|
|
prefix: int,
|
|
|
|
|
args: []ast.Expr_Id,
|
|
|
|
|
actual_args: []types.Type,
|
|
|
|
|
expected: types.Type,
|
|
|
|
|
pkg: ast.Package_Id,
|
|
|
|
|
file: ast.File_Id,
|
|
|
|
|
diagnose := false,
|
|
|
|
|
) -> ([]Comptime_Value, bool) {
|
|
|
|
|
values := make([]Comptime_Value, prefix, checker.allocator)
|
|
|
|
|
bound := make([]bool, prefix, checker.allocator)
|
|
|
|
|
defer delete(bound, checker.allocator)
|
|
|
|
|
for param, index in function.params[:prefix] {
|
|
|
|
|
values[index].name = param.name
|
|
|
|
|
if is_comptime_type_param(checker, param) {
|
|
|
|
|
values[index].kind = .Type
|
|
|
|
|
} else {
|
|
|
|
|
values[index].kind = .Integer
|
|
|
|
|
values[index].type = type_from_syntax(checker, param.type, function.pkg, function.file)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
matched := true
|
|
|
|
|
if is_runtime_type(checker, expected) {
|
|
|
|
|
if types.is_valid(function.error) && types.kind(expected, &checker.module.types) == .Fallible {
|
|
|
|
|
matched = match_inferred_type_pattern(
|
|
|
|
|
checker, function, prefix, function.result,
|
|
|
|
|
types.fallible_success(expected, &checker.module.types), values, bound,
|
|
|
|
|
source.Span{}, diagnose,
|
|
|
|
|
) && matched
|
|
|
|
|
matched = match_inferred_type_pattern(
|
|
|
|
|
checker, function, prefix, function.error,
|
|
|
|
|
types.fallible_error(expected, &checker.module.types), values, bound,
|
|
|
|
|
source.Span{}, diagnose,
|
|
|
|
|
) && matched
|
|
|
|
|
} else if type_pattern_mentions_comptime(checker, function, prefix, function.result) {
|
|
|
|
|
matched = match_inferred_type_pattern(
|
|
|
|
|
checker, function, prefix, function.result, expected, values, bound,
|
|
|
|
|
source.Span{}, diagnose,
|
|
|
|
|
) && matched
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Concrete arguments bind first. Numeric constants are contextual and therefore
|
|
|
|
|
// only contribute their default type after stronger evidence has had a chance.
|
|
|
|
|
weak_passes := [2]bool{false, true}
|
|
|
|
|
for weak in weak_passes {
|
|
|
|
|
for arg_id, source_index in args {
|
|
|
|
|
param_index := prefix+source_index
|
|
|
|
|
if param_index >= len(function.params) || param_index >= len(actual_args) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
is_weak := is_numeric_constant_expr(checker, arg_id)
|
|
|
|
|
if is_weak != weak {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if !type_pattern_mentions_comptime(checker, function, prefix, function.params[param_index].type) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if weak {
|
|
|
|
|
if item, ok := types.node(&checker.module.types, function.params[param_index].type); ok &&
|
|
|
|
|
item.qualifier == 0 && item.name != 0 {
|
|
|
|
|
if binding_index, is_binding := comptime_binding_index(function, prefix, symbol.Id(item.name));
|
|
|
|
|
is_binding && bound[binding_index] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
matched = match_inferred_type_pattern(
|
|
|
|
|
checker, function, prefix, function.params[param_index].type,
|
|
|
|
|
actual_args[param_index], values, bound,
|
|
|
|
|
checker.ast_module.exprs[arg_id].span, diagnose,
|
|
|
|
|
) && matched
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for param, index in function.params[:prefix] {
|
|
|
|
|
if bound[index] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
matched = false
|
|
|
|
|
if diagnose {
|
|
|
|
|
source.addf(
|
|
|
|
|
checker.diagnostics, param.span,
|
|
|
|
|
"cannot infer comptime parameter '%s'; pass it explicitly",
|
|
|
|
|
symbol_text(checker, param.name),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !matched {
|
|
|
|
|
delete(values, checker.allocator)
|
|
|
|
|
return nil, false
|
|
|
|
|
}
|
|
|
|
|
return values, true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
call_arg_expected :: proc(checker: ^Checker, function: ast.Function, index: int) -> types.Type {
|
|
|
|
|
if index < 0 || index >= len(function.params) {
|
|
|
|
|
return types.INVALID
|
|
|
|
@@ -1112,6 +1509,14 @@ call_arg_expected :: proc(checker: ^Checker, function: ast.Function, index: int)
|
|
|
|
|
if is_comptime_type_param(checker, function.params[index]) {
|
|
|
|
|
return types.INVALID
|
|
|
|
|
}
|
|
|
|
|
for param in function.params {
|
|
|
|
|
if !param.comptime_value {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if _, ok := current_comptime_value(checker, param.name); !ok {
|
|
|
|
|
return types.INVALID
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
@@ -1273,6 +1678,31 @@ resolve_type_factory_call :: proc(checker: ^Checker, expr_id: ast.Expr_Id, pkg:
|
|
|
|
|
ct_state_destroy(&state)
|
|
|
|
|
checker.type_factories[entry_index].result = result
|
|
|
|
|
checker.type_factories[entry_index].resolving = false
|
|
|
|
|
if types.is_valid(result) {
|
|
|
|
|
generated := false
|
|
|
|
|
for entry in checker.generated_types {
|
|
|
|
|
if types.equal(entry.result, result) {
|
|
|
|
|
generated = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if generated {
|
|
|
|
|
has_origin := false
|
|
|
|
|
for origin in checker.type_factory_origins {
|
|
|
|
|
if types.equal(origin.result, result) {
|
|
|
|
|
has_origin = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !has_origin {
|
|
|
|
|
append(&checker.type_factory_origins, Type_Factory_Origin{
|
|
|
|
|
result=result,
|
|
|
|
|
template=template,
|
|
|
|
|
values=clone_comptime_values(values, checker.allocator),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1631,7 +2061,10 @@ validate_declarations :: proc(checker: ^Checker) {
|
|
|
|
|
locals: [dynamic]symbol.Id
|
|
|
|
|
locals.allocator = checker.allocator
|
|
|
|
|
for param in function.params {
|
|
|
|
|
param_type := type_from_syntax(checker, param.type, function.pkg, function.file)
|
|
|
|
|
param_type := types.INVALID
|
|
|
|
|
if param.comptime_value || !has_comptime {
|
|
|
|
|
param_type = type_from_syntax(checker, param.type, function.pkg, function.file)
|
|
|
|
|
}
|
|
|
|
|
if param.comptime_value {
|
|
|
|
|
if function.c_abi {
|
|
|
|
|
checker.template_diagnostics[function_id] = source.add(
|
|
|
|
@@ -2078,9 +2511,12 @@ mark_spec_demanded :: proc(checker: ^Checker, id: Spec_Id, stack: ^[dynamic]Spec
|
|
|
|
|
|
|
|
|
|
Infer_Frame :: struct {
|
|
|
|
|
expr: ast.Expr_Id,
|
|
|
|
|
expected: types.Type,
|
|
|
|
|
stage: u8,
|
|
|
|
|
left: types.Type,
|
|
|
|
|
arg_index: int,
|
|
|
|
|
arg_mode: Call_Argument_Mode,
|
|
|
|
|
prefix: int,
|
|
|
|
|
args: []types.Type,
|
|
|
|
|
template: ast.Function_Id,
|
|
|
|
|
}
|
|
|
|
@@ -2093,11 +2529,12 @@ infer_nested_expr :: proc(
|
|
|
|
|
file: ast.File_Id,
|
|
|
|
|
demanded: ^[dynamic]Spec_Id,
|
|
|
|
|
local_types: []types.Type = nil,
|
|
|
|
|
expected := types.INVALID,
|
|
|
|
|
) -> types.Type {
|
|
|
|
|
outer := checker.infer_stack
|
|
|
|
|
checker.infer_stack = nil
|
|
|
|
|
checker.infer_stack.allocator = checker.allocator
|
|
|
|
|
result := infer_expr(checker, expr_id, locals, pkg, file, demanded, local_types)
|
|
|
|
|
result := infer_expr(checker, expr_id, locals, pkg, file, demanded, local_types, expected)
|
|
|
|
|
delete(checker.infer_stack)
|
|
|
|
|
checker.infer_stack = outer
|
|
|
|
|
return result
|
|
|
|
@@ -2111,6 +2548,7 @@ infer_compound_expr :: proc(
|
|
|
|
|
file: ast.File_Id,
|
|
|
|
|
demanded: ^[dynamic]Spec_Id,
|
|
|
|
|
local_types: []types.Type = nil,
|
|
|
|
|
expected := types.INVALID,
|
|
|
|
|
) -> types.Type {
|
|
|
|
|
store := &checker.module.types
|
|
|
|
|
#partial switch expr.kind {
|
|
|
|
@@ -2224,10 +2662,12 @@ infer_compound_expr :: proc(
|
|
|
|
|
_ = infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types)
|
|
|
|
|
return types.child_type(value, store) if types.is_optional(value, store) else types.INVALID
|
|
|
|
|
case .Try:
|
|
|
|
|
value := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
|
|
|
|
left_expected := expected if checker.ast_module.exprs[expr.left].kind == .Call else types.INVALID
|
|
|
|
|
value := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types, left_expected)
|
|
|
|
|
return types.fallible_success(value, store) if types.kind(value, store) == .Fallible else types.INVALID
|
|
|
|
|
case .Catch:
|
|
|
|
|
value := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
|
|
|
|
left_expected := expected if checker.ast_module.exprs[expr.left].kind == .Call else types.INVALID
|
|
|
|
|
value := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types, left_expected)
|
|
|
|
|
success := types.fallible_success(value, store)
|
|
|
|
|
error_type := types.fallible_error(value, store)
|
|
|
|
|
if expr.right != ast.INVALID_EXPR {
|
|
|
|
@@ -2273,6 +2713,7 @@ infer_expr :: proc(
|
|
|
|
|
file := ast.File_Id(0),
|
|
|
|
|
demanded: ^[dynamic]Spec_Id = nil,
|
|
|
|
|
local_types: []types.Type = nil,
|
|
|
|
|
expected := types.INVALID,
|
|
|
|
|
) -> types.Type {
|
|
|
|
|
stack := checker.infer_stack
|
|
|
|
|
clear_dynamic_array(&stack)
|
|
|
|
@@ -2283,7 +2724,7 @@ infer_expr :: proc(
|
|
|
|
|
clear_dynamic_array(&stack)
|
|
|
|
|
checker.infer_stack = stack
|
|
|
|
|
}
|
|
|
|
|
append(&stack, Infer_Frame{expr=expr_id, template=ast.INVALID_FUNCTION})
|
|
|
|
|
append(&stack, Infer_Frame{expr=expr_id, expected=expected, template=ast.INVALID_FUNCTION})
|
|
|
|
|
last := types.INVALID
|
|
|
|
|
|
|
|
|
|
for len(stack) > 0 {
|
|
|
|
@@ -2330,7 +2771,7 @@ infer_expr :: proc(
|
|
|
|
|
case .String, .Array, .None, .Undefined, .Address, .Deref, .Index, .Slice,
|
|
|
|
|
.Field, .Unwrap, .Orelse, .Try, .Catch, .Struct_Literal, .Keyed, .Enum_Literal, .Cast,
|
|
|
|
|
.Comptime, .Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
|
|
|
|
|
last = infer_compound_expr(checker, expr, locals, pkg, file, demanded, local_types)
|
|
|
|
|
last = infer_compound_expr(checker, expr, locals, pkg, file, demanded, local_types, frame.expected)
|
|
|
|
|
_ = pop(&stack)
|
|
|
|
|
case .Function_Literal:
|
|
|
|
|
template := ast.Function_Id(u32(expr.integer))
|
|
|
|
@@ -2559,11 +3000,26 @@ infer_expr :: proc(
|
|
|
|
|
_ = pop(&stack)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
function := checker.ast_module.functions[template]
|
|
|
|
|
mode, prefix := call_argument_mode(function, len(expr.args))
|
|
|
|
|
if mode == .Invalid {
|
|
|
|
|
last = types.INVALID
|
|
|
|
|
_ = pop(&stack)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
stack[frame_index].template = template
|
|
|
|
|
stack[frame_index].args = make([]types.Type, len(expr.args), checker.allocator)
|
|
|
|
|
stack[frame_index].arg_mode = mode
|
|
|
|
|
stack[frame_index].prefix = prefix
|
|
|
|
|
stack[frame_index].args = make([]types.Type, len(function.params), checker.allocator)
|
|
|
|
|
stack[frame_index].arg_index = next_runtime_call_arg(function, mode, prefix, 0, len(expr.args))
|
|
|
|
|
stack[frame_index].stage = 3
|
|
|
|
|
if len(expr.args) > 0 {
|
|
|
|
|
append(&stack, Infer_Frame{expr=expr.args[0], template=ast.INVALID_FUNCTION})
|
|
|
|
|
if stack[frame_index].arg_index < len(expr.args) {
|
|
|
|
|
param_index := call_param_index(mode, prefix, stack[frame_index].arg_index)
|
|
|
|
|
arg_expected := call_arg_expected(checker, function, param_index)
|
|
|
|
|
if !is_runtime_type(checker, arg_expected) {
|
|
|
|
|
arg_expected = types.INVALID
|
|
|
|
|
}
|
|
|
|
|
append(&stack, Infer_Frame{expr=expr.args[stack[frame_index].arg_index], expected=arg_expected, template=ast.INVALID_FUNCTION})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
@@ -2616,37 +3072,62 @@ infer_expr :: proc(
|
|
|
|
|
}
|
|
|
|
|
if frame.stage == 3 {
|
|
|
|
|
if frame.arg_index < len(expr.args) {
|
|
|
|
|
stack[frame_index].args[frame.arg_index] = last
|
|
|
|
|
stack[frame_index].arg_index += 1
|
|
|
|
|
if frame.arg_index+1 < len(expr.args) {
|
|
|
|
|
append(&stack, Infer_Frame{expr=expr.args[frame.arg_index+1], template=ast.INVALID_FUNCTION})
|
|
|
|
|
param_index := call_param_index(frame.arg_mode, frame.prefix, frame.arg_index)
|
|
|
|
|
if param_index < len(stack[frame_index].args) {
|
|
|
|
|
stack[frame_index].args[param_index] = last
|
|
|
|
|
}
|
|
|
|
|
next := next_runtime_call_arg(
|
|
|
|
|
checker.ast_module.functions[frame.template], frame.arg_mode, frame.prefix,
|
|
|
|
|
frame.arg_index+1, len(expr.args),
|
|
|
|
|
)
|
|
|
|
|
stack[frame_index].arg_index = next
|
|
|
|
|
if next < len(expr.args) {
|
|
|
|
|
next_param := call_param_index(frame.arg_mode, frame.prefix, next)
|
|
|
|
|
arg_expected := call_arg_expected(checker, checker.ast_module.functions[frame.template], next_param)
|
|
|
|
|
if !is_runtime_type(checker, arg_expected) {
|
|
|
|
|
arg_expected = types.INVALID
|
|
|
|
|
}
|
|
|
|
|
append(&stack, Infer_Frame{expr=expr.args[next], expected=arg_expected, template=ast.INVALID_FUNCTION})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
function := checker.ast_module.functions[frame.template]
|
|
|
|
|
// A bare-name argument passed to a concrete (non-constraint) parameter pushes
|
|
|
|
|
// that parameter type back onto the argument's slot, so an open constant adopts
|
|
|
|
|
// 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(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
|
|
|
|
|
// the spec arg vector, not a demand).
|
|
|
|
|
for arg_index in 0..<len(expr.args) {
|
|
|
|
|
if !is_runtime_type(checker, stack[frame_index].args[arg_index]) {
|
|
|
|
|
fallback := open_const_default_type(checker, expr.args[arg_index], locals, pkg, file)
|
|
|
|
|
for source_index in 0..<len(expr.args) {
|
|
|
|
|
param_index := call_param_index(frame.arg_mode, frame.prefix, source_index)
|
|
|
|
|
if param_index < len(stack[frame_index].args) && !is_runtime_type(checker, stack[frame_index].args[param_index]) {
|
|
|
|
|
fallback := open_const_default_type(checker, expr.args[source_index], locals, pkg, file)
|
|
|
|
|
if is_runtime_type(checker, fallback) {
|
|
|
|
|
stack[frame_index].args[arg_index] = fallback
|
|
|
|
|
stack[frame_index].args[param_index] = fallback
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
comptime_values, comptime_ok := collect_comptime_values(checker, function, expr.args, pkg, file)
|
|
|
|
|
comptime_values: []Comptime_Value
|
|
|
|
|
comptime_ok := false
|
|
|
|
|
if frame.arg_mode == .Inferred {
|
|
|
|
|
comptime_values, comptime_ok = infer_call_comptime_values(
|
|
|
|
|
checker, function, frame.prefix, expr.args, stack[frame_index].args,
|
|
|
|
|
frame.expected, pkg, file,
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
comptime_values, comptime_ok = collect_comptime_values(checker, function, expr.args, pkg, file)
|
|
|
|
|
}
|
|
|
|
|
defer delete(comptime_values, checker.allocator)
|
|
|
|
|
if valid_call_arity(function, len(expr.args)) &&
|
|
|
|
|
comptime_ok &&
|
|
|
|
|
if comptime_ok &&
|
|
|
|
|
can_specialize(checker, function, stack[frame_index].args, comptime_values) {
|
|
|
|
|
previous_comptime := checker.current_comptime_values
|
|
|
|
|
checker.current_comptime_values = comptime_values
|
|
|
|
|
for source_index in 0..<len(expr.args) {
|
|
|
|
|
param_index := call_param_index(frame.arg_mode, frame.prefix, source_index)
|
|
|
|
|
if param_index >= len(function.params) || function.params[param_index].comptime_value {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
demand := call_arg_expected(checker, function, param_index)
|
|
|
|
|
record_demand(checker, expr.args[source_index], demand, locals, local_types, pkg, file)
|
|
|
|
|
}
|
|
|
|
|
checker.current_comptime_values = previous_comptime
|
|
|
|
|
spec := INVALID_SPEC
|
|
|
|
|
if demanded == nil {
|
|
|
|
|
spec = ensure_spec(checker, frame.template, stack[frame_index].args, comptime_values)
|
|
|
|
@@ -2657,7 +3138,10 @@ infer_expr :: proc(
|
|
|
|
|
if spec != INVALID_SPEC {
|
|
|
|
|
last = checker.specs[spec].result
|
|
|
|
|
} else {
|
|
|
|
|
previous_comptime := checker.current_comptime_values
|
|
|
|
|
checker.current_comptime_values = comptime_values
|
|
|
|
|
declared := function_channel_type(checker, function)
|
|
|
|
|
checker.current_comptime_values = previous_comptime
|
|
|
|
|
last = declared if is_runtime_type(checker, declared) || types.is_void(declared) else types.INVALID
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@@ -2810,7 +3294,8 @@ infer_statements :: proc(
|
|
|
|
|
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)
|
|
|
|
|
expected := declared_local if is_runtime_type(checker, declared_local) else types.INVALID
|
|
|
|
|
value_type = infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types, expected)
|
|
|
|
|
declared_local = resolve_inferred_array_from_type(checker, declared_local, value_type)
|
|
|
|
|
}
|
|
|
|
|
if is_runtime_type(checker, declared_local) && !has_inferred_array_count(checker, declared_local) {
|
|
|
|
@@ -2863,14 +3348,24 @@ infer_statements :: proc(
|
|
|
|
|
infer_statements(checker, statement.body, locals, local_types, pkg, file, demanded, result, result_hint)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
value_type := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types)
|
|
|
|
|
expected_assignment := types.INVALID
|
|
|
|
|
if statement.target != ast.INVALID_EXPR {
|
|
|
|
|
expected_assignment = infer_expr(checker, statement.target, locals^[:], pkg, file, demanded, local_types)
|
|
|
|
|
} else if statement.name != checker.sink_symbol {
|
|
|
|
|
if local_index, ok := find_infer_local_index(locals^[:], statement.name); ok {
|
|
|
|
|
expected_assignment = locals^[local_index].type
|
|
|
|
|
} else if global := find_global(checker, statement.name, pkg, file); global != ast.INVALID_GLOBAL {
|
|
|
|
|
expected_assignment = checker.global_types[global]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
value_type := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types, expected_assignment)
|
|
|
|
|
// Only push the target's type back onto a bare-name RHS (e.g. `x += speed`):
|
|
|
|
|
// pushing through an arithmetic RHS would feed the target's (often provisional)
|
|
|
|
|
// type onto open-constant operands and poison their family. Operands of an
|
|
|
|
|
// arithmetic RHS resolve from their own authoritative uses.
|
|
|
|
|
rhs_is_arith := is_arith_kind(checker.ast_module.exprs[statement.expr].kind)
|
|
|
|
|
if statement.target != ast.INVALID_EXPR {
|
|
|
|
|
target_type := infer_expr(checker, statement.target, locals^[:], pkg, file, demanded, local_types)
|
|
|
|
|
target_type := expected_assignment
|
|
|
|
|
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 &&
|
|
|
|
@@ -2903,7 +3398,7 @@ infer_statements :: proc(
|
|
|
|
|
}
|
|
|
|
|
case .Return:
|
|
|
|
|
if statement.expr != ast.INVALID_EXPR {
|
|
|
|
|
returned := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types)
|
|
|
|
|
returned := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types, result_hint)
|
|
|
|
|
if is_runtime_type(checker, result_hint) {
|
|
|
|
|
_ = record_demand_shallow(checker, statement.expr, result_hint, locals^[:], local_types, pkg, file)
|
|
|
|
|
expr := checker.ast_module.exprs[statement.expr]
|
|
|
|
@@ -3505,12 +4000,13 @@ infer_all :: proc(checker: ^Checker) {
|
|
|
|
|
if global.external {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
inferred := infer_expr(checker, global.expr, nil, global.pkg, global.file)
|
|
|
|
|
declared := resolve_inferred_array(
|
|
|
|
|
checker,
|
|
|
|
|
type_from_syntax(checker, global.type, global.pkg, global.file),
|
|
|
|
|
global.expr,
|
|
|
|
|
)
|
|
|
|
|
expected := declared if is_runtime_type(checker, declared) else types.INVALID
|
|
|
|
|
inferred := infer_expr(checker, global.expr, nil, global.pkg, global.file, expected=expected)
|
|
|
|
|
if resolved := resolve_inferred_array_from_type(checker, declared, inferred);
|
|
|
|
|
resolved != declared {
|
|
|
|
|
if !types.equal(checker.global_types[index], resolved) {
|
|
|
|
@@ -3991,19 +4487,13 @@ Build_Expr_Frame :: struct {
|
|
|
|
|
stage: u8,
|
|
|
|
|
left: hir.Expr_Id,
|
|
|
|
|
arg_index: int,
|
|
|
|
|
arg_mode: Call_Argument_Mode,
|
|
|
|
|
prefix: int,
|
|
|
|
|
built_args: []hir.Expr_Id,
|
|
|
|
|
arg_types: []types.Type,
|
|
|
|
|
template: ast.Function_Id,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_built_template_arg :: proc(checker: ^Checker, function: ast.Function, start: int) -> int {
|
|
|
|
|
index := start
|
|
|
|
|
for index < len(function.params) && is_comptime_type_param(checker, function.params[index]) {
|
|
|
|
|
index += 1
|
|
|
|
|
}
|
|
|
|
|
return index
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hir_location_writable :: proc(checker: ^Checker, expr_id: hir.Expr_Id, locals: []Build_Local) -> bool {
|
|
|
|
|
if expr_id == hir.INVALID_EXPR || int(expr_id) >= len(checker.module.exprs) {
|
|
|
|
|
return false
|
|
|
|
@@ -4714,7 +5204,8 @@ build_compound_expr :: proc(
|
|
|
|
|
target=hir.INVALID_REF, diagnostic=source.INVALID_DIAGNOSTIC,
|
|
|
|
|
})
|
|
|
|
|
case .Try:
|
|
|
|
|
channel := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
|
|
|
|
left_expected := expected if checker.ast_module.exprs[expr.left].kind == .Call else types.INVALID
|
|
|
|
|
channel := build_nested_expr(checker, expr.left, locals, global_reads, calls, left_expected, pkg, file)
|
|
|
|
|
channel_type := checker.module.exprs[channel].type
|
|
|
|
|
success := types.fallible_success(channel_type, store)
|
|
|
|
|
if !types.is_valid(success) {
|
|
|
|
@@ -4747,7 +5238,8 @@ build_compound_expr :: proc(
|
|
|
|
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
|
|
|
|
})
|
|
|
|
|
case .Catch:
|
|
|
|
|
channel := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
|
|
|
|
left_expected := expected if checker.ast_module.exprs[expr.left].kind == .Call else types.INVALID
|
|
|
|
|
channel := build_nested_expr(checker, expr.left, locals, global_reads, calls, left_expected, pkg, file)
|
|
|
|
|
channel_type := checker.module.exprs[channel].type
|
|
|
|
|
success := types.fallible_success(channel_type, store)
|
|
|
|
|
if !types.is_valid(success) {
|
|
|
|
@@ -5448,9 +5940,20 @@ build_expr :: proc(
|
|
|
|
|
_ = pop(&stack)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if !valid_call_arity(function, len(expr.args)) {
|
|
|
|
|
mode, prefix := call_argument_mode(function, len(expr.args))
|
|
|
|
|
if mode == .Invalid {
|
|
|
|
|
message := "function '%s' expects at least %d arguments, got %d" if function.variadic else
|
|
|
|
|
"function '%s' expects %d arguments, got %d"
|
|
|
|
|
if inferred_prefix, inferable := comptime_prefix_count(function); inferable && !function.c_abi && !function.variadic {
|
|
|
|
|
id := source.addf(
|
|
|
|
|
checker.diagnostics, expr.span,
|
|
|
|
|
"function '%s' expects %d arguments with explicit comptime parameters or %d with inferred comptime parameters, got %d",
|
|
|
|
|
symbol_text(checker, expr.name), len(function.params), len(function.params)-inferred_prefix, len(expr.args),
|
|
|
|
|
)
|
|
|
|
|
last = invalid_hir_expr(checker, expr.span, id)
|
|
|
|
|
_ = pop(&stack)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
id := source.addf(
|
|
|
|
|
checker.diagnostics,
|
|
|
|
|
expr.span,
|
|
|
|
@@ -5464,15 +5967,18 @@ build_expr :: proc(
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
stack[frame_index].template = template
|
|
|
|
|
stack[frame_index].arg_mode = mode
|
|
|
|
|
stack[frame_index].prefix = prefix
|
|
|
|
|
stack[frame_index].built_args = make([]hir.Expr_Id, len(expr.args), checker.allocator)
|
|
|
|
|
stack[frame_index].arg_types = make([]types.Type, len(expr.args), checker.allocator)
|
|
|
|
|
stack[frame_index].arg_types = make([]types.Type, len(function.params), checker.allocator)
|
|
|
|
|
for &arg in stack[frame_index].built_args {
|
|
|
|
|
arg = hir.INVALID_EXPR
|
|
|
|
|
}
|
|
|
|
|
stack[frame_index].arg_index = next_built_template_arg(checker, function, 0)
|
|
|
|
|
stack[frame_index].arg_index = next_runtime_call_arg(function, mode, prefix, 0, len(expr.args))
|
|
|
|
|
stack[frame_index].stage = 3
|
|
|
|
|
if stack[frame_index].arg_index < len(expr.args) {
|
|
|
|
|
arg_expected := call_arg_expected(checker, function, stack[frame_index].arg_index)
|
|
|
|
|
param_index := call_param_index(mode, prefix, stack[frame_index].arg_index)
|
|
|
|
|
arg_expected := call_arg_expected(checker, function, param_index)
|
|
|
|
|
if !is_runtime_type(checker, arg_expected) {
|
|
|
|
|
arg_expected = types.INVALID
|
|
|
|
|
}
|
|
|
|
@@ -5527,11 +6033,18 @@ build_expr :: proc(
|
|
|
|
|
if frame.stage == 3 {
|
|
|
|
|
if frame.arg_index < len(expr.args) {
|
|
|
|
|
stack[frame_index].built_args[frame.arg_index] = last
|
|
|
|
|
stack[frame_index].arg_types[frame.arg_index] = checker.module.exprs[last].type
|
|
|
|
|
next := next_built_template_arg(checker, checker.ast_module.functions[frame.template], frame.arg_index+1)
|
|
|
|
|
param_index := call_param_index(frame.arg_mode, frame.prefix, frame.arg_index)
|
|
|
|
|
if param_index < len(stack[frame_index].arg_types) {
|
|
|
|
|
stack[frame_index].arg_types[param_index] = checker.module.exprs[last].type
|
|
|
|
|
}
|
|
|
|
|
next := next_runtime_call_arg(
|
|
|
|
|
checker.ast_module.functions[frame.template], frame.arg_mode, frame.prefix,
|
|
|
|
|
frame.arg_index+1, len(expr.args),
|
|
|
|
|
)
|
|
|
|
|
stack[frame_index].arg_index = next
|
|
|
|
|
if next < len(expr.args) {
|
|
|
|
|
arg_expected := call_arg_expected(checker, checker.ast_module.functions[frame.template], next)
|
|
|
|
|
next_param := call_param_index(frame.arg_mode, frame.prefix, next)
|
|
|
|
|
arg_expected := call_arg_expected(checker, checker.ast_module.functions[frame.template], next_param)
|
|
|
|
|
if !is_runtime_type(checker, arg_expected) {
|
|
|
|
|
arg_expected = types.INVALID
|
|
|
|
|
}
|
|
|
|
@@ -5540,8 +6053,39 @@ build_expr :: proc(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
function := checker.ast_module.functions[frame.template]
|
|
|
|
|
comptime_values, comptime_ok := collect_comptime_values(checker, function, expr.args, pkg, file, diagnose=true)
|
|
|
|
|
comptime_values: []Comptime_Value
|
|
|
|
|
comptime_ok := false
|
|
|
|
|
if frame.arg_mode == .Inferred {
|
|
|
|
|
comptime_values, comptime_ok = infer_call_comptime_values(
|
|
|
|
|
checker, function, frame.prefix, expr.args, stack[frame_index].arg_types,
|
|
|
|
|
frame.expected, pkg, file, diagnose=true,
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
comptime_values, comptime_ok = collect_comptime_values(checker, function, expr.args, pkg, file, diagnose=true)
|
|
|
|
|
}
|
|
|
|
|
defer delete(comptime_values, checker.allocator)
|
|
|
|
|
if comptime_ok {
|
|
|
|
|
previous_comptime := checker.current_comptime_values
|
|
|
|
|
checker.current_comptime_values = comptime_values
|
|
|
|
|
for source_index in 0..<len(expr.args) {
|
|
|
|
|
param_index := call_param_index(frame.arg_mode, frame.prefix, source_index)
|
|
|
|
|
if param_index >= len(function.params) || function.params[param_index].comptime_value ||
|
|
|
|
|
!is_numeric_constant_expr(checker, expr.args[source_index]) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
expected_arg := call_arg_expected(checker, function, param_index)
|
|
|
|
|
if !is_runtime_type(checker, expected_arg) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
rebuilt := build_nested_expr(
|
|
|
|
|
checker, expr.args[source_index], locals, global_reads, calls,
|
|
|
|
|
expected_arg, pkg, file,
|
|
|
|
|
)
|
|
|
|
|
stack[frame_index].built_args[source_index] = rebuilt
|
|
|
|
|
stack[frame_index].arg_types[param_index] = checker.module.exprs[rebuilt].type
|
|
|
|
|
}
|
|
|
|
|
checker.current_comptime_values = previous_comptime
|
|
|
|
|
}
|
|
|
|
|
arg_violation := source.INVALID_DIAGNOSTIC
|
|
|
|
|
if comptime_ok {
|
|
|
|
|
previous_comptime := checker.current_comptime_values
|
|
|
|
@@ -5604,10 +6148,11 @@ build_expr :: proc(
|
|
|
|
|
runtime_arg_count := runtime_count + max(0, len(source_args)-len(function.params))
|
|
|
|
|
runtime_args := make([]hir.Expr_Id, runtime_arg_count, checker.allocator)
|
|
|
|
|
runtime_index := 0
|
|
|
|
|
for param, source_index in function.params {
|
|
|
|
|
for param, param_index in function.params {
|
|
|
|
|
if param.comptime_value {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
source_index := param_index if frame.arg_mode == .Explicit else param_index-frame.prefix
|
|
|
|
|
arg := source_args[source_index]
|
|
|
|
|
runtime_args[runtime_index] = coerce_expr(
|
|
|
|
|
checker,
|
|
|
|
@@ -8977,6 +9522,7 @@ check :: proc(
|
|
|
|
|
checker.anon_globals.allocator = allocator
|
|
|
|
|
checker.type_factories.allocator = allocator
|
|
|
|
|
checker.generated_types.allocator = allocator
|
|
|
|
|
checker.type_factory_origins.allocator = allocator
|
|
|
|
|
build_symbol_indexes(&checker)
|
|
|
|
|
checker.global_types = make([]types.Type, len(ast_module.globals), allocator)
|
|
|
|
|
checker.global_demands = make([]types.Type, len(ast_module.globals), allocator)
|
|
|
|
@@ -9026,8 +9572,12 @@ check :: proc(
|
|
|
|
|
for entry in checker.generated_types {
|
|
|
|
|
delete(entry.values, allocator)
|
|
|
|
|
}
|
|
|
|
|
for origin in checker.type_factory_origins {
|
|
|
|
|
delete(origin.values, allocator)
|
|
|
|
|
}
|
|
|
|
|
delete(checker.type_factories)
|
|
|
|
|
delete(checker.generated_types)
|
|
|
|
|
delete(checker.type_factory_origins)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for function, index in ast_module.functions {
|
|
|
|
|