distinct type aliasing

This commit is contained in:
2026-06-23 12:29:21 +02:00
parent 6512ccd543
commit f16f352d1e
15 changed files with 525 additions and 58 deletions
+127 -24
View File
@@ -870,6 +870,15 @@ validate_declarations :: proc(checker: ^Checker) {
validate_type_nodes :: proc(checker: ^Checker) {
for item, index in checker.module.types.nodes {
id := types.DYNAMIC_START+types.Type(index)
if item.kind == .Distinct &&
(!item.declared || !types.is_runtime_value(item.child, &checker.module.types)) {
source.addf(
checker.diagnostics,
source.Span{},
"distinct type '%s' requires a concrete runtime backing type",
symbol_text(checker, symbol.Id(item.name)),
)
}
if item.has_sentinel {
value := i128(item.sentinel)
if types.is_signed(item.child, checker.target) {
@@ -1311,6 +1320,14 @@ infer_expr :: proc(
}
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
if !ok {
distinct_type := types.find_named(&checker.module.types, u32(target_pkg), u32(expr.name))
distinct_item, distinct_ok := types.node(&checker.module.types, distinct_type)
if available && distinct_ok && distinct_item.kind == .Distinct && len(expr.args) == 1 {
stack[frame_index].left = distinct_type
stack[frame_index].stage = 7
append(&stack, Infer_Frame{expr=expr.args[0], template=ast.INVALID_FUNCTION})
continue
}
last = types.INVALID
_ = pop(&stack)
continue
@@ -1426,6 +1443,10 @@ infer_expr :: proc(
stack[frame_index].args = nil
_ = pop(&stack)
}
if frame.stage == 7 {
last = frame.left
_ = pop(&stack)
}
}
return last
}
@@ -1951,6 +1972,7 @@ build_float_expr :: proc(checker: ^Checker, expr: ast.Expr, expected: types.Type
Build_Expr_Frame :: struct {
expr: ast.Expr_Id,
expected: types.Type,
target_type: types.Type,
stage: u8,
left: hir.Expr_Id,
arg_index: int,
@@ -2701,38 +2723,81 @@ build_expr :: proc(
template := find_template(checker, expr.name, target_pkg)
if template == ast.INVALID_FUNCTION {
callee := hir.INVALID_EXPR
callee_from_global := false
non_callable := false
non_callable_global := false
if !symbol.is_valid(expr.qualifier) {
if local, ok := find_build_local(locals, expr.name); ok {
callee = add_hir_expr(checker, hir.Expr{
kind=.Local, span=expr.span, type=local.type, target=hir.local_ref(local.id),
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
})
if _, _, _, callable := types.function_pointer(local.type, &checker.module.types); callable {
callee = add_hir_expr(checker, hir.Expr{
kind=.Local, span=expr.span, type=local.type, target=hir.local_ref(local.id),
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
})
} else {
non_callable = true
}
}
}
if callee == hir.INVALID_EXPR {
if callee == hir.INVALID_EXPR && !non_callable {
if global := find_global(checker, expr.name, target_pkg); global != ast.INVALID_GLOBAL {
callee = build_global_reference(checker, global, expr.span, global_reads)
callee_from_global = true
if _, _, _, callable := types.function_pointer(checker.global_types[global], &checker.module.types); callable {
callee = build_global_reference(checker, global, expr.span, global_reads)
} else {
non_callable = true
non_callable_global = true
}
}
}
if callee == hir.INVALID_EXPR {
id := add_unsupported_diagnostic(checker, expr.span, target_pkg, expr.name)
if id == source.INVALID_DIAGNOSTIC {
id = add_call_resolution_diagnostic(checker, expr, target_pkg)
distinct_type := types.find_named(&checker.module.types, u32(target_pkg), u32(expr.name))
distinct_item, distinct_ok := types.node(&checker.module.types, distinct_type)
if distinct_ok && distinct_item.kind == .Distinct {
if !is_runtime_type(checker, distinct_type) {
id := source.addf(
checker.diagnostics,
expr.span,
"distinct type '%s' has no concrete runtime backing type",
symbol_text(checker, expr.name),
)
last = invalid_hir_expr(checker, expr.span, id)
_ = pop(&stack)
continue
}
if len(expr.args) != 1 {
id := source.addf(
checker.diagnostics,
expr.span,
"distinct type '%s' expects 1 argument, got %d",
symbol_text(checker, expr.name),
len(expr.args),
)
last = invalid_hir_expr(checker, expr.span, id, distinct_type)
_ = pop(&stack)
continue
}
stack[frame_index].target_type = distinct_type
stack[frame_index].stage = 8
append(&stack, Build_Expr_Frame{
expr=expr.args[0],
expected=distinct_item.child,
template=ast.INVALID_FUNCTION,
})
continue
}
id := source.INVALID_DIAGNOSTIC
if non_callable {
id = add_call_resolution_diagnostic(checker, expr, target_pkg) if non_callable_global else
source.add(checker.diagnostics, expr.span, "call target is not a function pointer")
} else {
id = add_unsupported_diagnostic(checker, expr.span, target_pkg, expr.name)
if id == source.INVALID_DIAGNOSTIC {
id = add_call_resolution_diagnostic(checker, expr, target_pkg)
}
}
last = invalid_hir_expr(checker, expr.span, id)
_ = pop(&stack)
continue
}
_, function_item, function_type, ok := types.function_pointer(checker.module.exprs[callee].type, &checker.module.types)
if !ok {
id := add_call_resolution_diagnostic(checker, expr, target_pkg) if callee_from_global else
source.add(checker.diagnostics, expr.span, "call target is not a function pointer")
last = invalid_hir_expr(checker, expr.span, id)
_ = pop(&stack)
continue
}
_, function_item, function_type, _ := types.function_pointer(checker.module.exprs[callee].type, &checker.module.types)
if !valid_callable_arity(function_item, len(expr.args)) {
message := "function pointer expects at least %d arguments, got %d" if function_item.variadic else
"function pointer expects %d arguments, got %d"
@@ -2999,6 +3064,32 @@ build_expr :: proc(
}
_ = pop(&stack)
}
if frame.stage == 8 {
distinct_item, ok := types.node(&checker.module.types, frame.target_type)
actual := checker.module.exprs[last].type
if !ok || distinct_item.kind != .Distinct || !types.equal(actual, distinct_item.child) {
id := source.addf(
checker.diagnostics,
expr.span,
"distinct type '%s' requires an exact %s value, got %s",
symbol_text(checker, expr.name),
types.name(distinct_item.child),
types.name(actual),
)
last = invalid_hir_expr(checker, expr.span, id, frame.target_type)
} else {
last = add_hir_expr(checker, hir.Expr{
kind=.Retype,
span=expr.span,
type=frame.target_type,
left=last,
target=hir.INVALID_REF,
right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
_ = pop(&stack)
}
}
return last
}
@@ -3783,6 +3874,21 @@ expr_problematic :: proc(checker: ^Checker, expr_id: hir.Expr_Id) -> bool {
return false
}
static_integer_value :: proc(module: ^hir.Module, expr_id: hir.Expr_Id) -> (i64, bool) {
current := expr_id
for current != hir.INVALID_EXPR && int(current) < len(module.exprs) {
expr := module.exprs[current]
if expr.kind == .Integer {
return expr.integer, true
}
if expr.kind != .Retype {
break
}
current = expr.left
}
return 0, false
}
build_globals :: proc(checker: ^Checker) {
for global, global_index in checker.ast_module.globals {
if global.external {
@@ -3864,11 +3970,8 @@ build_globals :: proc(checker: ^Checker) {
)
expr = invalid_hir_expr(checker, global.span, diagnostic)
}
is_static := checker.module.exprs[expr].kind == .Integer && diagnostic == source.INVALID_DIAGNOSTIC
static_value: i64
if is_static {
static_value = checker.module.exprs[expr].integer
}
static_value, is_static := static_integer_value(&checker.module, expr)
is_static = is_static && diagnostic == source.INVALID_DIAGNOSTIC
_ = hir.global_id(len(checker.module.globals))
append(
&checker.module.globals,