Pointer arithmetic builtin procedures

This commit is contained in:
Ginger Bill
2016-08-27 11:05:08 +01:00
parent 3a189b9c1c
commit ae75ab169b
6 changed files with 391 additions and 143 deletions
+10
View File
@@ -120,12 +120,18 @@ enum BuiltinProcId {
BuiltinProc_offset_of,
BuiltinProc_offset_of_val,
BuiltinProc_static_assert,
BuiltinProc_len,
BuiltinProc_cap,
BuiltinProc_copy,
BuiltinProc_append,
BuiltinProc_swizzle,
BuiltinProc_ptr_offset,
BuiltinProc_ptr_sub,
BuiltinProc_slice_ptr,
BuiltinProc_Count,
};
struct BuiltinProc {
@@ -151,6 +157,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
{STR_LIT("append"), 2, false, Expression_Expression},
{STR_LIT("swizzle"), 1, true, Expression_Expression},
{STR_LIT("ptr_offset"), 2, false, Expression_Expression},
{STR_LIT("ptr_sub"), 2, false, Expression_Expression},
{STR_LIT("slice_ptr"), 2, true, Expression_Expression},
};
struct CheckerContext {
+5 -3
View File
@@ -39,11 +39,13 @@ struct Entity {
struct { ExactValue value; } Constant;
struct {
b8 visited; // Cycle detection
b8 is_field; // Is struct field
b8 used; // Variable is used
b8 anonymous; // Variable is an anonymous struct field
b8 is_field; // Is struct field
b8 anonymous; // Variable is an anonymous
} Variable;
struct { b8 used; } Procedure;
struct {
b8 used;
} Procedure;
struct { BuiltinProcId id; } Builtin;
};
};
+303 -30
View File
@@ -1,17 +1,124 @@
void check_assignment (Checker *c, Operand *operand, Type *type, String context_name);
b32 check_is_assignable_to (Checker *c, Operand *operand, Type *type);
void check_expr (Checker *c, Operand *operand, AstNode *expression);
void check_multi_expr (Checker *c, Operand *operand, AstNode *expression);
void check_expr_or_type (Checker *c, Operand *operand, AstNode *expression);
ExpressionKind check_expr_base (Checker *c, Operand *operand, AstNode *expression, Type *type_hint = NULL);
Type * check_type (Checker *c, AstNode *expression, Type *named_type = NULL, CycleChecker *cycle_checker = NULL);
void check_selector (Checker *c, Operand *operand, AstNode *node);
void check_not_tuple (Checker *c, Operand *operand);
void convert_to_typed (Checker *c, Operand *operand, Type *target_type);
gbString expr_to_string (AstNode *expression);
void check_entity_decl (Checker *c, Entity *e, DeclInfo *decl, Type *named_type, CycleChecker *cycle_checker = NULL);
void check_proc_body (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body);
void update_expr_type (Checker *c, AstNode *e, Type *type, b32 final);
void check_expr (Checker *c, Operand *operand, AstNode *expression);
void check_multi_expr (Checker *c, Operand *operand, AstNode *expression);
void check_expr_or_type (Checker *c, Operand *operand, AstNode *expression);
ExpressionKind check_expr_base (Checker *c, Operand *operand, AstNode *expression, Type *type_hint = NULL);
Type * check_type (Checker *c, AstNode *expression, Type *named_type = NULL, CycleChecker *cycle_checker = NULL);
void check_selector (Checker *c, Operand *operand, AstNode *node);
void check_not_tuple (Checker *c, Operand *operand);
b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value);
void convert_to_typed (Checker *c, Operand *operand, Type *target_type);
gbString expr_to_string (AstNode *expression);
void check_entity_decl (Checker *c, Entity *e, DeclInfo *decl, Type *named_type, CycleChecker *cycle_checker = NULL);
void check_proc_body (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body);
void update_expr_type (Checker *c, AstNode *e, Type *type, b32 final);
b32 check_is_assignable_to_using_subtype(Checker *c, Type *dst, Type *src) {
return false;
}
b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argument = false) {
if (operand->mode == Addressing_Invalid ||
type == t_invalid) {
return true;
}
Type *s = operand->type;
if (are_types_identical(s, type))
return true;
Type *src = get_base_type(s);
Type *dst = get_base_type(type);
if (is_type_untyped(src)) {
switch (dst->kind) {
case Type_Basic:
if (operand->mode == Addressing_Constant)
return check_value_is_expressible(c, operand->value, dst, NULL);
if (src->kind == Type_Basic)
return src->Basic.kind == Basic_UntypedBool && is_type_boolean(dst);
break;
case Type_Pointer:
return src->Basic.kind == Basic_UntypedPointer;
}
}
if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src)))
return true;
if (is_type_pointer(dst) && is_type_rawptr(src))
return true;
if (is_type_rawptr(dst) && is_type_pointer(src))
return true;
if (dst->kind == Type_Array && src->kind == Type_Array) {
if (are_types_identical(dst->Array.elem, src->Array.elem)) {
return dst->Array.count == src->Array.count;
}
}
if (dst->kind == Type_Slice && src->kind == Type_Slice) {
if (are_types_identical(dst->Slice.elem, src->Slice.elem)) {
return true;
}
}
if (is_argument) {
// Polymorphism for subtyping
if (check_is_assignable_to_using_subtype(c, dst, src)) {
return true;
}
}
return false;
}
// NOTE(bill): `content_name` is for debugging
// TODO(bill): Maybe allow assignment to tuples?
void check_assignment(Checker *c, Operand *operand, Type *type, String context_name, b32 is_argument = false) {
check_not_tuple(c, operand);
if (operand->mode == Addressing_Invalid)
return;
if (is_type_untyped(operand->type)) {
Type *target_type = type;
if (type == NULL)
target_type = default_type(operand->type);
convert_to_typed(c, operand, target_type);
if (operand->mode == Addressing_Invalid)
return;
}
if (type != NULL) {
if (!check_is_assignable_to(c, operand, type, is_argument)) {
gbString type_string = type_to_string(type);
gbString op_type_string = type_to_string(operand->type);
gbString expr_str = expr_to_string(operand->expr);
defer (gb_string_free(type_string));
defer (gb_string_free(op_type_string));
defer (gb_string_free(expr_str));
// TODO(bill): is this a good enough error message?
error(&c->error_collector, ast_node_token(operand->expr),
"Cannot assign value `%s` of type `%s` to `%s` in %.*s",
expr_str,
op_type_string,
type_string,
LIT(context_name));
operand->mode = Addressing_Invalid;
}
}
}
void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *> *entity_map) {
t = get_base_type(type_deref(t));
@@ -1095,7 +1202,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
if (!are_types_identical(x->type, y->type)) {
if (x->type != t_invalid &&
y->type != t_invalid) {
y->type != t_invalid) {
gbString xt = type_to_string(x->type);
gbString yt = type_to_string(y->type);
defer (gb_string_free(xt));
@@ -1906,6 +2013,166 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
operand->type = make_type_vector(c->allocator, elem_type, arg_count);
operand->mode = Addressing_Value;
} break;
case BuiltinProc_ptr_offset: {
// ptr_offset :: proc(ptr: ^T, offset: int) -> ^T
// ^T cannot be rawptr
Type *ptr_type = get_base_type(operand->type);
if (!is_type_pointer(ptr_type)) {
gbString type_str = type_to_string(operand->type);
defer (gb_string_free(type_str));
error(&c->error_collector, ast_node_token(call),
"Expected a pointer to `ptr_offset`, got `%s`",
type_str);
return false;
}
if (ptr_type == t_rawptr) {
error(&c->error_collector, ast_node_token(call),
"`rawptr` cannot have pointer arithmetic");
return false;
}
AstNode *offset = ce->arg_list->next;
Operand op = {};
check_expr(c, &op, offset);
if (op.mode == Addressing_Invalid)
return false;
Type *offset_type = get_base_type(op.type);
if (!is_type_integer(offset_type)) {
error(&c->error_collector, ast_node_token(op.expr), "Pointer offsets for `ptr_offset` must be an integer");
return false;
}
if (operand->mode == Addressing_Constant &&
op.mode == Addressing_Constant) {
u8 *ptr = cast(u8 *)operand->value.value_pointer;
isize elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem);
ptr += elem_size * op.value.value_integer;
operand->value.value_pointer = ptr;
} else {
operand->mode = Addressing_Value;
}
} break;
case BuiltinProc_ptr_sub: {
// ptr_sub :: proc(a, b: ^T) -> int
// ^T cannot be rawptr
Type *ptr_type = get_base_type(operand->type);
if (!is_type_pointer(ptr_type)) {
gbString type_str = type_to_string(operand->type);
defer (gb_string_free(type_str));
error(&c->error_collector, ast_node_token(call),
"Expected a pointer to `ptr_add`, got `%s`",
type_str);
return false;
}
if (ptr_type == t_rawptr) {
error(&c->error_collector, ast_node_token(call),
"`rawptr` cannot have pointer arithmetic");
return false;
}
AstNode *offset = ce->arg_list->next;
Operand op = {};
check_expr(c, &op, offset);
if (op.mode == Addressing_Invalid)
return false;
if (!is_type_pointer(op.type)) {
gbString type_str = type_to_string(operand->type);
defer (gb_string_free(type_str));
error(&c->error_collector, ast_node_token(call),
"Expected a pointer to `ptr_add`, got `%s`",
type_str);
return false;
}
if (get_base_type(op.type) == t_rawptr) {
error(&c->error_collector, ast_node_token(call),
"`rawptr` cannot have pointer arithmetic");
return false;
}
if (!are_types_identical(operand->type, op.type)) {
gbString a = type_to_string(operand->type);
gbString b = type_to_string(op.type);
defer (gb_string_free(a));
defer (gb_string_free(b));
error(&c->error_collector, ast_node_token(op.expr),
"`ptr_sub` requires to pointer of the same type. Got `%s` and `%s`.", a, b);
return false;
}
operand->type = t_int;
if (operand->mode == Addressing_Constant &&
op.mode == Addressing_Constant) {
u8 *ptr_a = cast(u8 *)operand->value.value_pointer;
u8 *ptr_b = cast(u8 *)op.value.value_pointer;
isize elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem);
operand->value = make_exact_value_integer((ptr_a - ptr_b) / elem_size);
} else {
operand->mode = Addressing_Value;
}
} break;
case BuiltinProc_slice_ptr: {
// slice_ptr :: proc(a: ^T, len: int[, cap: int]) -> []T
// ^T cannot be rawptr
Type *ptr_type = get_base_type(operand->type);
if (!is_type_pointer(ptr_type)) {
gbString type_str = type_to_string(operand->type);
defer (gb_string_free(type_str));
error(&c->error_collector, ast_node_token(call),
"Expected a pointer to `ptr_add`, got `%s`",
type_str);
return false;
}
if (ptr_type == t_rawptr) {
error(&c->error_collector, ast_node_token(call),
"`rawptr` cannot have pointer arithmetic");
return false;
}
AstNode *len = ce->arg_list->next;
Operand op = {};
check_expr(c, &op, len);
if (op.mode == Addressing_Invalid)
return false;
if (!is_type_integer(op.type)) {
gbString type_str = type_to_string(operand->type);
defer (gb_string_free(type_str));
error(&c->error_collector, ast_node_token(call),
"Length for `slice_ptr` must be an integer, got `%s`",
type_str);
return false;
}
AstNode *cap = len->next;
if (cap != NULL) {
check_expr(c, &op, len);
if (op.mode == Addressing_Invalid)
return false;
if (!is_type_integer(op.type)) {
gbString type_str = type_to_string(operand->type);
defer (gb_string_free(type_str));
error(&c->error_collector, ast_node_token(call),
"Capacity for `slice_ptr` must be an integer, got `%s`",
type_str);
return false;
}
if (cap->next != NULL) {
error(&c->error_collector, ast_node_token(call),
"Too many arguments to `slice_ptr`, expected either 2 or 3");
return false;
}
}
operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem);
operand->mode = Addressing_Value;
} break;
}
return true;
@@ -1937,7 +2204,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
continue;
if (operand->type->kind != Type_Tuple) {
check_not_tuple(c, operand);
check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"));
check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"), true);
param_index++;
} else {
auto *tuple = &operand->type->Tuple;
@@ -1949,7 +2216,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
operand->type = e->type;
operand->mode = Addressing_Value;
check_not_tuple(c, operand);
check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"));
check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"), true);
}
if (i < tuple->variable_count && param_index == param_count) {
@@ -2367,11 +2634,15 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ
o->mode = Addressing_Variable;
break;
case Type_Pointer:
valid = true;
o->mode = Addressing_Variable;
o->type = get_base_type(t->Pointer.elem);
break;
case Type_Pointer: {
Type *bt = get_base_type(t->Pointer.elem);
if (bt->kind == Type_Array) {
valid = true;
max_count = bt->Array.count;
o->mode = Addressing_Variable;
o->type = bt->Array.elem;
}
} break;
}
if (!valid) {
@@ -2409,7 +2680,6 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ
max_count = o->value.value_string.len;
}
o->type = t_string;
o->mode = Addressing_Value;
}
break;
@@ -2423,19 +2693,20 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ
goto error;
}
o->type = make_type_slice(c->allocator, t->Array.elem);
o->mode = Addressing_Value;
break;
case Type_Slice:
valid = true;
o->mode = Addressing_Value;
break;
case Type_Pointer:
valid = true;
o->type = make_type_slice(c->allocator, get_base_type(t->Pointer.elem));
o->mode = Addressing_Value;
break;
case Type_Pointer: {
Type *bt = get_base_type(t->Pointer.elem);
if (bt->kind == Type_Array) {
valid = true;
max_count = bt->Array.count;
o->type = make_type_slice(c->allocator, bt->Array.elem);
}
} break;
}
if (!valid) {
@@ -2445,6 +2716,8 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ
goto error;
}
o->mode = Addressing_Value;
i64 indices[3] = {};
AstNode *nodes[3] = {se->low, se->high, se->max};
for (isize i = 0; i < gb_count_of(nodes); i++) {
-96
View File
@@ -70,102 +70,6 @@ b32 check_is_terminating(Checker *c, AstNode *node) {
return false;
}
b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type) {
if (operand->mode == Addressing_Invalid ||
type == t_invalid) {
return true;
}
Type *s = operand->type;
if (are_types_identical(s, type))
return true;
Type *sb = get_base_type(s);
Type *tb = get_base_type(type);
if (is_type_untyped(sb)) {
switch (tb->kind) {
case Type_Basic:
if (operand->mode == Addressing_Constant)
return check_value_is_expressible(c, operand->value, tb, NULL);
if (sb->kind == Type_Basic)
return sb->Basic.kind == Basic_UntypedBool && is_type_boolean(tb);
break;
case Type_Pointer:
return sb->Basic.kind == Basic_UntypedPointer;
}
}
if (are_types_identical(sb, tb) && (!is_type_named(sb) || !is_type_named(tb)))
return true;
if (is_type_pointer(sb) && is_type_rawptr(tb))
return true;
if (is_type_rawptr(sb) && is_type_pointer(tb))
return true;
if (sb->kind == Type_Array && tb->kind == Type_Array) {
if (are_types_identical(sb->Array.elem, tb->Array.elem)) {
return sb->Array.count == tb->Array.count;
}
}
if (sb->kind == Type_Slice && tb->kind == Type_Slice) {
if (are_types_identical(sb->Slice.elem, tb->Slice.elem)) {
return true;
}
}
return false;
}
// NOTE(bill): `content_name` is for debugging
// TODO(bill): Maybe allow assignment to tuples?
void check_assignment(Checker *c, Operand *operand, Type *type, String context_name) {
check_not_tuple(c, operand);
if (operand->mode == Addressing_Invalid)
return;
if (is_type_untyped(operand->type)) {
Type *target_type = type;
if (type == NULL)
target_type = default_type(operand->type);
convert_to_typed(c, operand, target_type);
if (operand->mode == Addressing_Invalid)
return;
}
if (type != NULL) {
if (!check_is_assignable_to(c, operand, type)) {
gbString type_string = type_to_string(type);
gbString op_type_string = type_to_string(operand->type);
gbString expr_str = expr_to_string(operand->expr);
defer (gb_string_free(type_string));
defer (gb_string_free(op_type_string));
defer (gb_string_free(expr_str));
// TODO(bill): is this a good enough error message?
error(&c->error_collector, ast_node_token(operand->expr),
"Cannot assign value `%s` of type `%s` to `%s` in %.*s",
expr_str,
op_type_string,
type_string,
LIT(context_name));
operand->mode = Addressing_Invalid;
}
}
}
Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
if (op_a->mode == Addressing_Invalid ||
op_a->type == t_invalid) {
+71 -14
View File
@@ -985,9 +985,9 @@ ssaValue *ssa_emit_arith(ssaProcedure *proc, Token op, ssaValue *left, ssaValue
break;
}
ssaValue *v = ssa_make_instr_binary_op(proc, op, left, right);
ssaValue *v = ssa_emit(proc, ssa_make_instr_binary_op(proc, op, left, right));
ssa_set_type(v, type);
return ssa_emit(proc, v);
return v;
}
ssaValue *ssa_emit_comp(ssaProcedure *proc, Token op, ssaValue *left, ssaValue *right) {
@@ -1696,18 +1696,23 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
// copy :: proc(dst, src: []Type) -> int
AstNode *dst_node = ce->arg_list;
AstNode *src_node = ce->arg_list->next;
ssaValue *dst_slice = ssa_build_addr(proc, dst_node).addr;
ssaValue *src_slice = ssa_build_addr(proc, src_node).addr;
ssaValue *dst_slice = ssa_build_expr(proc, dst_node);
ssaValue *src_slice = ssa_build_expr(proc, src_node);
Type *slice_type = get_base_type(ssa_type(dst_slice));
GB_ASSERT(slice_type->kind == Type_Slice);
Type *elem_type = slice_type->Slice.elem;
i64 size_of_elem = type_size_of(proc->module->sizes, proc->module->allocator, elem_type);
ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, dst_slice), t_rawptr);
ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, src_slice), t_rawptr);
ssaValue *d = ssa_add_local_generated(proc, slice_type);
ssaValue *s = ssa_add_local_generated(proc, slice_type);
ssa_emit_store(proc, d, dst_slice);
ssa_emit_store(proc, s, src_slice);
ssaValue *len_dst = ssa_slice_len(proc, dst_slice);
ssaValue *len_src = ssa_slice_len(proc, src_slice);
ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, d), t_rawptr);
ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, s), t_rawptr);
ssaValue *len_dst = ssa_slice_len(proc, d);
ssaValue *len_src = ssa_slice_len(proc, s);
Token lt = {Token_Lt};
ssaValue *cond = ssa_emit_comp(proc, lt, len_dst, len_src);
@@ -1795,6 +1800,51 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
return ssa_emit(proc, ssa_make_instr_shuffle_vector(proc, vector, indices, index_count));
} break;
case BuiltinProc_ptr_offset: {
ssaValue *ptr = ssa_build_expr(proc, ce->arg_list);
ssaValue *offset = ssa_build_expr(proc, ce->arg_list->next);
return ssa_emit_ptr_offset(proc, ptr, offset);
} break;
case BuiltinProc_ptr_sub: {
ssaValue *ptr_a = ssa_build_expr(proc, ce->arg_list);
ssaValue *ptr_b = ssa_build_expr(proc, ce->arg_list->next);
Type *ptr_type = get_base_type(ssa_type(ptr_a));
GB_ASSERT(ptr_type->kind == Type_Pointer);
isize elem_size = type_size_of(proc->module->sizes, proc->module->allocator, ptr_type->Pointer.elem);
Token sub = {Token_Sub};
ssaValue *v = ssa_emit_arith(proc, sub, ptr_a, ptr_b, t_int);
if (elem_size > 1) {
Token quo = {Token_Quo};
ssaValue *ez = ssa_make_value_constant(proc->module->allocator, t_int,
make_exact_value_integer(elem_size));
v = ssa_emit_arith(proc, quo, v, ez, t_int);
}
return v;
} break;
case BuiltinProc_slice_ptr: {
ssaValue *ptr = ssa_build_expr(proc, ce->arg_list);
ssaValue *len = ssa_build_expr(proc, ce->arg_list->next);
ssaValue *cap = len;
len = ssa_emit_conv(proc, len, t_int);
if (ce->arg_list->next->next != NULL) {
cap = ssa_build_expr(proc, ce->arg_list->next->next);
cap = ssa_emit_conv(proc, cap, t_int);
}
Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ssa_type(ptr)));
ssaValue *slice = ssa_add_local_generated(proc, slice_type);
ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_zero32, ssa_type(ptr)), ptr);
ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_one32, t_int), len);
ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_two32, t_int), cap);
return ssa_emit_load(proc, slice);
} break;
}
}
}
@@ -2010,7 +2060,8 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
}
} break;
case Type_Pointer: {
elem = ssa_emit_load(proc, ssa_build_addr(proc, ie->expr).addr);
ssaValue *array = ssa_emit_load(proc, ssa_build_expr(proc, ie->expr));
elem = ssa_array_elem(proc, array);
} break;
}
@@ -2050,18 +2101,24 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
case_end;
case_ast_node(de, DerefExpr, expr);
ssaValue *e = ssa_emit_load(proc, ssa_build_addr(proc, de->expr).addr);
ssaValue *gep = ssa_make_instr_get_element_ptr(proc, e, NULL, NULL, 0, false);
ssaValue *e = ssa_build_expr(proc, de->expr);
ssaValue *gep = ssa_emit_zero_gep(proc, e);
// HACK(bill): need to deref here as stack variables are of type pointer
// and addresses are already pointers
// TODO(bill): Completely redo the type system for SSA
Type *t = type_deref(ssa_type(e));
gep->Instr.GetElementPtr.result_type = t;
gep->Instr.GetElementPtr.elem_type = t;
ssaValue *v = ssa_emit(proc, gep);
return ssa_make_addr(v, expr);
return ssa_make_addr(gep, expr);
case_end;
}
TokenPos token_pos = ast_node_token(expr).pos;
GB_PANIC("Unexpected address expression\n"
"\tAstNode: %.*s\n", LIT(ast_node_strings[expr->kind]));
"\tAstNode: %.*s @ "
"%.*s(%td:%td)\n",
LIT(ast_node_strings[expr->kind]),
LIT(token_pos.file), token_pos.line, token_pos.column);
return ssa_make_addr(NULL, NULL);
+2
View File
@@ -2,6 +2,8 @@
#pragma warning(disable: 4245)
#include "utf8proc/utf8proc.h"
// #define UTF8PROC_IMPLEMENTATION
// #include "utf8proc/utf8proc_new.h"
#pragma warning(pop)