typed alloc

This commit is contained in:
2026-07-12 00:29:22 +02:00
parent 220b1c6e82
commit fbbbfa454c
6 changed files with 328 additions and 66 deletions
+56 -17
View File
@@ -281,13 +281,15 @@ is_ptr_cast_call :: proc(checker: ^Checker, expr: ast.Expr) -> bool {
symbol_text(checker, expr.name) == "ptr_cast"
}
Layout_Builtin :: enum u8 {
Type_Builtin :: enum u8 {
None,
Size_Of,
Align_Of,
Min_Value,
Max_Value,
}
layout_builtin_call :: proc(checker: ^Checker, expr: ast.Expr) -> Layout_Builtin {
type_builtin_call :: proc(checker: ^Checker, expr: ast.Expr) -> Type_Builtin {
if expr.kind != .Call || expr.left != ast.INVALID_EXPR || symbol.is_valid(expr.qualifier) {
return .None
}
@@ -298,6 +300,12 @@ layout_builtin_call :: proc(checker: ^Checker, expr: ast.Expr) -> Layout_Builtin
if name == "align_of" {
return .Align_Of
}
if name == "min_value" {
return .Min_Value
}
if name == "max_value" {
return .Max_Value
}
return .None
}
@@ -314,21 +322,30 @@ valid_layout_type :: proc(checker: ^Checker, value: types.Type) -> bool {
return types.is_runtime_value(value, &checker.module.types)
}
layout_builtin_value :: proc(checker: ^Checker, kind: Layout_Builtin, value: types.Type) -> i128 {
type_builtin_value :: proc(checker: ^Checker, kind: Type_Builtin, value: types.Type) -> i128 {
#partial switch kind {
case .Size_Of:
return i128(types.size(value, &checker.module.types, checker.target))
case .Align_Of:
return i128(types.alignment_of(value, &checker.module.types, checker.target))
case .Min_Value:
if types.is_unsigned(value, checker.target) {
return 0
}
return -(i128(1) << u32(types.bits(value, checker.target)-1))
case .Max_Value:
bit_count := types.bits(value, checker.target)
sign_bit_count := 1 if types.is_signed(value, checker.target) else 0
return (i128(1) << u32(bit_count-sign_bit_count))-1
case:
return 0
}
}
build_layout_builtin :: proc(
build_type_builtin :: proc(
checker: ^Checker,
expr: ast.Expr,
kind: Layout_Builtin,
kind: Type_Builtin,
pkg: ast.Package_Id,
file: ast.File_Id,
) -> hir.Expr_Id {
@@ -338,18 +355,24 @@ build_layout_builtin :: proc(
}
target, target_ok := resolve_type_argument(checker, expr.args[0], pkg, file)
if !target_ok {
id := source.add(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "layout target must be a type")
label := "layout" if kind == .Size_Of || kind == .Align_Of else "integer bound"
id := source.addf(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "%s target must be a type", label)
return invalid_hir_expr(checker, expr.span, id, types.USIZE)
}
if !valid_layout_type(checker, target) {
if (kind == .Size_Of || kind == .Align_Of) && !valid_layout_type(checker, target) {
id := source.addf(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "layout target must be a sized runtime value type, got %s", type_label(checker, target))
return invalid_hir_expr(checker, expr.span, id, types.USIZE)
}
if (kind == .Min_Value || kind == .Max_Value) && !types.is_concrete_integer(target) {
id := source.addf(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "integer bound target must be a concrete integer type, got %s", type_label(checker, target))
return invalid_hir_expr(checker, expr.span, id, types.USIZE)
}
result_type := types.USIZE if kind == .Size_Of || kind == .Align_Of else target
return build_constant_expr(
checker,
expr,
Constant{kind=.Value, value=layout_builtin_value(checker, kind, target)},
types.USIZE,
Constant{kind=.Value, value=type_builtin_value(checker, kind, target)},
result_type,
)
}
@@ -2201,8 +2224,15 @@ infer_expr :: proc(
}
continue
}
if builtin := layout_builtin_call(checker, expr); builtin != .None {
last = types.USIZE
if builtin := type_builtin_call(checker, expr); builtin != .None {
if builtin == .Size_Of || builtin == .Align_Of {
last = types.USIZE
} else if len(expr.args) == 1 {
target, ok := resolve_type_argument(checker, expr.args[0], pkg, file)
last = target if ok && types.is_concrete_integer(target) else types.INVALID
} else {
last = types.INVALID
}
_ = pop(&stack)
continue
}
@@ -3188,10 +3218,19 @@ infer_all :: proc(checker: ^Checker) {
if global.external {
continue
}
if global.expr != ast.INVALID_EXPR && int(global.expr) < len(checker.ast_module.exprs) &&
layout_builtin_call(checker, checker.ast_module.exprs[global.expr]) != .None {
checker.global_types[index] = types.USIZE
continue
if global.expr != ast.INVALID_EXPR && int(global.expr) < len(checker.ast_module.exprs) {
expr := checker.ast_module.exprs[global.expr]
if builtin := type_builtin_call(checker, expr); builtin != .None {
if builtin == .Size_Of || builtin == .Align_Of {
checker.global_types[index] = types.USIZE
} else if len(expr.args) == 1 {
target, ok := resolve_type_argument(checker, expr.args[0], global.pkg, global.file)
if ok && types.is_concrete_integer(target) {
checker.global_types[index] = target
}
}
continue
}
}
constant := eval_integer_constant_in_context(checker, global.expr, global.pkg, global.file)
if constant.kind == .Value && fits_i64(constant.value) {
@@ -4998,8 +5037,8 @@ build_expr :: proc(
append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION})
continue
}
if builtin := layout_builtin_call(checker, expr); builtin != .None {
last = build_layout_builtin(checker, expr, builtin, pkg, file)
if builtin := type_builtin_call(checker, expr); builtin != .None {
last = build_type_builtin(checker, expr, builtin, pkg, file)
_ = pop(&stack)
continue
}
+9 -4
View File
@@ -1909,18 +1909,23 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
}
return ct_eval_template_call(state, ast.Function_Id(u32(state.values[callee].index)), expr.args, expr.span, expected, depth+1)
}
if builtin := layout_builtin_call(checker, expr); builtin != .None {
if builtin := type_builtin_call(checker, expr); builtin != .None {
if len(expr.args) != 1 {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "%s expects 1 argument, got %d", symbol_text(checker, expr.name), len(expr.args))
}
target, target_ok := resolve_type_argument(checker, expr.args[0], state.pkg, state.file)
if !target_ok {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, checker.ast_module.exprs[expr.args[0]].span, "layout target must be a type")
label := "layout" if builtin == .Size_Of || builtin == .Align_Of else "integer bound"
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, checker.ast_module.exprs[expr.args[0]].span, "%s target must be a type", label)
}
if !valid_layout_type(checker, target) {
if (builtin == .Size_Of || builtin == .Align_Of) && !valid_layout_type(checker, target) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, checker.ast_module.exprs[expr.args[0]].span, "layout target must be a sized runtime value type, got %s", type_label(checker, target))
}
return ct_add_value(state, Ct_Value{kind=.Integer, type=types.USIZE, integer=layout_builtin_value(checker, builtin, target)}), ct_flow(.Normal), true
if (builtin == .Min_Value || builtin == .Max_Value) && !types.is_concrete_integer(target) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, checker.ast_module.exprs[expr.args[0]].span, "integer bound target must be a concrete integer type, got %s", type_label(checker, target))
}
result_type := types.USIZE if builtin == .Size_Of || builtin == .Align_Of else target
return ct_add_value(state, Ct_Value{kind=.Integer, type=result_type, integer=type_builtin_value(checker, builtin, target)}), ct_flow(.Normal), true
}
target_pkg, available := expr_package(checker, expr, state.pkg, state.file, false)
if !available {