mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-19 21:10:30 +00:00
multivalued procedure calls allows in for in to allow a pseudo-iterator; @thread_local for variables in procedure
This commit is contained in:
@@ -1233,6 +1233,44 @@ ranged_fields_for_array_compound_literals :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
range_statements_with_multiple_return_values :: proc() {
|
||||
// IMPORTANT NOTE(bill, 2019-11-02): This feature is subject to be changed/removed
|
||||
fmt.println("\n#range statements with multiple return values");
|
||||
My_Iterator :: struct {
|
||||
index: int,
|
||||
data: []i32,
|
||||
};
|
||||
make_my_iterator :: proc(data: []i32) -> My_Iterator {
|
||||
return My_Iterator{data = data};
|
||||
}
|
||||
my_iterator :: proc(it: ^My_Iterator) -> (val: i32, idx: int, cond: bool) {
|
||||
if cond = it.index < len(it.data); cond {
|
||||
val = it.data[it.index];
|
||||
idx = it.index;
|
||||
it.index += 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
data := make([]i32, 6);
|
||||
for _, i in data {
|
||||
data[i] = i32(i*i);
|
||||
}
|
||||
|
||||
{
|
||||
it := make_my_iterator(data);
|
||||
for val in my_iterator(&it) {
|
||||
fmt.println(val);
|
||||
}
|
||||
}
|
||||
{
|
||||
it := make_my_iterator(data);
|
||||
for val, idx in my_iterator(&it) {
|
||||
fmt.println(val, idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
when true {
|
||||
extra_general_stuff();
|
||||
@@ -1256,6 +1294,7 @@ main :: proc() {
|
||||
inline_for_statement();
|
||||
where_clauses();
|
||||
ranged_fields_for_array_compound_literals();
|
||||
range_statements_with_multiple_return_values();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1487,7 +1487,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
val1 = t_int;
|
||||
} else {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr_or_type(ctx, &operand, rs->expr);
|
||||
check_expr_base(ctx, &operand, expr, nullptr);
|
||||
error_operand_no_value(&operand);
|
||||
|
||||
if (operand.mode == Addressing_Type) {
|
||||
if (!is_type_enum(operand.type)) {
|
||||
@@ -1532,6 +1533,45 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
val0 = t->Map.key;
|
||||
val1 = t->Map.value;
|
||||
break;
|
||||
|
||||
case Type_Tuple:
|
||||
if (false) {
|
||||
check_not_tuple(ctx, &operand);
|
||||
} else {
|
||||
isize count = t->Tuple.variables.count;
|
||||
if (count < 1 || count > 3) {
|
||||
check_not_tuple(ctx, &operand);
|
||||
error_line("\tMultiple return valued parameters in a range statement are limited to a maximum of 2 usable values with a trailing boolean for the conditional\n");
|
||||
break;
|
||||
}
|
||||
Type *cond_type = t->Tuple.variables[count-1]->type;
|
||||
if (!is_type_boolean(cond_type)) {
|
||||
gbString s = type_to_string(cond_type);
|
||||
error(operand.expr, "The final type of %td-valued tuple must be a boolean, got %s", count, s);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
}
|
||||
|
||||
if (count > 1) val0 = t->Tuple.variables[0]->type;
|
||||
if (count > 2) val1 = t->Tuple.variables[1]->type;
|
||||
|
||||
if (rs->val1 != nullptr && count < 3) {
|
||||
gbString s = type_to_string(t);
|
||||
error(operand.expr, "Expected a 3-value tuple on the rhs, got (%s)", s);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rs->val0 != nullptr && count < 2) {
|
||||
gbString s = type_to_string(t);
|
||||
error(operand.expr, "Expected at least a 2-values tuple on the rhs, got (%s)", s);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1849,6 +1889,19 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
e->flags |= EntityFlag_Static;
|
||||
}
|
||||
}
|
||||
if (ac.thread_local_model != "") {
|
||||
String name = e->token.string;
|
||||
if (name == "_") {
|
||||
error(e->token, "The 'thread_local' attribute is not allowed to be applied to '_'");
|
||||
} else {
|
||||
e->flags |= EntityFlag_Static;
|
||||
}
|
||||
e->Variable.thread_local_model = ac.thread_local_model;
|
||||
}
|
||||
|
||||
if (ac.is_static && ac.thread_local_model != "") {
|
||||
error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied");
|
||||
}
|
||||
}
|
||||
|
||||
check_arity_match(ctx, vd);
|
||||
|
||||
@@ -2217,6 +2217,33 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
|
||||
}
|
||||
ac->is_static = true;
|
||||
return true;
|
||||
} else if (name == "thread_local") {
|
||||
if (ac->init_expr_list_count > 0) {
|
||||
error(elem, "A thread local variable declaration cannot have initialization values");
|
||||
} else if (c->foreign_context.curr_library) {
|
||||
error(elem, "A foreign block variable cannot be thread local");
|
||||
} else if (ac->is_export) {
|
||||
error(elem, "An exported variable cannot be thread local");
|
||||
} else if (ev.kind == ExactValue_Invalid) {
|
||||
ac->thread_local_model = str_lit("default");
|
||||
} else if (ev.kind == ExactValue_String) {
|
||||
String model = ev.value_string;
|
||||
if (model == "default" ||
|
||||
model == "localdynamic" ||
|
||||
model == "initialexec" ||
|
||||
model == "localexec") {
|
||||
ac->thread_local_model = model;
|
||||
} else {
|
||||
error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model));
|
||||
error_line("\tdefault\n");
|
||||
error_line("\tlocaldynamic\n");
|
||||
error_line("\tinitialexec\n");
|
||||
error_line("\tlocalexec\n");
|
||||
}
|
||||
} else {
|
||||
error(elem, "Expected either no value or a string for '%.*s'", LIT(name));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c->curr_proc_decl != nullptr) {
|
||||
@@ -2257,33 +2284,6 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
|
||||
error(elem, "Expected a string value for '%.*s'", LIT(name));
|
||||
}
|
||||
return true;
|
||||
} else if (name == "thread_local") {
|
||||
if (ac->init_expr_list_count > 0) {
|
||||
error(elem, "A thread local variable declaration cannot have initialization values");
|
||||
} else if (c->foreign_context.curr_library) {
|
||||
error(elem, "A foreign block variable cannot be thread local");
|
||||
} else if (ac->is_export) {
|
||||
error(elem, "An exported variable cannot be thread local");
|
||||
} else if (ev.kind == ExactValue_Invalid) {
|
||||
ac->thread_local_model = str_lit("default");
|
||||
} else if (ev.kind == ExactValue_String) {
|
||||
String model = ev.value_string;
|
||||
if (model == "default" ||
|
||||
model == "localdynamic" ||
|
||||
model == "initialexec" ||
|
||||
model == "localexec") {
|
||||
ac->thread_local_model = model;
|
||||
} else {
|
||||
error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model));
|
||||
error_line("\tdefault\n");
|
||||
error_line("\tlocaldynamic\n");
|
||||
error_line("\tinitialexec\n");
|
||||
error_line("\tlocalexec\n");
|
||||
}
|
||||
} else {
|
||||
error(elem, "Expected either no value or a string for '%.*s'", LIT(name));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
39
src/ir.cpp
39
src/ir.cpp
@@ -8741,6 +8741,35 @@ void ir_build_range_enum(irProcedure *proc, Type *enum_type, Type *val_type, irV
|
||||
if (done_) *done_ = done;
|
||||
}
|
||||
|
||||
void ir_build_range_tuple(irProcedure *proc, Ast *expr, Type *val0_type, Type *val1_type,
|
||||
irValue **val0_, irValue **val1_, irBlock **loop_, irBlock **done_) {
|
||||
irBlock *loop = ir_new_block(proc, nullptr, "for.tuple.loop");
|
||||
ir_emit_jump(proc, loop);
|
||||
ir_start_block(proc, loop);
|
||||
|
||||
irBlock *body = ir_new_block(proc, nullptr, "for.tuple.body");
|
||||
irBlock *done = ir_new_block(proc, nullptr, "for.tuple.done");
|
||||
|
||||
irValue *tuple_value = ir_build_expr(proc, expr);
|
||||
Type *tuple = ir_type(tuple_value);
|
||||
GB_ASSERT(tuple->kind == Type_Tuple);
|
||||
i32 tuple_count = cast(i32)tuple->Tuple.variables.count;
|
||||
i32 cond_index = tuple_count-1;
|
||||
|
||||
irValue *cond = ir_emit_struct_ev(proc, tuple_value, cond_index);
|
||||
ir_emit_if(proc, cond, body, done);
|
||||
ir_start_block(proc, body);
|
||||
|
||||
irValue *val0 = nullptr;
|
||||
|
||||
|
||||
if (val0_) *val0_ = ir_emit_struct_ev(proc, tuple_value, 0);
|
||||
if (val1_) *val1_ = ir_emit_struct_ev(proc, tuple_value, 1);
|
||||
if (loop_) *loop_ = loop;
|
||||
if (done_) *done_ = done;
|
||||
}
|
||||
|
||||
|
||||
void ir_store_type_case_implicit(irProcedure *proc, Ast *clause, irValue *value) {
|
||||
Entity *e = implicit_entity_of_node(clause);
|
||||
GB_ASSERT(e != nullptr);
|
||||
@@ -8818,7 +8847,12 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
|
||||
|
||||
irValue *g = ir_value_global(e, value);
|
||||
g->Global.name = mangled_name;
|
||||
g->Global.is_internal = true;
|
||||
g->Global.is_private = true;
|
||||
if (e->Variable.thread_local_model != "") {
|
||||
g->Global.thread_local_model = e->Variable.thread_local_model;
|
||||
} else {
|
||||
g->Global.is_internal = true;
|
||||
}
|
||||
ir_module_add_value(proc->module, e, g);
|
||||
map_set(&proc->module->members, key, g);
|
||||
}
|
||||
@@ -9252,6 +9286,9 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
|
||||
ir_build_range_string(proc, string, val0_type, &val, &key, &loop, &done);
|
||||
break;
|
||||
}
|
||||
case Type_Tuple:
|
||||
ir_build_range_tuple(proc, rs->expr, val0_type, val1_type, &val, &key, &loop, &done);
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("Cannot range over %s", type_to_string(expr_type));
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user