allocation related primitives
This commit is contained in:
@@ -277,6 +277,26 @@ is_ptr_cast_call :: proc(checker: ^Checker, expr: ast.Expr) -> bool {
|
||||
symbol_text(checker, expr.name) == "ptr_cast"
|
||||
}
|
||||
|
||||
Layout_Builtin :: enum u8 {
|
||||
None,
|
||||
Size_Of,
|
||||
Align_Of,
|
||||
}
|
||||
|
||||
layout_builtin_call :: proc(checker: ^Checker, expr: ast.Expr) -> Layout_Builtin {
|
||||
if expr.kind != .Call || expr.left != ast.INVALID_EXPR || symbol.is_valid(expr.qualifier) {
|
||||
return .None
|
||||
}
|
||||
name := symbol_text(checker, expr.name)
|
||||
if name == "size_of" {
|
||||
return .Size_Of
|
||||
}
|
||||
if name == "align_of" {
|
||||
return .Align_Of
|
||||
}
|
||||
return .None
|
||||
}
|
||||
|
||||
valid_ptr_cast_child :: proc(checker: ^Checker, value: types.Type) -> bool {
|
||||
return types.is_valid(value) &&
|
||||
!types.is_void(value) &&
|
||||
@@ -286,6 +306,49 @@ valid_ptr_cast_child :: proc(checker: ^Checker, value: types.Type) -> bool {
|
||||
types.is_opaque_struct(value, &checker.module.types))
|
||||
}
|
||||
|
||||
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 {
|
||||
#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:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
build_layout_builtin :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr,
|
||||
kind: Layout_Builtin,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
) -> hir.Expr_Id {
|
||||
if len(expr.args) != 1 {
|
||||
id := source.addf(checker.diagnostics, expr.span, "%s expects 1 argument, got %d", symbol_text(checker, expr.name), len(expr.args))
|
||||
return invalid_hir_expr(checker, expr.span, id, types.USIZE)
|
||||
}
|
||||
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")
|
||||
return invalid_hir_expr(checker, expr.span, id, types.USIZE)
|
||||
}
|
||||
if !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)
|
||||
}
|
||||
return build_constant_expr(
|
||||
checker,
|
||||
expr,
|
||||
Constant{kind=.Value, value=layout_builtin_value(checker, kind, target)},
|
||||
types.USIZE,
|
||||
)
|
||||
}
|
||||
|
||||
is_type_metatype_syntax :: proc(checker: ^Checker, value: ast.Type_Syntax) -> bool {
|
||||
item, ok := types.node(&checker.module.types, value)
|
||||
return ok && item.name == u32(checker.type_symbol) && item.qualifier == 0
|
||||
@@ -1828,7 +1891,7 @@ infer_compound_expr :: proc(
|
||||
case .Slice:
|
||||
value := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types)
|
||||
item, ok := types.container(value, store)
|
||||
if !ok || item.kind == .Pointer {
|
||||
if !ok || (item.kind == .Pointer && expr.args[1] == ast.INVALID_EXPR) {
|
||||
return types.INVALID
|
||||
}
|
||||
for bound in expr.args {
|
||||
@@ -2098,6 +2161,11 @@ infer_expr :: proc(
|
||||
}
|
||||
continue
|
||||
}
|
||||
if builtin := layout_builtin_call(checker, expr); builtin != .None {
|
||||
last = types.USIZE
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if is_ptr_cast_call(checker, expr) {
|
||||
if len(expr.args) != 2 {
|
||||
last = types.INVALID
|
||||
@@ -3080,6 +3148,11 @@ 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
|
||||
}
|
||||
constant := eval_integer_constant_in_context(checker, global.expr, global.pkg, global.file)
|
||||
if constant.kind == .Value && fits_i64(constant.value) {
|
||||
checker.global_open_const[index] = true
|
||||
@@ -4227,8 +4300,12 @@ build_compound_expr :: proc(
|
||||
container := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
container_type := checker.module.exprs[container].type
|
||||
item, ok := types.container(container_type, store)
|
||||
if !ok || item.kind == .Pointer {
|
||||
id := source.add(checker.diagnostics, expr.span, "slicing requires an array, slice, or pointer-to-array")
|
||||
if !ok {
|
||||
id := source.add(checker.diagnostics, expr.span, "slicing requires an array, slice, pointer-to-array, or many-item pointer")
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
if item.kind == .Pointer && expr.args[1] == ast.INVALID_EXPR {
|
||||
id := source.add(checker.diagnostics, expr.span, "many-item pointer slicing requires an explicit end bound")
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
bounds := make([]hir.Expr_Id, 2, checker.allocator)
|
||||
@@ -4881,6 +4958,11 @@ 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)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if is_ptr_cast_call(checker, expr) {
|
||||
if len(expr.args) != 2 {
|
||||
id := source.addf(checker.diagnostics, expr.span, "ptr_cast expects 2 arguments, got %d", len(expr.args))
|
||||
|
||||
@@ -1909,6 +1909,19 @@ 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 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")
|
||||
}
|
||||
if !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
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, state.pkg, state.file, false)
|
||||
if !available {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "unavailable function package")
|
||||
|
||||
+13
-3
@@ -1114,7 +1114,7 @@ emit_instruction_stream :: proc(
|
||||
}
|
||||
container := instructions[instruction.a]
|
||||
item, ok := types.container(container.type, &emitter.module.types)
|
||||
if !ok || (item.kind != .Array && item.kind != .Slice) {
|
||||
if !ok || (item.kind != .Array && item.kind != .Slice && item.kind != .Pointer) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid slice container")
|
||||
continue
|
||||
}
|
||||
@@ -1132,6 +1132,12 @@ emit_instruction_stream :: proc(
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_len%d = extractvalue %s %%v%d, 1\n", instruction_index, llvm_type(container.type, &emitter.module.types), instruction.a)
|
||||
pointer_name = fmt.tprintf("%%slice_ptr%d", instruction_index)
|
||||
length_name = fmt.tprintf("%%slice_len%d", instruction_index)
|
||||
} else if item.kind == .Pointer {
|
||||
if len(instruction.args) <= 1 || instruction.args[1] == ir.INVALID_INSTRUCTION {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid many-item pointer slice")
|
||||
continue
|
||||
}
|
||||
length_name = "0"
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_bound_start%d = add i64 0, ", instruction_index)
|
||||
if len(instruction.args) > 0 && instruction.args[0] != ir.INVALID_INSTRUCTION {
|
||||
@@ -1150,8 +1156,12 @@ emit_instruction_stream :: proc(
|
||||
start_name := fmt.tprintf("%%slice_bound_start%d", instruction_index)
|
||||
end_name := fmt.tprintf("%%slice_bound_end%d", instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_order%d = icmp ule i64 %s, %s\n", instruction_index, start_name, end_name)
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_end_ok%d = icmp ule i64 %s, %s\n", instruction_index, end_name, length_name)
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_ok%d = and i1 %%slice_order%d, %%slice_end_ok%d\n", instruction_index, instruction_index, instruction_index)
|
||||
if item.kind == .Pointer {
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_ok%d = or i1 false, %%slice_order%d\n", instruction_index, instruction_index)
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_end_ok%d = icmp ule i64 %s, %s\n", instruction_index, end_name, length_name)
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_ok%d = and i1 %%slice_order%d, %%slice_end_ok%d\n", instruction_index, instruction_index, instruction_index)
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " br i1 %%slice_ok%d, label %%slice_continue%d, label %%slice_trap%d\nslice_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
||||
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "slice bounds out of range")
|
||||
emit_trap_call(emitter, message)
|
||||
|
||||
@@ -653,6 +653,17 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Question, .At, .Star:
|
||||
start := tok
|
||||
target := parse_type_atom(parser)
|
||||
return add_expr(parser, ast.Expr{
|
||||
kind=.Type,
|
||||
span=span_from(start.span, previous(parser).span),
|
||||
type=target,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Integer:
|
||||
advance(parser)
|
||||
value, ok := parse_integer_magnitude(token_text(parser, tok))
|
||||
@@ -739,6 +750,18 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
case .Keyword_Func:
|
||||
return parse_function_literal(parser)
|
||||
case .Left_Bracket:
|
||||
if starts_declared_type(parser) {
|
||||
start := tok
|
||||
target := parse_type_atom(parser)
|
||||
return add_expr(parser, ast.Expr{
|
||||
kind=.Type,
|
||||
span=span_from(start.span, previous(parser).span),
|
||||
type=target,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
return parse_array_literal(parser, nesting)
|
||||
case .Dot:
|
||||
start := advance(parser)
|
||||
|
||||
Reference in New Issue
Block a user