mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 14:48:47 +00:00
Add experimental atom op tables for llvm-backend
This commit is contained in:
+21
-3
@@ -256,9 +256,6 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
|
|||||||
GB_ASSERT(e->type == nullptr);
|
GB_ASSERT(e->type == nullptr);
|
||||||
|
|
||||||
DeclInfo *decl = decl_info_of_entity(e);
|
DeclInfo *decl = decl_info_of_entity(e);
|
||||||
if (decl != nullptr) {
|
|
||||||
check_decl_attributes(ctx, decl->attributes, const_decl_attribute, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_distinct = is_type_distinct(init_expr);
|
bool is_distinct = is_type_distinct(init_expr);
|
||||||
Ast *te = remove_type_alias_clutter(init_expr);
|
Ast *te = remove_type_alias_clutter(init_expr);
|
||||||
@@ -280,6 +277,10 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
|
|||||||
error(init_expr, "'distinct' cannot be applied to 'typeid'");
|
error(init_expr, "'distinct' cannot be applied to 'typeid'");
|
||||||
is_distinct = false;
|
is_distinct = false;
|
||||||
}
|
}
|
||||||
|
if (is_distinct && is_type_any(e->type)) {
|
||||||
|
error(init_expr, "'distinct' cannot be applied to 'any'");
|
||||||
|
is_distinct = false;
|
||||||
|
}
|
||||||
if (!is_distinct) {
|
if (!is_distinct) {
|
||||||
e->type = bt;
|
e->type = bt;
|
||||||
named->Named.base = bt;
|
named->Named.base = bt;
|
||||||
@@ -298,6 +299,23 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (decl != nullptr) {
|
||||||
|
AttributeContext ac = {};
|
||||||
|
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
|
||||||
|
if (ac.atom_op_table != nullptr) {
|
||||||
|
Type *bt = base_type(e->type);
|
||||||
|
switch (bt->kind) {
|
||||||
|
case Type_Struct:
|
||||||
|
bt->Struct.atom_op_table = ac.atom_op_table;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error(e->token, "Only struct types can have custom atom operations");
|
||||||
|
gb_free(heap_allocator(), ac.atom_op_table);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// using decl
|
// using decl
|
||||||
if (decl->is_using) {
|
if (decl->is_using) {
|
||||||
|
|||||||
+76
-3
@@ -53,7 +53,7 @@ typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
|
|||||||
void check_expr (CheckerContext *c, Operand *operand, Ast *expression);
|
void check_expr (CheckerContext *c, Operand *operand, Ast *expression);
|
||||||
void check_multi_expr (CheckerContext *c, Operand *operand, Ast *expression);
|
void check_multi_expr (CheckerContext *c, Operand *operand, Ast *expression);
|
||||||
void check_multi_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression);
|
void check_multi_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression);
|
||||||
void check_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint = nullptr);
|
void check_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
|
||||||
ExprKind check_expr_base (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
|
ExprKind check_expr_base (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
|
||||||
void check_expr_with_type_hint (CheckerContext *c, Operand *o, Ast *e, Type *t);
|
void check_expr_with_type_hint (CheckerContext *c, Operand *o, Ast *e, Type *t);
|
||||||
Type * check_type (CheckerContext *c, Ast *expression);
|
Type * check_type (CheckerContext *c, Ast *expression);
|
||||||
@@ -8944,7 +8944,10 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
|||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ue, UnaryExpr, node);
|
case_ast_node(ue, UnaryExpr, node);
|
||||||
|
Ast *prev_unary_address_hint = c->unary_address_hint;
|
||||||
|
c->unary_address_hint = unparen_expr(node);
|
||||||
check_expr_base(c, o, ue->expr, type_hint);
|
check_expr_base(c, o, ue->expr, type_hint);
|
||||||
|
c->unary_address_hint = prev_unary_address_hint;
|
||||||
node->viral_state_flags |= ue->expr->viral_state_flags;
|
node->viral_state_flags |= ue->expr->viral_state_flags;
|
||||||
|
|
||||||
if (o->mode == Addressing_Invalid) {
|
if (o->mode == Addressing_Invalid) {
|
||||||
@@ -9070,6 +9073,47 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
|||||||
return Expr_Expr;
|
return Expr_Expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (t->kind == Type_Struct) {
|
||||||
|
TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
|
||||||
|
if (atom_op_table != nullptr) {
|
||||||
|
if (atom_op_table->op[TypeAtomOp_index_set]) {
|
||||||
|
if (c->assignment_lhs_hint == node) {
|
||||||
|
o->mode = Addressing_AtomOpAssign;
|
||||||
|
o->type = o->type;
|
||||||
|
o->expr = node;
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (atom_op_table->op[TypeAtomOp_index_get]) {
|
||||||
|
Entity *e = atom_op_table->op[TypeAtomOp_index_get];
|
||||||
|
if (ie->index == nullptr) {
|
||||||
|
gbString str = expr_to_string(o->expr);
|
||||||
|
error(o->expr, "Missing index for '%s'", str);
|
||||||
|
gb_string_free(str);
|
||||||
|
o->mode = Addressing_Invalid;
|
||||||
|
o->expr = node;
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_ASSERT(e->identifier != nullptr);
|
||||||
|
Ast *proc_ident = clone_ast(e->identifier);
|
||||||
|
|
||||||
|
auto args = array_make<Ast *>(heap_allocator(), 2);
|
||||||
|
args[0] = ie->expr;
|
||||||
|
args[1] = ie->index;
|
||||||
|
|
||||||
|
GB_ASSERT(c->file != nullptr);
|
||||||
|
Ast *fake_call = ast_call_expr(c->file, proc_ident, args, ie->open, ie->close, {});
|
||||||
|
check_expr_base(c, o, fake_call, type_hint);
|
||||||
|
AtomOpMapEntry entry = {TypeAtomOp_index_get, fake_call};
|
||||||
|
map_set(&c->info->atom_op_map, hash_pointer(node), entry);
|
||||||
|
o->expr = node;
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
i64 max_count = -1;
|
i64 max_count = -1;
|
||||||
bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
|
bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
|
||||||
|
|
||||||
@@ -9133,8 +9177,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
|||||||
o->value = get_constant_field_single(c, value, cast(i32)index, nullptr, nullptr);
|
o->value = get_constant_field_single(c, value, cast(i32)index, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node->viral_state_flags |= ie->index->viral_state_flags;
|
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
|
||||||
@@ -9191,6 +9233,37 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
|||||||
if (is_type_soa_struct(t)) {
|
if (is_type_soa_struct(t)) {
|
||||||
valid = true;
|
valid = true;
|
||||||
o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
|
o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
|
||||||
|
} else {
|
||||||
|
TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
|
||||||
|
if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) {
|
||||||
|
Entity *e = atom_op_table->op[TypeAtomOp_slice];
|
||||||
|
GB_ASSERT(e->identifier != nullptr);
|
||||||
|
Ast *proc_ident = clone_ast(e->identifier);
|
||||||
|
|
||||||
|
Ast *expr = se->expr;
|
||||||
|
if (o->mode == Addressing_Variable) {
|
||||||
|
expr = ast_unary_expr(c->file, {Token_And, STR_LIT("&")}, expr);
|
||||||
|
} else if (is_type_pointer(o->type)) {
|
||||||
|
// Okay
|
||||||
|
} else {
|
||||||
|
gbString str = expr_to_string(node);
|
||||||
|
error(node, "Cannot slice '%s', value is not addressable", str);
|
||||||
|
gb_string_free(str);
|
||||||
|
o->mode = Addressing_Invalid;
|
||||||
|
o->expr = node;
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
auto args = array_make<Ast *>(heap_allocator(), 1);
|
||||||
|
args[0] = expr;
|
||||||
|
|
||||||
|
|
||||||
|
GB_ASSERT(c->file != nullptr);
|
||||||
|
Ast *fake_call = ast_call_expr(c->file, proc_ident, args, se->open, se->close, {});
|
||||||
|
check_expr_base(c, o, fake_call, type_hint);
|
||||||
|
AtomOpMapEntry entry = {TypeAtomOp_slice, fake_call};
|
||||||
|
map_set(&c->info->atom_op_map, hash_pointer(node), entry);
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1296,9 +1296,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
o->expr = as->lhs[i];
|
o->expr = as->lhs[i];
|
||||||
o->mode = Addressing_Value;
|
o->mode = Addressing_Value;
|
||||||
} else {
|
} else {
|
||||||
|
ctx->assignment_lhs_hint = unparen_expr(as->lhs[i]);
|
||||||
check_expr(ctx, &lhs_operands[i], as->lhs[i]);
|
check_expr(ctx, &lhs_operands[i], as->lhs[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ctx->assignment_lhs_hint = nullptr; // Reset the assignment_lhs_hint
|
||||||
|
|
||||||
check_assignment_arguments(ctx, lhs_operands, &rhs_operands, as->rhs);
|
check_assignment_arguments(ctx, lhs_operands, &rhs_operands, as->rhs);
|
||||||
|
|
||||||
@@ -1310,8 +1312,61 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto lhs_to_ignore = array_make<bool>(ctx->allocator, lhs_count);
|
||||||
|
defer (array_free(&lhs_to_ignore));
|
||||||
|
|
||||||
isize max = gb_min(lhs_count, rhs_count);
|
isize max = gb_min(lhs_count, rhs_count);
|
||||||
|
// NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working
|
||||||
|
// correctly for assignments
|
||||||
for (isize i = 0; i < max; i++) {
|
for (isize i = 0; i < max; i++) {
|
||||||
|
if (lhs_operands[i].mode == Addressing_AtomOpAssign) {
|
||||||
|
Operand lhs = lhs_operands[i];
|
||||||
|
|
||||||
|
Type *t = base_type(lhs.type);
|
||||||
|
GB_ASSERT(t->kind == Type_Struct);
|
||||||
|
ast_node(ie, IndexExpr, unparen_expr(lhs.expr));
|
||||||
|
|
||||||
|
TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
|
||||||
|
GB_ASSERT(atom_op_table->op[TypeAtomOp_index_set] != nullptr);
|
||||||
|
Entity *e = atom_op_table->op[TypeAtomOp_index_set];
|
||||||
|
|
||||||
|
GB_ASSERT(e->identifier != nullptr);
|
||||||
|
Ast *proc_ident = clone_ast(e->identifier);
|
||||||
|
GB_ASSERT(ctx->file != nullptr);
|
||||||
|
|
||||||
|
|
||||||
|
TypeAndValue tv = type_and_value_of_expr(ie->expr);
|
||||||
|
Ast *expr = ie->expr;
|
||||||
|
if (is_type_pointer(tv.type)) {
|
||||||
|
// Okay
|
||||||
|
} else if (tv.mode == Addressing_Variable) {
|
||||||
|
// NOTE(bill): Hack it to take the address instead
|
||||||
|
expr = ast_unary_expr(ctx->file, {Token_And, STR_LIT("&")}, ie->expr);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto args = array_make<Ast *>(heap_allocator(), 3);
|
||||||
|
args[0] = expr;
|
||||||
|
args[1] = ie->index;
|
||||||
|
args[2] = rhs_operands[i].expr;
|
||||||
|
|
||||||
|
Ast *fake_call = ast_call_expr(ctx->file, proc_ident, args, ie->open, ie->close, {});
|
||||||
|
Operand fake_operand = {};
|
||||||
|
fake_operand.expr = lhs.expr;
|
||||||
|
check_expr_base(ctx, &fake_operand, fake_call, nullptr);
|
||||||
|
AtomOpMapEntry entry = {TypeAtomOp_index_set, fake_call};
|
||||||
|
map_set(&ctx->info->atom_op_map, hash_pointer(lhs.expr), entry);
|
||||||
|
|
||||||
|
lhs_to_ignore[i] = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (isize i = 0; i < max; i++) {
|
||||||
|
if (lhs_to_ignore[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]);
|
check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]);
|
||||||
}
|
}
|
||||||
if (lhs_count != rhs_count) {
|
if (lhs_count != rhs_count) {
|
||||||
|
|||||||
@@ -335,6 +335,19 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
|
|||||||
array_add(&array, e);
|
array_add(&array, e);
|
||||||
map_set(&ctx->checker->info.gen_types, hash_pointer(original_type), array);
|
map_set(&ctx->checker->info.gen_types, hash_pointer(original_type), array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type *dst_bt = base_type(named_type);
|
||||||
|
Type *src_bt = base_type(original_type);
|
||||||
|
if ((dst_bt != nullptr && src_bt != nullptr) &&
|
||||||
|
(dst_bt->kind == src_bt->kind)){
|
||||||
|
if (dst_bt->kind == Type_Struct) {
|
||||||
|
if (dst_bt->Struct.atom_op_table == nullptr) {
|
||||||
|
dst_bt->Struct.atom_op_table = src_bt->Struct.atom_op_table;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
|
void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
|
||||||
|
|||||||
+211
@@ -2,6 +2,7 @@
|
|||||||
#include "types.cpp"
|
#include "types.cpp"
|
||||||
|
|
||||||
void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
|
void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
|
||||||
|
void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
|
||||||
|
|
||||||
|
|
||||||
bool is_operand_value(Operand o) {
|
bool is_operand_value(Operand o) {
|
||||||
@@ -810,6 +811,9 @@ void init_checker_info(CheckerInfo *i) {
|
|||||||
if (i->allow_identifier_uses) {
|
if (i->allow_identifier_uses) {
|
||||||
array_init(&i->identifier_uses, a);
|
array_init(&i->identifier_uses, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
map_init(&i->atom_op_map, a);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy_checker_info(CheckerInfo *i) {
|
void destroy_checker_info(CheckerInfo *i) {
|
||||||
@@ -828,6 +832,7 @@ void destroy_checker_info(CheckerInfo *i) {
|
|||||||
array_free(&i->required_foreign_imports_through_force);
|
array_free(&i->required_foreign_imports_through_force);
|
||||||
array_free(&i->required_global_variables);
|
array_free(&i->required_global_variables);
|
||||||
|
|
||||||
|
map_destroy(&i->atom_op_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckerContext make_checker_context(Checker *c) {
|
CheckerContext make_checker_context(Checker *c) {
|
||||||
@@ -2413,7 +2418,213 @@ DECL_ATTRIBUTE_PROC(const_decl_attribute) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DECL_ATTRIBUTE_PROC(type_decl_attribute) {
|
||||||
|
if (name == "private") {
|
||||||
|
// NOTE(bill): Handled elsewhere `check_collect_value_decl`
|
||||||
|
return true;
|
||||||
|
} else if (name == "index_get") {
|
||||||
|
if (value != nullptr) {
|
||||||
|
Operand o = {};
|
||||||
|
check_expr_or_type(c, &o, value);
|
||||||
|
Entity *e = entity_of_node(value);
|
||||||
|
if (e != nullptr && e->kind == Entity_Procedure) {
|
||||||
|
if (ac->deferred_procedure.entity != nullptr) {
|
||||||
|
error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
Type *pt = base_type(e->type);
|
||||||
|
GB_ASSERT(pt->kind == Type_Proc);
|
||||||
|
|
||||||
|
if (pt->Proc.result_count == 0) {
|
||||||
|
error(value, "'%s' attribute must return something", LIT(name));
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pt->Proc.param_count < 2) {
|
||||||
|
error(value, "'%s' attribute must allow for 2 parameters", LIT(name));
|
||||||
|
valid = false;
|
||||||
|
} else {
|
||||||
|
isize minimum_param_count = 0;
|
||||||
|
for_array(i, pt->Proc.params->Tuple.variables) {
|
||||||
|
Entity *param = pt->Proc.params->Tuple.variables[i];
|
||||||
|
if (param->kind == Entity_Variable) {
|
||||||
|
if (param->Variable.param_value.kind == ParameterValue_Invalid) {
|
||||||
|
minimum_param_count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (param->kind == Entity_Constant) {
|
||||||
|
minimum_param_count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minimum_param_count > 2) {
|
||||||
|
error(value, "'%s' attribute must allow for at a minimum 2 parameters", LIT(name));
|
||||||
|
value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid && build_context.use_llvm_api) {
|
||||||
|
if (ac->atom_op_table == nullptr) {
|
||||||
|
ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
|
||||||
|
}
|
||||||
|
ac->atom_op_table->op[TypeAtomOp_index_get] = e;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
|
||||||
|
return false;
|
||||||
|
} else if (name == "index_set") {
|
||||||
|
if (value != nullptr) {
|
||||||
|
Operand o = {};
|
||||||
|
check_expr_or_type(c, &o, value);
|
||||||
|
Entity *e = entity_of_node(value);
|
||||||
|
if (e != nullptr && e->kind == Entity_Procedure) {
|
||||||
|
if (ac->deferred_procedure.entity != nullptr) {
|
||||||
|
error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
Type *pt = base_type(e->type);
|
||||||
|
GB_ASSERT(pt->kind == Type_Proc);
|
||||||
|
|
||||||
|
if (pt->Proc.param_count < 3) {
|
||||||
|
error(value, "'%s' attribute must allow for 3 parameters", LIT(name));
|
||||||
|
valid = false;
|
||||||
|
} else {
|
||||||
|
isize minimum_param_count = 0;
|
||||||
|
for_array(i, pt->Proc.params->Tuple.variables) {
|
||||||
|
Entity *param = pt->Proc.params->Tuple.variables[i];
|
||||||
|
if (param->kind == Entity_Variable) {
|
||||||
|
if (param->Variable.param_value.kind == ParameterValue_Invalid) {
|
||||||
|
minimum_param_count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (param->kind == Entity_Constant) {
|
||||||
|
minimum_param_count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minimum_param_count > 3) {
|
||||||
|
error(value, "'%s' attribute must allow for at a minimum 3 parameters", LIT(name));
|
||||||
|
value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pt->Proc.variadic || pt->Proc.c_vararg) {
|
||||||
|
error(value, "'%s' attribute does not allow variadic procedures", LIT(name));
|
||||||
|
value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid && build_context.use_llvm_api) {
|
||||||
|
if (ac->atom_op_table == nullptr) {
|
||||||
|
ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
|
||||||
|
}
|
||||||
|
ac->atom_op_table->op[TypeAtomOp_index_set] = e;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
|
||||||
|
return false;
|
||||||
|
} else if (name == "slice") {
|
||||||
|
if (value != nullptr) {
|
||||||
|
Operand o = {};
|
||||||
|
check_expr_or_type(c, &o, value);
|
||||||
|
Entity *e = entity_of_node(value);
|
||||||
|
if (e != nullptr && e->kind == Entity_Procedure) {
|
||||||
|
if (ac->deferred_procedure.entity != nullptr) {
|
||||||
|
error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
Type *pt = base_type(e->type);
|
||||||
|
GB_ASSERT(pt->kind == Type_Proc);
|
||||||
|
|
||||||
|
if (pt->Proc.param_count < 1) {
|
||||||
|
error(value, "'%s' attribute must allow for 1 parameter", LIT(name));
|
||||||
|
valid = false;
|
||||||
|
} else {
|
||||||
|
isize minimum_param_count = 0;
|
||||||
|
for_array(i, pt->Proc.params->Tuple.variables) {
|
||||||
|
Entity *param = pt->Proc.params->Tuple.variables[i];
|
||||||
|
if (param->kind == Entity_Variable) {
|
||||||
|
if (param->Variable.param_value.kind == ParameterValue_Invalid) {
|
||||||
|
minimum_param_count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (param->kind == Entity_Constant) {
|
||||||
|
minimum_param_count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minimum_param_count > 1) {
|
||||||
|
error(value, "'%s' attribute must allow for at a minimum 1 parameter", LIT(name));
|
||||||
|
value = false;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Entity *param = pt->Proc.params->Tuple.variables[0];
|
||||||
|
Type *param_type = base_type(param->type);
|
||||||
|
if (is_type_pointer(param_type) && !is_type_rawptr(param_type)) {
|
||||||
|
// okay
|
||||||
|
} else {
|
||||||
|
error(value, "'%s' attribute's first parameter must be a pointer", LIT(name));
|
||||||
|
value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pt->Proc.variadic || pt->Proc.c_vararg) {
|
||||||
|
error(value, "'%s' attribute does not allow variadic procedures", LIT(name));
|
||||||
|
value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pt->Proc.result_count != 1) {
|
||||||
|
error(value, "'%s' attribute must return 1 result", LIT(name));
|
||||||
|
value = false;
|
||||||
|
} else {
|
||||||
|
Type *rt = pt->Proc.results->Tuple.variables[0]->type;
|
||||||
|
rt = base_type(rt);
|
||||||
|
if (!is_type_slice(rt)) {
|
||||||
|
error(value, "'%s' attribute must return a slice", LIT(name));
|
||||||
|
value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid && build_context.use_llvm_api) {
|
||||||
|
if (ac->atom_op_table == nullptr) {
|
||||||
|
ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable);
|
||||||
|
}
|
||||||
|
ac->atom_op_table->op[TypeAtomOp_slice] = e;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#include "check_expr.cpp"
|
#include "check_expr.cpp"
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ struct AttributeContext {
|
|||||||
String thread_local_model;
|
String thread_local_model;
|
||||||
String deprecated_message;
|
String deprecated_message;
|
||||||
DeferredProcedure deferred_procedure;
|
DeferredProcedure deferred_procedure;
|
||||||
|
struct TypeAtomOpTable *atom_op_table;
|
||||||
};
|
};
|
||||||
|
|
||||||
AttributeContext make_attribute_context(String link_prefix) {
|
AttributeContext make_attribute_context(String link_prefix) {
|
||||||
@@ -232,6 +233,11 @@ struct ForeignContext {
|
|||||||
typedef Array<Entity *> CheckerTypePath;
|
typedef Array<Entity *> CheckerTypePath;
|
||||||
typedef Array<Type *> CheckerPolyPath;
|
typedef Array<Type *> CheckerPolyPath;
|
||||||
|
|
||||||
|
struct AtomOpMapEntry {
|
||||||
|
u32 kind;
|
||||||
|
Ast *node;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// CheckerInfo stores all the symbol information for a type-checked program
|
// CheckerInfo stores all the symbol information for a type-checked program
|
||||||
struct CheckerInfo {
|
struct CheckerInfo {
|
||||||
@@ -262,6 +268,8 @@ struct CheckerInfo {
|
|||||||
Array<Entity *> required_foreign_imports_through_force;
|
Array<Entity *> required_foreign_imports_through_force;
|
||||||
Array<Entity *> required_global_variables;
|
Array<Entity *> required_global_variables;
|
||||||
|
|
||||||
|
Map<AtomOpMapEntry> atom_op_map; // Key: Ast *
|
||||||
|
|
||||||
|
|
||||||
bool allow_identifier_uses;
|
bool allow_identifier_uses;
|
||||||
Array<Ast *> identifier_uses; // only used by 'odin query'
|
Array<Ast *> identifier_uses; // only used by 'odin query'
|
||||||
@@ -301,6 +309,9 @@ struct CheckerContext {
|
|||||||
bool hide_polymorphic_errors;
|
bool hide_polymorphic_errors;
|
||||||
bool in_polymorphic_specialization;
|
bool in_polymorphic_specialization;
|
||||||
Scope * polymorphic_scope;
|
Scope * polymorphic_scope;
|
||||||
|
|
||||||
|
Ast *assignment_lhs_hint;
|
||||||
|
Ast *unary_address_hint;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Checker {
|
struct Checker {
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ struct Entity {
|
|||||||
isize order_in_src;
|
isize order_in_src;
|
||||||
String deprecated_message;
|
String deprecated_message;
|
||||||
|
|
||||||
|
// IMPORTANT NOTE(bill): This must be a discriminated union because of patching
|
||||||
|
// later entity kinds
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
ExactValue value;
|
ExactValue value;
|
||||||
|
|||||||
+128
-2
@@ -160,8 +160,83 @@ void lb_addr_store(lbProcedure *p, lbAddr const &addr, lbValue value) {
|
|||||||
value.value = LLVMConstNull(lb_type(p->module, t));
|
value.value = LLVMConstNull(lb_type(p->module, t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (addr.kind == lbAddr_AtomOp_index_set) {
|
||||||
|
lbValue ptr = addr.addr;
|
||||||
|
lbValue index = addr.index_set.index;
|
||||||
|
Ast *node = addr.index_set.node;
|
||||||
|
|
||||||
if (addr.kind == lbAddr_Map) {
|
ast_node(ce, CallExpr, node);
|
||||||
|
Type *proc_type = type_and_value_of_expr(ce->proc).type;
|
||||||
|
proc_type = base_type(proc_type);
|
||||||
|
GB_ASSERT(is_type_proc(proc_type));
|
||||||
|
TypeProc *pt = &proc_type->Proc;
|
||||||
|
|
||||||
|
isize arg_count = 3;
|
||||||
|
isize param_count = 0;
|
||||||
|
if (pt->params) {
|
||||||
|
GB_ASSERT(pt->params->kind == Type_Tuple);
|
||||||
|
param_count = pt->params->Tuple.variables.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto args = array_make<lbValue>(heap_allocator(), gb_max(arg_count, param_count));
|
||||||
|
args[0] = ptr;
|
||||||
|
args[1] = index;
|
||||||
|
args[2] = value;
|
||||||
|
|
||||||
|
isize arg_index = arg_count;
|
||||||
|
if (arg_count < param_count) {
|
||||||
|
lbModule *m = p->module;
|
||||||
|
String proc_name = {};
|
||||||
|
if (p->entity != nullptr) {
|
||||||
|
proc_name = p->entity->token.string;
|
||||||
|
}
|
||||||
|
TokenPos pos = ast_token(ce->proc).pos;
|
||||||
|
|
||||||
|
TypeTuple *param_tuple = &pt->params->Tuple;
|
||||||
|
|
||||||
|
isize end = cast(isize)param_count;
|
||||||
|
while (arg_index < end) {
|
||||||
|
Entity *e = param_tuple->variables[arg_index];
|
||||||
|
GB_ASSERT(e->kind == Entity_Variable);
|
||||||
|
|
||||||
|
switch (e->Variable.param_value.kind) {
|
||||||
|
case ParameterValue_Constant:
|
||||||
|
args[arg_index++] = lb_const_value(p->module, e->type, e->Variable.param_value.value);
|
||||||
|
break;
|
||||||
|
case ParameterValue_Nil:
|
||||||
|
args[arg_index++] = lb_const_nil(m, e->type);
|
||||||
|
break;
|
||||||
|
case ParameterValue_Location:
|
||||||
|
args[arg_index++] = lb_emit_source_code_location(p, proc_name, pos);
|
||||||
|
break;
|
||||||
|
case ParameterValue_Value:
|
||||||
|
args[arg_index++] = lb_build_expr(p, e->Variable.param_value.ast_value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *e = entity_from_expr(ce->proc);
|
||||||
|
GB_ASSERT(e != nullptr);
|
||||||
|
GB_ASSERT(is_type_polymorphic(e->type));
|
||||||
|
|
||||||
|
{
|
||||||
|
lbValue *found = nullptr;
|
||||||
|
if (p->module != e->code_gen_module) {
|
||||||
|
gb_mutex_lock(&p->module->mutex);
|
||||||
|
}
|
||||||
|
found = map_get(&e->code_gen_module->values, hash_entity(e));
|
||||||
|
if (p->module != e->code_gen_module) {
|
||||||
|
gb_mutex_unlock(&p->module->mutex);
|
||||||
|
}
|
||||||
|
GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(e->token.string));
|
||||||
|
|
||||||
|
lb_emit_call(p, *found, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else if (addr.kind == lbAddr_Map) {
|
||||||
lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value);
|
lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value);
|
||||||
return;
|
return;
|
||||||
} else if (addr.kind == lbAddr_BitField) {
|
} else if (addr.kind == lbAddr_BitField) {
|
||||||
@@ -8587,7 +8662,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
|
|||||||
expr = unparen_expr(expr);
|
expr = unparen_expr(expr);
|
||||||
|
|
||||||
TypeAndValue tv = type_and_value_of_expr(expr);
|
TypeAndValue tv = type_and_value_of_expr(expr);
|
||||||
GB_ASSERT(tv.mode != Addressing_Invalid);
|
GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "%s", expr_to_string(expr));
|
||||||
GB_ASSERT(tv.mode != Addressing_Type);
|
GB_ASSERT(tv.mode != Addressing_Type);
|
||||||
|
|
||||||
if (tv.value.kind != ExactValue_Invalid) {
|
if (tv.value.kind != ExactValue_Invalid) {
|
||||||
@@ -9301,6 +9376,27 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
|
|||||||
return lb_addr(val);
|
return lb_addr(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!is_type_indexable(t)) {
|
||||||
|
AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr));
|
||||||
|
if (found != nullptr) {
|
||||||
|
if (found->kind == TypeAtomOp_index_get) {
|
||||||
|
return lb_build_addr(p, found->node);
|
||||||
|
} else if (found->kind == TypeAtomOp_index_get_ptr) {
|
||||||
|
return lb_addr(lb_build_expr(p, found->node));
|
||||||
|
} else if (found->kind == TypeAtomOp_index_set) {
|
||||||
|
lbValue ptr = lb_build_addr_ptr(p, ie->expr);
|
||||||
|
if (deref) {
|
||||||
|
ptr = lb_emit_load(p, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
lbAddr addr = {lbAddr_AtomOp_index_set};
|
||||||
|
addr.addr = ptr;
|
||||||
|
addr.index_set.index = lb_build_expr(p, ie->index);
|
||||||
|
addr.index_set.node = found->node;
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
|
GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
|
||||||
|
|
||||||
if (is_type_map(t)) {
|
if (is_type_map(t)) {
|
||||||
@@ -9450,6 +9546,36 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
|
|||||||
|
|
||||||
bool no_indices = se->low == nullptr && se->high == nullptr;
|
bool no_indices = se->low == nullptr && se->high == nullptr;
|
||||||
|
|
||||||
|
{
|
||||||
|
Type *type = base_type(type_of_expr(se->expr));
|
||||||
|
if (type->kind == Type_Struct && !is_type_soa_struct(type)) {
|
||||||
|
TypeAtomOpTable *atom_op_table = type->Struct.atom_op_table;
|
||||||
|
if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) {
|
||||||
|
AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr));
|
||||||
|
if (found) {
|
||||||
|
lbValue base = lb_build_expr(p, found->node);
|
||||||
|
|
||||||
|
Type *slice_type = base.type;
|
||||||
|
lbValue len = lb_slice_len(p, base);
|
||||||
|
if (high.value == nullptr) high = len;
|
||||||
|
|
||||||
|
if (!no_indices) {
|
||||||
|
lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
lbValue elem = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low);
|
||||||
|
lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
|
||||||
|
|
||||||
|
lbAddr slice = lb_add_local_generated(p, slice_type, false);
|
||||||
|
lb_fill_slice(p, slice, elem, new_len);
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
lbValue addr = lb_build_addr_ptr(p, se->expr);
|
lbValue addr = lb_build_addr_ptr(p, se->expr);
|
||||||
lbValue base = lb_emit_load(p, addr);
|
lbValue base = lb_emit_load(p, addr);
|
||||||
Type *type = base_type(base.type);
|
Type *type = base_type(base.type);
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ enum lbAddrKind {
|
|||||||
lbAddr_BitField,
|
lbAddr_BitField,
|
||||||
lbAddr_Context,
|
lbAddr_Context,
|
||||||
lbAddr_SoaVariable,
|
lbAddr_SoaVariable,
|
||||||
|
|
||||||
|
lbAddr_AtomOp_index_set,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lbAddr {
|
struct lbAddr {
|
||||||
@@ -48,6 +50,10 @@ struct lbAddr {
|
|||||||
lbValue index;
|
lbValue index;
|
||||||
Ast *index_expr;
|
Ast *index_expr;
|
||||||
} soa;
|
} soa;
|
||||||
|
struct {
|
||||||
|
lbValue index;
|
||||||
|
Ast *node;
|
||||||
|
} index_set;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -333,7 +339,7 @@ void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *m
|
|||||||
|
|
||||||
void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
|
void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
|
||||||
lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
|
lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
|
||||||
|
lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos);
|
||||||
|
|
||||||
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
|
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
|
||||||
#define LB_STARTUP_CONTEXT_PROC_NAME "__$startup_context"
|
#define LB_STARTUP_CONTEXT_PROC_NAME "__$startup_context"
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ enum AddressingMode {
|
|||||||
// rhs: acts like OptionalOk
|
// rhs: acts like OptionalOk
|
||||||
Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check)
|
Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check)
|
||||||
Addressing_SoaVariable, // Struct-Of-Arrays indexed variable
|
Addressing_SoaVariable, // Struct-Of-Arrays indexed variable
|
||||||
|
|
||||||
|
Addressing_AtomOpAssign, // Specialized for custom atom operations for assignments
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeAndValue {
|
struct TypeAndValue {
|
||||||
|
|||||||
+25
-4
@@ -1,5 +1,6 @@
|
|||||||
struct Scope;
|
struct Scope;
|
||||||
struct Ast;
|
struct Ast;
|
||||||
|
struct Entity;
|
||||||
|
|
||||||
enum BasicKind {
|
enum BasicKind {
|
||||||
Basic_Invalid,
|
Basic_Invalid,
|
||||||
@@ -123,6 +124,21 @@ enum StructSoaKind {
|
|||||||
StructSoa_Dynamic = 3,
|
StructSoa_Dynamic = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum TypeAtomOpKind {
|
||||||
|
TypeAtomOp_Invalid,
|
||||||
|
|
||||||
|
TypeAtomOp_index_get,
|
||||||
|
TypeAtomOp_index_set,
|
||||||
|
TypeAtomOp_slice,
|
||||||
|
TypeAtomOp_index_get_ptr,
|
||||||
|
|
||||||
|
TypeAtomOp_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TypeAtomOpTable {
|
||||||
|
Entity *op[TypeAtomOp_COUNT];
|
||||||
|
};
|
||||||
|
|
||||||
struct TypeStruct {
|
struct TypeStruct {
|
||||||
Array<Entity *> fields;
|
Array<Entity *> fields;
|
||||||
Array<String> tags;
|
Array<String> tags;
|
||||||
@@ -136,16 +152,18 @@ struct TypeStruct {
|
|||||||
i64 custom_align;
|
i64 custom_align;
|
||||||
Entity * names;
|
Entity * names;
|
||||||
|
|
||||||
|
TypeAtomOpTable *atom_op_table;
|
||||||
|
|
||||||
|
Type * soa_elem;
|
||||||
|
i64 soa_count;
|
||||||
|
StructSoaKind soa_kind;
|
||||||
|
|
||||||
bool are_offsets_set;
|
bool are_offsets_set;
|
||||||
bool are_offsets_being_processed;
|
bool are_offsets_being_processed;
|
||||||
bool is_packed;
|
bool is_packed;
|
||||||
bool is_raw_union;
|
bool is_raw_union;
|
||||||
bool is_polymorphic;
|
bool is_polymorphic;
|
||||||
bool is_poly_specialized;
|
bool is_poly_specialized;
|
||||||
|
|
||||||
StructSoaKind soa_kind;
|
|
||||||
Type * soa_elem;
|
|
||||||
i64 soa_count;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeUnion {
|
struct TypeUnion {
|
||||||
@@ -157,6 +175,9 @@ struct TypeUnion {
|
|||||||
i64 tag_size;
|
i64 tag_size;
|
||||||
Type * polymorphic_params; // Type_Tuple
|
Type * polymorphic_params; // Type_Tuple
|
||||||
Type * polymorphic_parent;
|
Type * polymorphic_parent;
|
||||||
|
|
||||||
|
TypeAtomOpTable *atom_op_table;
|
||||||
|
|
||||||
bool no_nil;
|
bool no_nil;
|
||||||
bool maybe;
|
bool maybe;
|
||||||
bool is_polymorphic;
|
bool is_polymorphic;
|
||||||
|
|||||||
Reference in New Issue
Block a user