contextual void construction

This commit is contained in:
2026-06-29 20:24:48 +02:00
parent c82f070d55
commit 98c303d22c
4 changed files with 185 additions and 8 deletions
+59
View File
@@ -1984,6 +1984,42 @@ infer_statements :: proc(
case .Defer:
deferred := [1]ast.Stmt_Id{statement.update}
infer_statements(checker, deferred[:], locals, local_types, pkg, file, demanded, result, result_hint)
case .Match:
// The build pass desugars `match` to an if/else chain, but inference runs first
// and must still visit the subject and arm bodies so calls there get specialized
// (e.g. `match get()`). Mirror the `.For`/unwrap-`.If` capture handling.
subject_type := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types)
is_tagged := types.is_tagged_union(subject_type, &checker.module.types)
for arm_id in statement.body {
arm := checker.ast_module.statements[arm_id]
if arm.kind != .Match_Arm {
continue
}
for pattern in arm.patterns {
_ = infer_expr(checker, pattern, locals^[:], pkg, file, demanded, local_types)
}
capture_start := len(locals^)
if len(arm.captures) > 0 && is_tagged && len(arm.patterns) > 0 {
capture := arm.captures[0]
if capture != checker.sink_symbol {
capture_type := types.INVALID
pattern := checker.ast_module.exprs[arm.patterns[0]]
if pattern.kind == .Enum_Literal {
if _, field, ok := find_struct_field(checker, subject_type, pattern.name); ok {
capture_type = field.type
if arm.pointer_capture {
// Mutability is best-effort here; the build pass finalizes
// the exact pointer type and coerces the captured value.
capture_type = types.pointer(&checker.module.types, field.type, true, false)
}
}
}
append(locals, Infer_Local{name=capture, type=capture_type, declared=capture_type, statement=ast.INVALID_STMT})
}
}
infer_statements(checker, arm.body, locals, local_types, pkg, file, demanded, result, result_hint)
resize(locals, capture_start)
}
}
}
resize(locals, scope_start)
@@ -3101,6 +3137,29 @@ build_compound_expr :: proc(
)
return invalid_hir_expr(checker, expr.span, id, expected)
case .Enum_Literal:
// A bare enum literal in a tagged-union context constructs a variant. Only a
// void-payload variant can be built this way (it has no value); a payload variant
// must use `T{ variant = ... }`. (`.variant{...}` payload construction is the
// milestone-23 error-channel form.)
if types.is_tagged_union(expected, store) {
index, field, found := find_struct_field(checker, expected, expr.name)
if !found {
id := source.addf(checker.diagnostics, expr.span, "unknown variant '.%s' on '%s'", symbol_text(checker, expr.name), type_label(checker, expected))
return invalid_hir_expr(checker, expr.span, id, expected)
}
if !types.is_void(field.type) {
id := source.addf(checker.diagnostics, expr.span, "variant '.%s' on '%s' needs a payload; only void variants can be built from a bare '.%s'",
symbol_text(checker, expr.name), type_label(checker, expected), symbol_text(checker, expr.name))
return invalid_hir_expr(checker, expr.span, id, expected)
}
values := make([]hir.Expr_Id, 1, checker.allocator)
values[0] = hir.INVALID_EXPR
return add_hir_expr(checker, hir.Expr{
kind=.Struct, span=expr.span, type=expected, args=values, integer=i64(index),
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
if !types.is_enum(expected, store) {
id := source.addf(
checker.diagnostics,