add constcast and immutable free

This commit is contained in:
2026-07-22 00:44:35 +02:00
parent 5f343ad2d3
commit 9c6215776e
12 changed files with 256 additions and 11 deletions
+39
View File
@@ -5187,6 +5187,16 @@ infer_expr :: proc(
_ = pop(&stack)
continue
}
if is_intrinsic_call(checker, expr, "constcast") {
if len(expr.args) == 1 {
operand := infer_nested_expr(checker, expr.args[0], locals, pkg, file, demanded, local_types)
last, _ = types.restore_mutability(&checker.module.types, operand)
} else {
last = types.INVALID
}
_ = pop(&stack)
continue
}
if is_intrinsic_call(checker, expr, "compile_error") {
last = types.VOID
_ = pop(&stack)
@@ -9056,6 +9066,17 @@ build_expr :: proc(
append(&stack, Build_Expr_Frame{expr=expr.args[1], expected=types.INVALID, template=ast.INVALID_FUNCTION})
continue
}
if is_intrinsic_call(checker, expr, "constcast") {
if len(expr.args) != 1 {
id := source.addf(checker.diagnostics, expr.span, "constcast! expects 1 argument, got %d", len(expr.args))
last = invalid_hir_expr(checker, expr.span, id)
_ = pop(&stack)
continue
}
stack[frame_index].stage = 10
append(&stack, Build_Expr_Frame{expr=expr.args[0], expected=types.INVALID, template=ast.INVALID_FUNCTION})
continue
}
if is_intrinsic_call(checker, expr, "compile_error") {
message := "compile_error! requires one comptime string argument"
if len(expr.args) == 1 {
@@ -9716,6 +9737,24 @@ build_expr :: proc(
}
_ = pop(&stack)
}
if frame.stage == 10 {
result, ok := types.restore_mutability(&checker.module.types, checker.module.exprs[last].type)
if !ok {
id := source.add(checker.diagnostics, expr.span, "constcast! operand must be a pointer, optional pointer, or slice")
last = invalid_hir_expr(checker, expr.span, id)
} else {
last = add_hir_expr(checker, hir.Expr{
kind=.Const_Cast,
span=expr.span,
type=result,
left=last,
target=hir.INVALID_REF,
right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
_ = pop(&stack)
}
}
return last
}
+3
View File
@@ -3243,6 +3243,9 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
if builtin := memory_builtin_call(checker, expr); builtin != .None {
return ct_eval_memory_call(state, expr, builtin, depth+1)
}
if is_intrinsic_call(checker, expr, "constcast") {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "constcast! is not available during comptime evaluation")
}
if expr.intrinsic {
if symbol.is_valid(expr.qualifier) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "intrinsic calls must be unqualified")
+1
View File
@@ -107,6 +107,7 @@ Expr_Kind :: enum u8 {
Retype,
Scalar_Cast,
Pointer_Cast,
Const_Cast,
Weaken_Pointer,
Weaken_Slice,
Decay_Array_Pointer,
+1
View File
@@ -102,6 +102,7 @@ Opcode :: enum u8 {
Retype,
Scalar_Cast,
Pointer_Cast,
Const_Cast,
Weaken_Pointer,
Weaken_Slice,
Decay_Array_Pointer,
+11 -1
View File
@@ -287,7 +287,7 @@ valid_value :: proc(
.Load_Global, .Function_Address, .Address_Of, .Load, .Union_Tag, .Slice, .Length, .Slice_Ptr,
.Fallible_Error, .Extract, .Select, .Unwrap,
.Optional_Is_Some, .Optional_Value, .Orelse,
.Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
.Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Const_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
.Neg_Checked, .Add_Checked, .Sub_Checked, .Mul_Checked, .Div_Checked,
.Div_Trunc_Checked, .Div_Floor_Checked, .Div_Exact_Checked, .Div_Ceil_Checked,
.Rem_Checked, .Mod_Checked,
@@ -1930,6 +1930,16 @@ emit_instruction_stream :: proc(
continue
}
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr %%v%d, ptr null\n", instruction_index, instruction.a)
case .Const_Cast:
if !valid_instruction(instructions, instruction.a) ||
!types.same_constcast_shape(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid const cast operand")
continue
}
type_name := llvm_type(instruction.type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, %s ", instruction_index, type_name)
write_operand(&emitter.builder, instructions, instruction.a, instructions[instruction.a].type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", %s zeroinitializer\n", type_name)
case .Weaken_Slice:
if !valid_instruction(instructions, instruction.a) ||
!types.can_weaken_slice(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
+2 -1
View File
@@ -768,7 +768,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
})
}
_ = pop(&stack)
case .Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer:
case .Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Const_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer:
stack[frame_index].stage = 1
append(&stack, Lower_Expr_Frame{expr=expr.left})
case .Negate, .Bit_Not:
@@ -837,6 +837,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
case .Retype: op = .Retype
case .Scalar_Cast: op = .Scalar_Cast
case .Pointer_Cast: op = .Pointer_Cast
case .Const_Cast: op = .Const_Cast
case: op = .Widen
}
last = append_instruction(state, ir.Instruction{
+39
View File
@@ -1340,6 +1340,45 @@ replace_pointer_child :: proc(store: ^Store, value, child: Type) -> (Type, bool)
return intern(store, item), true
}
restore_mutability :: proc(store: ^Store, value: Type) -> (Type, bool) {
item, ok := node(store, resolve_alias(value, store))
if !ok {
return INVALID, false
}
if item.kind == .Optional {
restored, restored_ok := restore_mutability(store, item.child)
if !restored_ok || !is_pointer(restored, store) {
return INVALID, false
}
return optional(store, restored), true
}
if item.kind != .Pointer && item.kind != .Slice {
return INVALID, false
}
item.mutable = true
return intern(store, item), true
}
same_constcast_shape :: proc(from, to: Type, store: ^Store) -> bool {
from_item, from_ok := node(store, resolve_alias(from, store))
to_item, to_ok := node(store, resolve_alias(to, store))
if !from_ok || !to_ok {
return false
}
if from_item.kind == .Optional || to_item.kind == .Optional {
return from_item.kind == .Optional && to_item.kind == .Optional &&
is_pointer(from_item.child, store) && is_pointer(to_item.child, store) &&
same_constcast_shape(from_item.child, to_item.child, store)
}
return (from_item.kind == .Pointer || from_item.kind == .Slice) &&
from_item.kind == to_item.kind &&
from_item.child == to_item.child &&
from_item.many == to_item.many &&
to_item.mutable &&
from_item.has_sentinel == to_item.has_sentinel &&
(!from_item.has_sentinel || from_item.sentinel == to_item.sentinel)
}
same_pointer_shape :: proc(left, right: Type, store: ^Store) -> bool {
left_item, left_ok := node(store, left)
right_item, right_ok := node(store, right)