feat: contextually type empty mutable slice literals
This commit is contained in:
@@ -1488,6 +1488,15 @@ is_numeric_constant_expr :: proc(checker: ^Checker, expr_id: ast.Expr_Id) -> boo
|
||||
return eval_constant(checker, expr_id).kind == .Value || is_float_constant_expr(checker, expr_id)
|
||||
}
|
||||
|
||||
is_empty_array_address :: proc(checker: ^Checker, expr: ast.Expr) -> bool {
|
||||
if expr.kind != .Address || expr.left == ast.INVALID_EXPR ||
|
||||
int(expr.left) >= len(checker.ast_module.exprs) {
|
||||
return false
|
||||
}
|
||||
operand := checker.ast_module.exprs[expr.left]
|
||||
return operand.kind == .Array && len(operand.args) == 0
|
||||
}
|
||||
|
||||
is_typed_integer_fold_candidate :: proc(checker: ^Checker, expr_id: ast.Expr_Id, depth := 0) -> bool {
|
||||
if depth > 64 || expr_id == ast.INVALID_EXPR || int(expr_id) >= len(checker.ast_module.exprs) {
|
||||
return false
|
||||
@@ -3152,8 +3161,9 @@ infer_call_comptime_values :: proc(
|
||||
}
|
||||
arg_expr := checker.ast_module.exprs[arg_id]
|
||||
is_null := arg_expr.kind == .Null
|
||||
// An empty literal supplies no element evidence ahead of concrete arguments.
|
||||
is_weak := is_numeric_constant_expr(checker, arg_id) ||
|
||||
arg_expr.kind == .String || is_null
|
||||
arg_expr.kind == .String || is_null || is_empty_array_address(checker, arg_expr)
|
||||
contains_type_call := type_pattern_contains_type_call(
|
||||
checker, function.params[param_index].type,
|
||||
)
|
||||
@@ -5220,6 +5230,13 @@ infer_compound_expr :: proc(
|
||||
_ = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
return type_from_syntax(checker, expr.type, pkg, file)
|
||||
case .Address:
|
||||
if is_empty_array_address(checker, expr) {
|
||||
if node, ok := types.node(store, expected);
|
||||
ok && node.kind == .Slice && node.mutable && !node.has_sentinel {
|
||||
child := types.array(store, node.child, 0, true)
|
||||
return types.pointer(store, child, true, false)
|
||||
}
|
||||
}
|
||||
child := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
return types.pointer(store, child, false, false)
|
||||
case .Deref:
|
||||
@@ -8758,11 +8775,9 @@ build_compound_expr :: proc(
|
||||
value := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
return build_scalar_cast(checker, value, target, expr.span)
|
||||
case .Address:
|
||||
// `&<array literal>` (Zig's `&.{...}`): the operand is an rvalue with no
|
||||
// address, so promote it to an anonymous global constant and take *its*
|
||||
// address. Reuses the existing non-scalar-global storage path; only the
|
||||
// stable global address enters the expression, so it never dangles. The
|
||||
// resulting `*[N]T` then decays to a slice via the usual coercion.
|
||||
// Promote array literals to anonymous globals so their addresses never
|
||||
// dangle. Only a fresh empty literal may acquire mutable slice context;
|
||||
// ordinary literals retain readonly storage and the usual decay rules.
|
||||
if expr.left != ast.INVALID_EXPR && checker.ast_module.exprs[expr.left].kind == .Array {
|
||||
operand := checker.ast_module.exprs[expr.left]
|
||||
// Propagate an element-expected type through `&` so literal elements
|
||||
@@ -8770,12 +8785,15 @@ build_compound_expr :: proc(
|
||||
// Without this, `&["x"]` infers `*[1]*[N:0]u8`, which won't decay to
|
||||
// `[][]u8` because can_decay_array_pointer requires child equality.
|
||||
element := types.INVALID
|
||||
mutable := false
|
||||
if node, ok := types.node(store, expected); ok && (node.kind == .Slice || node.kind == .Array) {
|
||||
element = node.child
|
||||
mutable = len(operand.args) == 0 && node.kind == .Slice &&
|
||||
node.mutable && !node.has_sentinel
|
||||
}
|
||||
synth_expected := types.INVALID
|
||||
if types.is_valid(element) {
|
||||
synth_expected = types.array(store, element, u64(len(operand.args)), false)
|
||||
synth_expected = types.array(store, element, u64(len(operand.args)), mutable)
|
||||
}
|
||||
value := build_nested_expr(checker, expr.left, locals, global_reads, calls, synth_expected, pkg, file)
|
||||
array_type := checker.module.exprs[value].type
|
||||
@@ -8784,7 +8802,7 @@ build_compound_expr :: proc(
|
||||
name = symbol.intern(checker.symbols, "__anon.array"),
|
||||
type = array_type,
|
||||
expr = value,
|
||||
writable = false,
|
||||
writable = mutable,
|
||||
external = false,
|
||||
diagnostic = source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
@@ -8794,7 +8812,7 @@ build_compound_expr :: proc(
|
||||
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Address, span=expr.span, type=types.pointer(store, array_type, false, false),
|
||||
kind=.Address, span=expr.span, type=types.pointer(store, array_type, mutable, false),
|
||||
left=global_ref, target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
@@ -10389,14 +10407,21 @@ build_expr :: proc(
|
||||
checker.current_comptime_values = comptime_values
|
||||
for source_index in 0..<len(expr.args) {
|
||||
param_index := call_param_index(frame.mapping, source_index)
|
||||
empty_array := is_empty_array_address(checker, checker.ast_module.exprs[expr.args[source_index]])
|
||||
if param_index >= len(function.params) || function.params[param_index].comptime_value ||
|
||||
!is_numeric_constant_expr(checker, expr.args[source_index]) {
|
||||
(!is_numeric_constant_expr(checker, expr.args[source_index]) && !empty_array) {
|
||||
continue
|
||||
}
|
||||
expected_arg := call_arg_expected(checker, function, param_index)
|
||||
if !is_runtime_type(checker, expected_arg) {
|
||||
continue
|
||||
}
|
||||
if empty_array && types.can_decay_array_pointer(
|
||||
checker.module.exprs[stack[frame_index].built_args[source_index]].type,
|
||||
expected_arg, &checker.module.types,
|
||||
) {
|
||||
continue
|
||||
}
|
||||
rebuilt := build_nested_expr(
|
||||
checker, expr.args[source_index], locals, global_reads, calls,
|
||||
expected_arg, pkg, file,
|
||||
|
||||
Reference in New Issue
Block a user