default struct fields

This commit is contained in:
2026-07-18 16:23:47 +02:00
parent e889a99e55
commit 9eb7476522
10 changed files with 191 additions and 11 deletions
+67
View File
@@ -4471,12 +4471,19 @@ infer_compound_expr :: proc(
}
return value
}
fields := types.fields_for(store, value)
item, item_ok := types.node(store, value)
initialized := make([]bool, len(fields), checker.allocator)
defer delete(initialized, checker.allocator)
for keyed in expr.args {
keyed_expr := checker.ast_module.exprs[keyed]
if keyed_expr.left == ast.INVALID_EXPR {
continue
}
slot, field, ok := find_struct_field_slot(checker, value, keyed_expr.name)
if ok && item_ok {
initialized[slot-int(item.field_start)] = true
}
if !ok || !is_inferred_record_field(checker, slot) {
_ = infer_nested_expr(checker, keyed_expr.left, locals, pkg, file, demanded, local_types)
continue
@@ -4491,6 +4498,19 @@ infer_compound_expr :: proc(
)
}
}
if item_ok && item.kind == .Struct && !item.tuple {
for field, index in fields {
if initialized[index] {
continue
}
if field_default, ok := find_struct_field_default(checker, value, symbol.Id(field.name)); ok {
_ = infer_nested_expr(
checker, field_default.expr, nil, field_default.pkg, field_default.file,
demanded, expected=field.type,
)
}
}
}
return value
case .Keyed:
return infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
@@ -6098,6 +6118,27 @@ infer_all :: proc(checker: ^Checker) {
checker.record_field_demands_dirty = false
spec_count := len(checker.specs)
for field_default in checker.ast_module.struct_field_defaults {
slot, field, ok := find_struct_field_slot(checker, field_default.record, field_default.field)
if !ok {
continue
}
expected := field.type if is_runtime_type(checker, field.type) else types.INVALID
inferred := infer_expr(
checker, field_default.expr, nil, field_default.pkg, field_default.file,
expected=expected,
)
_ = record_field_expr_candidate(
checker, slot, field_default.expr, inferred, nil, field_default.pkg, field_default.file,
)
if is_runtime_type(checker, checker.module.types.fields[slot].type) {
_ = record_demand(
checker, field_default.expr, checker.module.types.fields[slot].type,
nil, nil, field_default.pkg, field_default.file,
)
}
}
// Backward demands: a global pushes its own (declared or already-resolved) type
// onto open numeric slots reachable through names and numeric arithmetic.
for global, index in checker.ast_module.globals {
@@ -6753,6 +6794,21 @@ find_struct_field :: proc(checker: ^Checker, struct_type: types.Type, name: symb
return 0, {}, false
}
find_struct_field_default :: proc(
checker: ^Checker,
struct_type: types.Type,
name: symbol.Id,
) -> (ast.Struct_Field_Default, bool) {
resolved := types.resolve_alias(struct_type, &checker.module.types)
for field_default in checker.ast_module.struct_field_defaults {
if types.resolve_alias(field_default.record, &checker.module.types) == resolved &&
field_default.field == name {
return field_default, true
}
}
return {}, false
}
find_tuple_field :: proc(checker: ^Checker, tuple_type: types.Type, index: u64) -> (int, types.Field, bool) {
item, ok := types.node(&checker.module.types, tuple_type)
if !ok || item.kind != .Struct || !item.tuple || index >= u64(item.field_count) {
@@ -7895,6 +7951,17 @@ build_compound_expr :: proc(
if values[index] != hir.INVALID_EXPR {
continue
}
if field_default, ok := find_struct_field_default(checker, struct_type, symbol.Id(field.name)); ok {
values[index] = build_nested_expr(
checker, field_default.expr, nil, global_reads, calls,
field.type, field_default.pkg, field_default.file,
)
values[index] = coerce_expr(
checker, values[index], field.type,
checker.ast_module.exprs[field_default.expr].span,
)
continue
}
id := source.addf(checker.diagnostics, expr.span, "missing initializer for struct field '%s'", symbol_text(checker, symbol.Id(field.name)))
delete(values, checker.allocator)
return invalid_hir_expr(checker, expr.span, id, struct_type)