Browse Source

Variadic `append`

Ginger Bill 8 năm trước cách đây
mục cha
commit
43be91bca3
5 tập tin đã thay đổi với 105 bổ sung44 xóa
  1. 1 6
      code/demo.odin
  2. 8 6
      core/_preload.odin
  3. 20 19
      src/check_expr.c
  4. 1 1
      src/checker.c
  5. 75 12
      src/ir.c

+ 1 - 6
code/demo.odin

@@ -14,12 +14,7 @@ main :: proc() {
 	defer free(array);
 	reserve(^array, 10);
 
-	append(^array, 2);
-	append(^array, 3);
-	append(^array, 5);
-	append(^array, 7);
-	append(^array, 11);
-	append(^array, 13);
+	append(^array, 2, 3, 5, 7, 11, 13);
 	for val, idx in array {
 		fmt.println(val, idx);
 	}

+ 8 - 6
core/_preload.odin

@@ -352,20 +352,22 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capa
 }
 
 
-__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, item_ptr: rawptr) {
+__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
+                               items: rawptr, item_count: int) -> int {
 	array := cast(^Raw_Dynamic_Array)array_;
 
 	ok := true;
-	if array.data == nil || array.capacity <= array.count {
-		capacity := 2 * array.capacity + 8;
+	if array.data == nil || array.capacity <= array.count+item_count {
+		capacity := 2 * array.capacity + max(8, item_count);
 		ok := __dynamic_array_reserve(array, elem_size, elem_align, capacity);
 	}
 	if !ok {
 		// TODO(bill): Better error handling for failed reservation
-		return;
+		return array.count;
 	}
 	data := cast(^byte)array.data;
 	assert(data != nil);
-	mem.copy(data + (elem_size*array.count), item_ptr, elem_size);
-	array.count += 1;
+	mem.copy(data + (elem_size*array.count), items, elem_size * item_count);
+	array.count += item_count;
+	return array.count;
 }

+ 20 - 19
src/check_expr.c

@@ -19,6 +19,7 @@ void     check_stmt                     (Checker *c, AstNode *node, u32 flags);
 void     check_stmt_list                (Checker *c, AstNodeArray stmts, u32 flags);
 void     check_init_constant            (Checker *c, Entity *e, Operand *operand);
 bool     check_representable_as_constant(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value);
+Type *   check_call_arguments           (Checker *c, Operand *operand, Type *proc_type, AstNode *call);
 
 
 gb_inline Type *check_type(Checker *c, AstNode *expression) {
@@ -2598,6 +2599,8 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		}
 	}
 
+	Operand prev_operand = *operand;
+
 	switch (id) {
 	case BuiltinProc_new:
 	case BuiltinProc_new_slice:
@@ -2715,7 +2718,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 	} break;
 
 	case BuiltinProc_append: {
-		// append :: proc(^[dynamic]Type, item: Type) {
+		// append :: proc(^[dynamic]Type, item: ...Type) {
 		Type *type = operand->type;
 		if (!is_type_pointer(type)) {
 			gbString str = type_to_string(type);
@@ -2730,27 +2733,25 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 			gb_string_free(str);
 			return false;
 		}
+
+		// TODO(bill): Semi-memory leaks
 		Type *elem = type->DynamicArray.elem;
+		Type *slice_elem = make_type_slice(c->allocator, elem);
 
+		Type *proc_type_params = make_type_tuple(c->allocator);
+		proc_type_params->Tuple.variables = gb_alloc_array(c->allocator, Entity *, 2);
+		proc_type_params->Tuple.variable_count = 2;
+		proc_type_params->Tuple.variables[0] = make_entity_param(c->allocator, NULL, blank_token, operand->type, false, false);
+		proc_type_params->Tuple.variables[1] = make_entity_param(c->allocator, NULL, blank_token, slice_elem, false, false);
+		Type *proc_type = make_type_proc(c->allocator, NULL, proc_type_params, 2, NULL, false, true, ProcCC_Odin);
 
-		AstNode *item = ce->args.e[1];
-		Operand op = {0};
-		check_expr(c, &op, item);
-		if (op.mode == Addressing_Invalid) {
-			return false;
-		}
-		Type *arg_type = op.type;
-		if (!check_is_assignable_to(c, &op, elem)) {
-			gbString elem_str = type_to_string(elem);
-			gbString str = type_to_string(arg_type);
-			error_node(operand->expr, "Expected `%s` for `append` item, got `%s`", elem_str, str);
-			gb_string_free(str);
-			gb_string_free(elem_str);
+		check_call_arguments(c, &prev_operand, proc_type, call);
+
+		if (prev_operand.mode == Addressing_Invalid) {
 			return false;
 		}
-
-		operand->type = NULL;
-		operand->mode = Addressing_NoValue;
+		operand->mode = Addressing_Value;
+		operand->type = t_int;
 	} break;
 
 
@@ -3284,7 +3285,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 				return false;
 			}
 
-			if (!are_types_identical(operand->type, b.type)) {
+			if (!are_types_identical(a.type, b.type)) {
 				gbString type_a = type_to_string(a.type);
 				gbString type_b = type_to_string(b.type);
 				error_node(call,
@@ -3352,7 +3353,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 				return false;
 			}
 
-			if (!are_types_identical(operand->type, b.type)) {
+			if (!are_types_identical(a.type, b.type)) {
 				gbString type_a = type_to_string(a.type);
 				gbString type_b = type_to_string(b.type);
 				error_node(call,

+ 1 - 1
src/checker.c

@@ -173,7 +173,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 	{STR_LIT("free"),             1, false, Expr_Stmt},
 
 	{STR_LIT("reserve"),          2, false, Expr_Stmt},
-	{STR_LIT("append"),           2, false, Expr_Stmt},
+	{STR_LIT("append"),           1, true,  Expr_Expr},
 
 	{STR_LIT("size_of"),          1, false, Expr_Expr},
 	{STR_LIT("size_of_val"),      1, false, Expr_Expr},

+ 75 - 12
src/ir.c

@@ -3025,23 +3025,86 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 					GB_ASSERT(is_type_pointer(type));
 					type = base_type(type_deref(type));
 					GB_ASSERT(is_type_dynamic_array(type));
-					Type *elem = type->DynamicArray.elem;
+					Type *elem_type = type->DynamicArray.elem;
 
-					irValue *elem_size  = ir_make_const_int(a, type_size_of(proc->module->sizes, a, elem));
-					irValue *elem_align = ir_make_const_int(a, type_align_of(proc->module->sizes, a, elem));
+					irValue *elem_size  = ir_make_const_int(a, type_size_of(proc->module->sizes, a, elem_type));
+					irValue *elem_align = ir_make_const_int(a, type_align_of(proc->module->sizes, a, elem_type));
 
 					array_ptr = ir_emit_conv(proc, array_ptr, t_rawptr);
 
-					irValue *item_ptr = ir_add_local_generated(proc, elem);
-					irValue *item = ir_build_expr(proc, ce->args.e[1]);
-					ir_emit_store(proc, item_ptr, item);
+					isize arg_index = 0;
+					isize arg_count = 0;
+					for_array(i, ce->args) {
+						if (i == 0) {
+							continue;
+						}
+						AstNode *a = ce->args.e[i];
+						Type *at = base_type(type_of_expr(proc->module->info, a));
+						if (at->kind == Type_Tuple) {
+							arg_count += at->Tuple.variable_count;
+						} else {
+							arg_count++;
+						}
+					}
+					irValue **args = gb_alloc_array(proc->module->allocator, irValue *, arg_count);
+					bool vari_expand = ce->ellipsis.pos.line != 0;
 
-					irValue **args = gb_alloc_array(a, irValue *, 4);
-					args[0] = array_ptr;
-					args[1] = elem_size;
-					args[2] = elem_align;
-					args[3] = ir_emit_conv(proc, item_ptr, t_rawptr);
-					return ir_emit_global_call(proc, "__dynamic_array_append", args, 4);
+					for_array(i, ce->args) {
+						irValue *a = ir_build_expr(proc, ce->args.e[i]);
+						Type *at = ir_type(a);
+						if (at->kind == Type_Tuple) {
+							for (isize i = 0; i < at->Tuple.variable_count; i++) {
+								Entity *e = at->Tuple.variables[i];
+								irValue *v = ir_emit_struct_ev(proc, a, i);
+								args[arg_index++] = v;
+							}
+						} else {
+							args[arg_index++] = a;
+						}
+					}
+
+					if (!vari_expand) {
+						for (isize i = 1; i < arg_count; i++) {
+							args[i] = ir_emit_conv(proc, args[i], elem_type);
+						}
+					}
+
+					if (!vari_expand) {
+						ir_emit_comment(proc, str_lit("variadic call argument generation"));
+						Type *slice_type = make_type_slice(a, elem_type);
+						irValue *slice = ir_add_local_generated(proc, slice_type);
+						isize slice_len = arg_count-1;
+
+						if (slice_len > 0) {
+							irValue *base_array = ir_add_local_generated(proc, make_type_array(a, elem_type, slice_len));
+
+							for (isize i = 1, j = 0; i < arg_count; i++, j++) {
+								irValue *addr = ir_emit_array_epi(proc, base_array, j);
+								ir_emit_store(proc, addr, args[i]);
+							}
+
+							irValue *base_elem  = ir_emit_array_epi(proc, base_array, 0);
+							irValue *slice_elem = ir_emit_struct_ep(proc, slice,      0);
+							ir_emit_store(proc, slice_elem, base_elem);
+							irValue *len = ir_make_const_int(a, slice_len);
+							ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len);
+						}
+
+						arg_count = 2;
+						args[arg_count-1] = ir_emit_load(proc, slice);
+					}
+
+					irValue *item_slice = args[1];
+					irValue *items = ir_slice_elem(proc, item_slice);
+					irValue *item_count = ir_slice_count(proc, item_slice);
+
+					irValue **daa_args = gb_alloc_array(a, irValue *, 5);
+					daa_args[0] = array_ptr;
+					daa_args[1] = elem_size;
+					daa_args[2] = elem_align;
+					daa_args[3] = ir_emit_conv(proc, items, t_rawptr);
+					daa_args[4] = ir_emit_conv(proc, item_count, t_int);
+					return ir_emit_global_call(proc, "__dynamic_array_append", daa_args, 5);
 				} break;