aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/check_builtin.cpp180
-rw-r--r--src/checker_builtin_procs.hpp2
-rw-r--r--src/llvm_backend_proc.cpp62
3 files changed, 244 insertions, 0 deletions
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index dd9896927..f01b61128 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -3243,6 +3243,186 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
+ case BuiltinProc_compress_values: {
+ Operand *ops = gb_alloc_array(temporary_allocator(), Operand, ce->args.count);
+
+ isize value_count = 0;
+
+ for_array(i, ce->args) {
+ Ast *arg = ce->args[i];
+ Operand *op = &ops[i];
+ check_multi_expr(c, op, arg);
+ if (op->mode == Addressing_Invalid) {
+ return false;
+ }
+
+ if (op->type == nullptr || op->type == t_invalid) {
+ gbString s = expr_to_string(op->expr);
+ error(op->expr, "Invalid expression to '%.*s', got %s", LIT(builtin_name), s);
+ gb_string_free(s);
+ }
+ if (is_type_tuple(op->type)) {
+ value_count += op->type->Tuple.variables.count;
+ } else {
+ value_count += 1;
+ }
+ }
+
+ GB_ASSERT(value_count >= 1);
+
+ if (value_count == 1) {
+ *operand = ops[0];
+ break;
+ }
+
+ if (type_hint != nullptr) {
+ Type *th = base_type(type_hint);
+ if (th->kind == Type_Struct) {
+ if (value_count == th->Struct.fields.count) {
+ isize index = 0;
+ for_array(i, ce->args) {
+ Operand *op = &ops[i];
+ if (is_type_tuple(op->type)) {
+ for (Entity *v : op->type->Tuple.variables) {
+ Operand x = {};
+ x.mode = Addressing_Value;
+ x.type = v->type;
+ check_assignment(c, &x, th->Struct.fields[index++]->type, builtin_name);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ }
+ } else {
+ check_assignment(c, op, th->Struct.fields[index++]->type, builtin_name);
+ if (op->mode == Addressing_Invalid) {
+ return false;
+ }
+ }
+ }
+
+ operand->type = type_hint;
+ operand->mode = Addressing_Value;
+ break;
+ }
+ } else if (is_type_array_like(th)) {
+ if (cast(i64)value_count == get_array_type_count(th)) {
+ Type *elem = base_array_type(th);
+ for_array(i, ce->args) {
+ Operand *op = &ops[i];
+ if (is_type_tuple(op->type)) {
+ for (Entity *v : op->type->Tuple.variables) {
+ Operand x = {};
+ x.mode = Addressing_Value;
+ x.type = v->type;
+ check_assignment(c, &x, elem, builtin_name);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ }
+ } else {
+ check_assignment(c, op, elem, builtin_name);
+ if (op->mode == Addressing_Invalid) {
+ return false;
+ }
+ }
+ }
+
+ operand->type = type_hint;
+ operand->mode = Addressing_Value;
+ break;
+ }
+ }
+ }
+
+ bool all_types_the_same = true;
+ Type *last_type = nullptr;
+ for_array(i, ce->args) {
+ Operand *op = &ops[i];
+ if (is_type_tuple(op->type)) {
+ if (last_type == nullptr) {
+ op->type->Tuple.variables[0]->type;
+ }
+ for (Entity *v : op->type->Tuple.variables) {
+ if (!are_types_identical(last_type, v->type)) {
+ all_types_the_same = false;
+ break;
+ }
+ last_type = v->type;
+ }
+ } else {
+ if (last_type == nullptr) {
+ last_type = op->type;
+ } else {
+ if (!are_types_identical(last_type, op->type)) {
+ all_types_the_same = false;
+ break;
+ }
+ last_type = op->type;
+ }
+ }
+ }
+
+ if (all_types_the_same) {
+ operand->type = alloc_type_array(last_type, value_count);
+ operand->mode = Addressing_Value;
+ } else {
+ Type *st = alloc_type_struct_complete();
+ st->Struct.fields = slice_make<Entity *>(permanent_allocator(), value_count);
+ st->Struct.tags = gb_alloc_array(permanent_allocator(), String, value_count);
+ st->Struct.offsets = gb_alloc_array(permanent_allocator(), i64, value_count);
+
+ Scope *scope = create_scope(c->info, nullptr);
+
+ Token token = {};
+ token.kind = Token_Ident;
+ token.pos = ast_token(call).pos;
+
+ isize index = 0;
+ for_array(i, ce->args) {
+ Operand *op = &ops[i];
+ if (is_type_tuple(op->type)) {
+ for (Entity *v : op->type->Tuple.variables) {
+ Type *t = default_type(v->type);
+ if (is_type_untyped(t)) {
+ gbString s = expr_to_string(op->expr);
+ error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
+ gb_string_free(s);
+ return false;
+ }
+
+ gbString s = gb_string_make_reserve(permanent_allocator(), 32);
+ s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
+ token.string = make_string_c(s);
+ Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
+ st->Struct.fields[index++] = e;
+ }
+ } else {
+ Type *t = default_type(op->type);
+ if (is_type_untyped(t)) {
+ gbString s = expr_to_string(op->expr);
+ error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
+ gb_string_free(s);
+ return false;
+ }
+
+ gbString s = gb_string_make_reserve(permanent_allocator(), 32);
+ s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
+ token.string = make_string_c(s);
+ Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
+ st->Struct.fields[index++] = e;
+ }
+ }
+
+
+ gb_unused(type_size_of(st));
+
+ operand->type = st;
+ operand->mode = Addressing_Value;
+ }
+ break;
+ }
+
+
case BuiltinProc_min: {
// min :: proc($T: typeid) -> ordered
// min :: proc(a: ..ordered) -> ordered
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index c7bd3a7a7..c4e487560 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -26,6 +26,7 @@ enum BuiltinProcId {
BuiltinProc_conj,
BuiltinProc_expand_values,
+ BuiltinProc_compress_values,
BuiltinProc_min,
BuiltinProc_max,
@@ -376,6 +377,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("expand_values"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
+ {STR_LIT("compress_values"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("min"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("max"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index ae1e87f18..f51ed2b4d 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -2246,6 +2246,68 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
return lb_emit_load(p, tuple);
}
+ case BuiltinProc_compress_values: {
+ isize value_count = 0;
+ for (Ast *arg : ce->args) {
+ Type *t = arg->tav.type;
+ if (is_type_tuple(t)) {
+ value_count += t->Tuple.variables.count;
+ } else {
+ value_count += 1;
+ }
+ }
+
+ if (value_count == 1) {
+ lbValue x = lb_build_expr(p, ce->args[0]);
+ x = lb_emit_conv(p, x, tv.type);
+ return x;
+ }
+
+ Type *dt = base_type(tv.type);
+ lbAddr addr = lb_add_local_generated(p, tv.type, true);
+ if (is_type_struct(dt) || is_type_tuple(dt)) {
+ i32 index = 0;
+ for (Ast *arg : ce->args) {
+ lbValue x = lb_build_expr(p, arg);
+ if (is_type_tuple(x.type)) {
+ for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
+ lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
+ lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
+ y = lb_emit_conv(p, y, type_deref(ptr.type));
+ lb_emit_store(p, ptr, y);
+ }
+ } else {
+ lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
+ x = lb_emit_conv(p, x, type_deref(ptr.type));
+ lb_emit_store(p, ptr, x);
+ }
+ }
+ GB_ASSERT(index == value_count);
+ } else if (is_type_array_like(dt)) {
+ i32 index = 0;
+ for (Ast *arg : ce->args) {
+ lbValue x = lb_build_expr(p, arg);
+ if (is_type_tuple(x.type)) {
+ for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
+ lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
+ lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
+ y = lb_emit_conv(p, y, type_deref(ptr.type));
+ lb_emit_store(p, ptr, y);
+ }
+ } else {
+ lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
+ x = lb_emit_conv(p, x, type_deref(ptr.type));
+ lb_emit_store(p, ptr, x);
+ }
+ }
+ GB_ASSERT(index == value_count);
+ } else {
+ GB_PANIC("TODO(bill): compress_values -> %s", type_to_string(tv.type));
+ }
+
+ return lb_addr_load(p, addr);
+ }
+
case BuiltinProc_min: {
Type *t = type_of_expr(expr);
if (ce->args.count == 2) {