Browse Source

Slices now have a capacity.

Ginger Bill 8 years ago
parent
commit
f29e303ce7
8 changed files with 224 additions and 143 deletions
  1. 10 8
      code/demo.odin
  2. 29 6
      core/_preload.odin
  3. 23 12
      src/check_expr.c
  4. 1 0
      src/check_stmt.c
  5. 98 92
      src/ir.c
  6. 8 1
      src/ir_print.c
  7. 31 13
      src/parser.c
  8. 24 11
      src/types.c

+ 10 - 8
code/demo.odin

@@ -8,15 +8,17 @@
 #import "strconv.odin";
 
 main :: proc() {
-	buf: [64]byte;
-	// len := strconv.generic_ftoa(buf[..], 123.5431, 'f', 4, 64);
-	x := 624.123;
-	s := strconv.format_float(buf[..], x, 'f', 6, 64);
+	// buf: [64]byte;
+	// // len := strconv.generic_ftoa(buf[..], 123.5431, 'f', 4, 64);
+	// x := 624.123;
+	// s := strconv.format_float(buf[..], x, 'f', 6, 64);
+	// fmt.println(s);
+	// fmt.printf("%3d\n", 102);
+
+	a: [10]int;
+	s := a[..0];
+	append(s, 1, 2, 6, 3, 6, 5, 5, 5, 5, 1, 2);
 	fmt.println(s);
-	fmt.printf("%3d\n", 102);
-
-	a := [..]int{0, 1, 2, 3, 4, 5, 6};
-	fmt.println(a[0..4]);
 
 
 when false {

+ 29 - 6
core/_preload.odin

@@ -321,12 +321,12 @@ __bounds_check_error :: proc(file: string, line, column: int, index, count: int)
 	__debug_trap();
 }
 
-__slice_expr_error :: proc(file: string, line, column: int, low, high: int) {
-	if 0 <= low && low <= high {
+__slice_expr_error :: proc(file: string, line, column: int, low, high, max: int) {
+	if 0 <= low && low <= high && high <= max {
 		return;
 	}
-	fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d..%d]\n",
-	            file, line, column, low, high);
+	fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d..%d..%d]\n",
+	            file, line, column, low, high, max);
 	__debug_trap();
 }
 __substring_expr_error :: proc(file: string, line, column: int, low, high: int) {
@@ -361,8 +361,9 @@ Raw_String :: struct #ordered {
 };
 
 Raw_Slice :: struct #ordered {
-	data:  rawptr,
-	count: int,
+	data:     rawptr,
+	count:    int,
+	capacity: int,
 };
 
 Raw_Dynamic_Array :: struct #ordered {
@@ -451,6 +452,28 @@ __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: in
 	return array.count;
 }
 
+__slice_append :: proc(slice_: rawptr, elem_size, elem_align: int,
+                       items: rawptr, item_count: int) -> int {
+	slice := cast(^Raw_Slice)slice_;
+
+	if item_count <= 0 || items == nil {
+		return slice.count;
+	}
+
+
+	ok := true;
+	if item_count > (slice.capacity-slice.count) {
+		item_count = (slice.capacity-slice.count);
+	}
+	if item_count > 0 {
+		data := cast(^byte)slice.data;
+		assert(data != nil);
+		mem.copy(data + (elem_size*slice.count), items, elem_size * item_count);
+		slice.count += item_count;
+	}
+	return slice.count;
+}
+
 
 // Map stuff
 

+ 23 - 12
src/check_expr.c

@@ -3057,19 +3057,26 @@ 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)
+		// append :: proc([]Type, item: ..Type)
 		Type *type = operand->type;
 		type = base_type(type);
-		if (!is_type_dynamic_array(type)) {
+		if (!is_type_dynamic_array(type) && !is_type_slice(type)) {
 			gbString str = type_to_string(type);
-			error_node(operand->expr, "Expected a dynamic array, got `%s`", str);
+			error_node(operand->expr, "Expected a slice or dynamic array, got `%s`", str);
 			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 *elem = NULL;
+		Type *slice_elem = NULL;
+		if (is_type_dynamic_array(type)) {
+			// TODO(bill): Semi-memory leaks
+			elem = type->DynamicArray.elem;
+		} else {
+			elem = type->Slice.elem;
+		}
+		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);
@@ -3404,7 +3411,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 	} break;
 
 	case BuiltinProc_swizzle: {
-		// swizzle :: proc(v: {N}T, T...) -> {M}T
+		// swizzle :: proc(v: {N}T, T..) -> {M}T
 		Type *vector_type = base_type(operand->type);
 		if (!is_type_vector(vector_type)) {
 			gbString type_str = type_to_string(operand->type);
@@ -5433,13 +5440,17 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		str = write_expr_to_string(str, se->expr);
 		str = gb_string_appendc(str, "[");
 		str = write_expr_to_string(str, se->low);
-		str = gb_string_appendc(str, ":");
+		str = gb_string_appendc(str, "..");
 		str = write_expr_to_string(str, se->high);
+		if (se->index3) {
+			str = gb_string_appendc(str, "..");
+			str = write_expr_to_string(str, se->max);
+		}
 		str = gb_string_appendc(str, "]");
 	case_end;
 
 	case_ast_node(e, Ellipsis, node);
-		str = gb_string_appendc(str, "...");
+		str = gb_string_appendc(str, "..");
 	case_end;
 
 	case_ast_node(fv, FieldValue, node);
@@ -5458,7 +5469,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		if (at->count != NULL &&
 		    at->count->kind == AstNode_UnaryExpr &&
 		    at->count->UnaryExpr.op.kind == Token_Ellipsis) {
-			str = gb_string_appendc(str, "...");
+			str = gb_string_appendc(str, "..");
 		} else {
 			str = write_expr_to_string(str, at->count);
 		}
@@ -5467,7 +5478,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 	case_end;
 
 	case_ast_node(at, DynamicArrayType, node);
-		str = gb_string_appendc(str, "[...]");
+		str = gb_string_appendc(str, "[..]");
 		str = write_expr_to_string(str, at->elem);
 	case_end;
 
@@ -5500,7 +5511,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 			str = gb_string_appendc(str, ": ");
 		}
 		if (f->flags&FieldFlag_ellipsis) {
-			str = gb_string_appendc(str, "...");
+			str = gb_string_appendc(str, "..");
 		}
 		str = write_expr_to_string(str, f->type);
 	case_end;

+ 1 - 0
src/check_stmt.c

@@ -1258,6 +1258,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			case Entity_Variable: {
 				Type *t = base_type(type_deref(e->type));
 				if (is_type_struct(t) || is_type_raw_union(t)) {
+					// TODO(bill): Make it work for unions too
 					Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
 					GB_ASSERT(found != NULL);
 					for_array(i, (*found)->elements.entries) {

+ 98 - 92
src/ir.c

@@ -218,6 +218,7 @@ struct irProcedure {
 		TokenPos pos;                                                 \
 		irValue *low;                                                 \
 		irValue *high;                                                \
+		irValue *max;                                                 \
 		bool     is_substring;                                        \
 	})                                                                \
 	IR_INSTR_KIND(DebugDeclare, struct { \
@@ -956,11 +957,12 @@ irValue *ir_make_instr_bounds_check(irProcedure *p, TokenPos pos, irValue *index
 	v->Instr.BoundsCheck.len   = len;
 	return v;
 }
-irValue *ir_make_instr_slice_bounds_check(irProcedure *p, TokenPos pos, irValue *low, irValue *high, bool is_substring) {
+irValue *ir_make_instr_slice_bounds_check(irProcedure *p, TokenPos pos, irValue *low, irValue *high, irValue *max, bool is_substring) {
 	irValue *v = ir_alloc_instr(p, irInstr_SliceBoundsCheck);
 	v->Instr.SliceBoundsCheck.pos  = pos;
 	v->Instr.SliceBoundsCheck.low  = low;
 	v->Instr.SliceBoundsCheck.high = high;
+	v->Instr.SliceBoundsCheck.max  = max;
 	v->Instr.SliceBoundsCheck.is_substring = is_substring;
 	return v;
 }
@@ -1845,6 +1847,7 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) {
 		switch (index) {
 		case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break;
 		case 1: result_type = make_type_pointer(a, t_int); break;
+		case 2: result_type = make_type_pointer(a, t_int); break;
 		}
 	} else if (is_type_string(t)) {
 		switch (index) {
@@ -2070,6 +2073,11 @@ irValue *ir_slice_count(irProcedure *proc, irValue *slice) {
 	GB_ASSERT(t->kind == Type_Slice);
 	return ir_emit_struct_ev(proc, slice, 1);
 }
+irValue *ir_slice_capacity(irProcedure *proc, irValue *slice) {
+	Type *t = base_type(ir_type(slice));
+	GB_ASSERT(t->kind == Type_Slice);
+	return ir_emit_struct_ev(proc, slice, 2);
+}
 
 irValue *ir_dynamic_array_elem(irProcedure *proc, irValue *da) {
 	Type *t = ir_type(da);
@@ -2106,8 +2114,19 @@ irValue *ir_string_len(irProcedure *proc, irValue *string) {
 }
 
 
+void ir_fill_slice(irProcedure *proc, irValue *slice_ptr, irValue *data, irValue *count, irValue *capacity) {
+	Type *t = ir_type(slice_ptr);
+	GB_ASSERT(is_type_pointer(t));
+	t = type_deref(t);
+	GB_ASSERT(is_type_slice(t));
+	ir_emit_store(proc, ir_emit_struct_ep(proc, slice_ptr, 0), data);
+	ir_emit_store(proc, ir_emit_struct_ep(proc, slice_ptr, 1), count);
+	ir_emit_store(proc, ir_emit_struct_ep(proc, slice_ptr, 2), capacity);
+}
+
+
 
-irValue *ir_add_local_slice(irProcedure *proc, Type *slice_type, irValue *base, irValue *low, irValue *high) {
+irValue *ir_add_local_slice(irProcedure *proc, Type *slice_type, irValue *base, irValue *low, irValue *high, irValue *max) {
 	// TODO(bill): array bounds checking for slice creation
 	// TODO(bill): check that low < high <= max
 	gbAllocator a = proc->module->allocator;
@@ -2123,8 +2142,16 @@ irValue *ir_add_local_slice(irProcedure *proc, Type *slice_type, irValue *base,
 		case Type_Pointer: high = v_one;                     break;
 		}
 	}
+	if (max == NULL) {
+		switch (bt->kind) {
+		case Type_Array:   high = ir_array_len(proc, base);      break;
+		case Type_Slice:   high = ir_slice_capacity(proc, base); break;
+		case Type_Pointer: high = v_one;                         break;
+		}
+	}
 
 	irValue *len = ir_emit_arith(proc, Token_Sub, high, low, t_int);
+	irValue *cap = ir_emit_arith(proc, Token_Sub, max, low, t_int);
 
 	irValue *elem = NULL;
 	switch (bt->kind) {
@@ -2136,14 +2163,7 @@ irValue *ir_add_local_slice(irProcedure *proc, Type *slice_type, irValue *base,
 	elem = ir_emit_ptr_offset(proc, elem, low);
 
 	irValue *slice = ir_add_local_generated(proc, slice_type);
-
-	irValue *gep = NULL;
-	gep = ir_emit_struct_ep(proc, slice, 0);
-	ir_emit_store(proc, gep, elem);
-
-	gep = ir_emit_struct_ep(proc, slice, 1);
-	ir_emit_store(proc, gep, len);
-
+	ir_fill_slice(proc, slice, elem, len, cap);
 	return slice;
 }
 
@@ -2386,7 +2406,8 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
 		ir_emit_store(proc, elem_ptr, elem);
 
 		irValue *len  = ir_string_len(proc, value);
-		irValue *slice = ir_add_local_slice(proc, dst, elem_ptr, v_zero, len);
+		irValue *cap  = len;
+		irValue *slice = ir_add_local_slice(proc, dst, elem_ptr, v_zero, len, cap);
 		return ir_emit_load(proc, slice);
 	}
 
@@ -2757,7 +2778,7 @@ void ir_emit_bounds_check(irProcedure *proc, Token token, irValue *index, irValu
 	ir_emit(proc, ir_make_instr_bounds_check(proc, token.pos, index, len));
 }
 
-void ir_emit_slice_bounds_check(irProcedure *proc, Token token, irValue *low, irValue *high, bool is_substring) {
+void ir_emit_slice_bounds_check(irProcedure *proc, Token token, irValue *low, irValue *high, irValue *max, bool is_substring) {
 	if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
 		return;
 	}
@@ -2765,10 +2786,11 @@ void ir_emit_slice_bounds_check(irProcedure *proc, Token token, irValue *low, ir
 	low  = ir_emit_conv(proc, low,  t_int);
 	high = ir_emit_conv(proc, high, t_int);
 
-	ir_emit(proc, ir_make_instr_slice_bounds_check(proc, token.pos, low, high, is_substring));
+	ir_emit(proc, ir_make_instr_slice_bounds_check(proc, token.pos, low, high, max, is_substring));
 }
 
 
+
 ////////////////////////////////////////////////////////////////
 //
 // @Build
@@ -3161,7 +3183,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 
 					irValue *count = ir_emit_conv(proc, ir_build_expr(proc, ce->args.e[1]), t_int);
 
-					ir_emit_slice_bounds_check(proc, ast_node_token(ce->args.e[1]), v_zero, count, false);
+					ir_emit_slice_bounds_check(proc, ast_node_token(ce->args.e[1]), v_zero, count, count, false);
 
 					irValue *slice_size = ir_emit_arith(proc, Token_Mul, elem_size, count, t_int);
 
@@ -3173,10 +3195,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 					irValue *ptr = ir_emit_conv(proc, call, ptr_type);
 					irValue *slice = ir_add_local_generated(proc, slice_type);
 
-					irValue *gep0 = ir_emit_struct_ep(proc, slice, 0);
-					irValue *gep1 = ir_emit_struct_ep(proc, slice, 1);
-					ir_emit_store(proc, gep0, ptr);
-					ir_emit_store(proc, gep1, count);
+					ir_fill_slice(proc, slice, ptr, count, count);
 					return ir_emit_load(proc, slice);
 				} break;
 
@@ -3315,8 +3334,16 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 					Type *type = ir_type(array_ptr);
 					GB_ASSERT(is_type_pointer(type));
 					type = base_type(type_deref(type));
-					GB_ASSERT(is_type_dynamic_array(type));
-					Type *elem_type = type->DynamicArray.elem;
+					Type *elem_type = NULL;
+					bool is_slice = false;
+					if (is_type_dynamic_array(type)) {
+						elem_type = type->DynamicArray.elem;
+					} else if (is_type_slice(type)) {
+						is_slice = true;
+						elem_type = type->Slice.elem;
+					} else {
+						GB_PANIC("Invalid type to append");
+					}
 
 					irValue *elem_size  = ir_make_const_int(a, type_size_of(a, elem_type));
 					irValue *elem_align = ir_make_const_int(a, type_align_of(a, elem_type));
@@ -3373,10 +3400,8 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 							}
 
 							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);
+							ir_fill_slice(proc, slice, base_elem, len, len);
 						}
 
 						arg_count = 2;
@@ -3393,6 +3418,10 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 					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);
+
+					if (is_slice) {
+					return ir_emit_global_call(proc, "__slice_append", daa_args, 5);
+					}
 					return ir_emit_global_call(proc, "__dynamic_array_append", daa_args, 5);
 				} break;
 
@@ -3538,8 +3567,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 
 					Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ir_type(ptr)));
 					irValue *slice = ir_add_local_generated(proc, slice_type);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), ptr);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), count);
+					ir_fill_slice(proc, slice, ptr, count, count);
 					return ir_emit_load(proc, slice);
 				} break;
 
@@ -3556,9 +3584,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 					irValue *ptr   = ir_emit_conv(proc, ir_slice_elem(proc, s), t_u8_ptr);
 					irValue *count = ir_slice_count(proc, s);
 					count = ir_emit_arith(proc, Token_Mul, count, ir_make_const_int(proc->module->allocator, elem_size), t_int);
-
-					ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), ptr);
-					ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), count);
+					ir_fill_slice(proc, slice, ptr, count, count);
 					return ir_emit_load(proc, slice);
 				} break;
 
@@ -3681,11 +3707,9 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
 					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 *base_elem = ir_emit_array_epi(proc, base_array, 0);
 				irValue *len = ir_make_const_int(allocator, slice_len);
-				ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len);
+				ir_fill_slice(proc, slice, base_elem, len, len);
 			}
 
 			arg_count = type->param_count;
@@ -4076,9 +4100,11 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 		gbAllocator a = proc->module->allocator;
 		irValue *low  = v_zero;
 		irValue *high = NULL;
+		irValue *max = NULL;
 
 		if (se->low  != NULL)    low  = ir_build_expr(proc, se->low);
 		if (se->high != NULL)    high = ir_build_expr(proc, se->high);
+		if (se->max  != NULL)    max  = ir_build_expr(proc, se->max);
 		irValue *addr = ir_build_addr(proc, se->expr).addr;
 		irValue *base = ir_emit_load(proc, addr);
 		Type *type = base_type(ir_type(base));
@@ -4096,16 +4122,15 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 			Type *slice_type = type;
 
 			if (high == NULL) high = ir_slice_count(proc, base);
+			if (max == NULL)  max =  ir_slice_capacity(proc, base);
 
-			ir_emit_slice_bounds_check(proc, se->open, low, high, false);
+			ir_emit_slice_bounds_check(proc, se->open, low, high, max, false);
 
 			irValue *elem  = ir_emit_ptr_offset(proc, ir_slice_elem(proc, base), low);
 			irValue *len   = ir_emit_arith(proc, Token_Sub, high, low, t_int);
+			irValue *cap   = ir_emit_arith(proc, Token_Sub, max, low, t_int);
 			irValue *slice = ir_add_local_generated(proc, slice_type);
-
-			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), elem);
-			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len);
-
+			ir_fill_slice(proc, slice, elem, len, cap);
 			return ir_make_addr(slice);
 		}
 
@@ -4113,16 +4138,15 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 			Type *dynamic_array = type;
 
 			if (high == NULL) high = ir_dynamic_array_count(proc, base);
+			if (max == NULL)  max = ir_dynamic_array_capacity(proc, base);
 
-			ir_emit_slice_bounds_check(proc, se->open, low, high, false);
+			ir_emit_slice_bounds_check(proc, se->open, low, high, max, false);
 
 			irValue *elem  = ir_emit_ptr_offset(proc, ir_dynamic_array_elem(proc, base), low);
 			irValue *len   = ir_emit_arith(proc, Token_Sub, high, low, t_int);
+			irValue *cap   = ir_emit_arith(proc, Token_Sub, max, low, t_int);
 			irValue *slice = ir_add_local_generated(proc, dynamic_array);
-
-			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), elem);
-			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len);
-
+			ir_fill_slice(proc, slice, elem, len, cap);
 			return ir_make_addr(slice);
 		}
 
@@ -4131,38 +4155,31 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 			Type *slice_type = make_type_slice(a, type->Array.elem);
 
 			if (high == NULL) high = ir_array_len(proc, base);
+			if (max == NULL)  max  = ir_array_len(proc, base);
 
-			ir_emit_slice_bounds_check(proc, se->open, low, high, false);
+			ir_emit_slice_bounds_check(proc, se->open, low, high, max, false);
 
 			irValue *elem = ir_emit_ptr_offset(proc, ir_array_elem(proc, addr), low);
 			irValue *len  = ir_emit_arith(proc, Token_Sub, high, low, t_int);
+			irValue *cap  = ir_emit_arith(proc, Token_Sub, max, low, t_int);
 			irValue *slice = ir_add_local_generated(proc, slice_type);
-
-			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 0), elem);
-			ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len);
-
+			ir_fill_slice(proc, slice, elem, len, cap);
 			return ir_make_addr(slice);
 		}
 
 		case Type_Basic: {
 			GB_ASSERT(type == t_string);
-			if (high == NULL) {
-				high = ir_string_len(proc, base);
-			}
-
-			ir_emit_slice_bounds_check(proc, se->open, low, high, true);
+			if (high == NULL) high = ir_string_len(proc, base);
+			if (max == NULL)  max = ir_string_len(proc, base);
 
-			irValue *elem, *len;
-			len = ir_emit_arith(proc, Token_Sub, high, low, t_int);
+			ir_emit_slice_bounds_check(proc, se->open, low, high, max, true);
 
-			elem = ir_string_elem(proc, base);
-			elem = ir_emit_ptr_offset(proc, elem, low);
+			irValue *elem = ir_emit_ptr_offset(proc, ir_string_elem(proc, base), low);
+			irValue *len = ir_emit_arith(proc, Token_Sub, high, low, t_int);
 
 			irValue *str = ir_add_local_generated(proc, t_string);
-			irValue *gep0 = ir_emit_struct_ep(proc, str, 0);
-			irValue *gep1 = ir_emit_struct_ep(proc, str, 1);
-			ir_emit_store(proc, gep0, elem);
-			ir_emit_store(proc, gep1, len);
+			ir_emit_store(proc, ir_emit_struct_ep(proc, str, 0), elem);
+			ir_emit_store(proc, ir_emit_struct_ep(proc, str, 1), len);
 
 			return ir_make_addr(str);
 		} break;
@@ -4369,13 +4386,8 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 					ir_emit_store(proc, offset, ev);
 				}
 
-				irValue *gep0 = ir_emit_struct_ep(proc, v, 0);
-				irValue *gep1 = ir_emit_struct_ep(proc, v, 1);
-				irValue *gep2 = ir_emit_struct_ep(proc, v, 1);
-
-				ir_emit_store(proc, gep0, data);
-				ir_emit_store(proc, gep1, ir_make_const_int(proc->module->allocator, slice->ConstantSlice.count));
-				ir_emit_store(proc, gep2, ir_make_const_int(proc->module->allocator, slice->ConstantSlice.count));
+				irValue *count = ir_make_const_int(proc->module->allocator, slice->ConstantSlice.count);
+				ir_fill_slice(proc, v, data, count, count);
 			}
 		} break;
 
@@ -6030,16 +6042,6 @@ void ir_add_foreign_library_path(irModule *m, Entity *e) {
 	array_add(&m->foreign_library_paths, library_path);
 }
 
-void ir_fill_slice(irProcedure *proc, irValue *slice_ptr, irValue *data, irValue *count) {
-	Type *t = ir_type(slice_ptr);
-	GB_ASSERT(is_type_pointer(t));
-	t = type_deref(t);
-	GB_ASSERT(is_type_slice(t));
-	irValue *elem = ir_emit_struct_ep(proc, slice_ptr, 0);
-	irValue *len  = ir_emit_struct_ep(proc, slice_ptr, 1);
-	ir_emit_store(proc, elem, data);
-	ir_emit_store(proc, len, count);
-}
 
 void ir_gen_tree(irGen *s) {
 	irModule *m = &s->module;
@@ -6376,9 +6378,10 @@ void ir_gen_tree(irGen *s) {
 				irValue *global_type_table = ir_find_global_variable(proc, str_lit("__type_table"));
 				Type *type = base_type(type_deref(ir_type(ir_global_type_info_data)));
 				GB_ASSERT(is_type_array(type));
+				irValue *len = ir_make_const_int(proc->module->allocator, type->Array.count);
 				ir_fill_slice(proc, global_type_table,
 				              ir_emit_array_epi(proc, ir_global_type_info_data, 0),
-				              ir_make_const_int(proc->module->allocator, type->Array.count));
+				              len, len);
 			}
 
 
@@ -6562,8 +6565,9 @@ void ir_gen_tree(irGen *s) {
 						}
 					}
 
-					ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types,   ir_make_const_int(a, t->Tuple.variable_count));
-					ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names,   ir_make_const_int(a, t->Tuple.variable_count));
+					irValue *count = ir_make_const_int(a, t->Tuple.variable_count);
+					ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types, count, count);
+					ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names, count, count);
 				} break;
 				case Type_Record: {
 					switch (t->Record.kind) {
@@ -6609,9 +6613,10 @@ void ir_gen_tree(irGen *s) {
 							ir_emit_store(proc, offset, ir_make_const_int(a, foffset));
 						}
 
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types,   ir_make_const_int(a, t->Record.field_count));
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names,   ir_make_const_int(a, t->Record.field_count));
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 2), memory_offsets, ir_make_const_int(a, t->Record.field_count));
+						irValue *count = ir_make_const_int(a, t->Record.field_count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types,   count, count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names,   count, count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 2), memory_offsets, count, count);
 					} break;
 					case TypeRecord_Union: {
 						ir_emit_comment(proc, str_lit("Type_Info_Union"));
@@ -6652,9 +6657,10 @@ void ir_gen_tree(irGen *s) {
 							}
 
 
-							ir_fill_slice(proc, ir_emit_struct_ep(proc, common_fields, 0), memory_types,   ir_make_const_int(a, field_count));
-							ir_fill_slice(proc, ir_emit_struct_ep(proc, common_fields, 1), memory_names,   ir_make_const_int(a, field_count));
-							ir_fill_slice(proc, ir_emit_struct_ep(proc, common_fields, 2), memory_offsets, ir_make_const_int(a, field_count));
+							irValue *count = ir_make_const_int(a, field_count);
+							ir_fill_slice(proc, ir_emit_struct_ep(proc, common_fields, 0), memory_types,   count, count);
+							ir_fill_slice(proc, ir_emit_struct_ep(proc, common_fields, 1), memory_names,   count, count);
+							ir_fill_slice(proc, ir_emit_struct_ep(proc, common_fields, 2), memory_offsets, count, count);
 						}
 
 						{
@@ -6681,9 +6687,8 @@ void ir_gen_tree(irGen *s) {
 							}
 
 							irValue *count = ir_make_const_int(a, variant_count);
-
-							ir_fill_slice(proc, variant_names, memory_names, count);
-							ir_fill_slice(proc, variant_types, memory_types, count);
+							ir_fill_slice(proc, variant_names, memory_names, count, count);
+							ir_fill_slice(proc, variant_types, memory_types, count, count);
 						}
 
 					} break;
@@ -6716,9 +6721,10 @@ void ir_gen_tree(irGen *s) {
 							}
 						}
 
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types,   ir_make_const_int(a, t->Record.field_count));
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names,   ir_make_const_int(a, t->Record.field_count));
-						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 2), memory_offsets, ir_make_const_int(a, t->Record.field_count));
+						irValue *count = ir_make_const_int(a, t->Record.field_count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 0), memory_types,   count, count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 1), memory_names,   count, count);
+						ir_fill_slice(proc, ir_emit_struct_ep(proc, record, 2), memory_offsets, count, count);
 					} break;
 					case TypeRecord_Enum:
 						ir_emit_comment(proc, str_lit("Type_Info_Enum"));

+ 8 - 1
src/ir_print.c

@@ -191,7 +191,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 	case Type_Slice:
 		ir_fprintf(f, "{");
 		ir_print_type(f, m, t->Slice.elem);
-		ir_fprintf(f, "*, i%lld}", word_bits);
+		ir_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits);
 		return;
 	case Type_DynamicArray:
 		ir_fprintf(f, "{");
@@ -1249,6 +1249,13 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 		ir_fprintf(f, " ");
 		ir_print_value(f, m, bc->high, t_int);
 
+		if (!bc->is_substring) {
+			ir_fprintf(f, ", ");
+			ir_print_type(f, m, t_int);
+			ir_fprintf(f, " ");
+			ir_print_value(f, m, bc->max, t_int);
+		}
+
 		ir_fprintf(f, ")\n");
 	} break;
 

+ 31 - 13
src/parser.c

@@ -151,8 +151,9 @@ AST_NODE_KIND(_ExprBegin,  "",  i32) \
 	AST_NODE_KIND(DerefExpr,    "dereference expression", struct { Token op; AstNode *expr; }) \
 	AST_NODE_KIND(SliceExpr, "slice expression", struct { \
 		AstNode *expr; \
-		Token open, close, interval; \
-		AstNode *low, *high; \
+		Token open, close; \
+		bool index3; \
+		AstNode *low, *high, *max; \
 	}) \
 	AST_NODE_KIND(CallExpr,     "call expression", struct { \
 		AstNode *    proc; \
@@ -667,14 +668,15 @@ AstNode *ast_index_expr(AstFile *f, AstNode *expr, AstNode *index, Token open, T
 }
 
 
-AstNode *ast_slice_expr(AstFile *f, AstNode *expr, Token open, Token close, Token interval, AstNode *low, AstNode *high) {
+AstNode *ast_slice_expr(AstFile *f, AstNode *expr, Token open, Token close, bool index3, AstNode *low, AstNode *high, AstNode *max) {
 	AstNode *result = make_ast_node(f, AstNode_SliceExpr);
 	result->SliceExpr.expr = expr;
 	result->SliceExpr.open = open;
 	result->SliceExpr.close = close;
-	result->SliceExpr.interval = interval;
+	result->SliceExpr.index3 = index3;
 	result->SliceExpr.low = low;
 	result->SliceExpr.high = high;
+	result->SliceExpr.max = max;
 	return result;
 }
 
@@ -1945,7 +1947,9 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
 				// TODO(bill): Handle this
 			}
 			Token open = {0}, close = {0}, interval = {0};
-			AstNode *indices[2] = {0};
+			AstNode *indices[3] = {0};
+			isize ellipsis_count = 0;
+			Token ellipses[2] = {0};
 
 			f->expr_level++;
 			open = expect_token(f, Token_OpenBracket);
@@ -1955,23 +1959,37 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
 			}
 			bool is_index = true;
 
-			if (f->curr_token.kind == Token_Ellipsis) {
-				is_index = false;
-				interval = f->curr_token;
+			while (f->curr_token.kind == Token_Ellipsis && ellipsis_count < gb_count_of(ellipses)) {
+				ellipses[ellipsis_count++] = f->curr_token;
 				next_token(f);
-				if (f->curr_token.kind != Token_CloseBracket &&
+				if (f->curr_token.kind != Token_Ellipsis &&
+				    f->curr_token.kind != Token_CloseBracket &&
 				    f->curr_token.kind != Token_EOF) {
-					indices[1] = parse_expr(f, false);
+					indices[ellipsis_count] = parse_expr(f, false);
 				}
 			}
 
+
 			f->expr_level--;
 			close = expect_token(f, Token_CloseBracket);
 
-			if (is_index) {
-				operand = ast_index_expr(f, operand, indices[0], open, close);
+			if (ellipsis_count > 0) {
+				bool index3 = false;
+				if (ellipsis_count == 2) {
+					index3 = true;
+					// 2nd and 3rd index must be present
+					if (indices[1] == NULL) {
+						error(ellipses[0], "2nd index required in 3-index slice expression");
+						indices[1] = ast_bad_expr(f, ellipses[0], ellipses[1]);
+					}
+					if (indices[2] == NULL) {
+						error(ellipses[1], "3rd index required in 3-index slice expression");
+						indices[2] = ast_bad_expr(f, ellipses[1], close);
+					}
+				}
+				operand = ast_slice_expr(f, operand, open, close, index3, indices[0], indices[1], indices[2]);
 			} else {
-				operand = ast_slice_expr(f, operand, open, close, interval, indices[0], indices[1]);
+				operand = ast_index_expr(f, operand, indices[0], open, close);
 			}
 		} break;
 

+ 24 - 11
src/types.c

@@ -1090,6 +1090,7 @@ gb_global Entity *entity__any_data       = NULL;
 gb_global Entity *entity__string_data    = NULL;
 gb_global Entity *entity__string_count   = NULL;
 gb_global Entity *entity__slice_count    = NULL;
+gb_global Entity *entity__slice_capacity = NULL;
 
 gb_global Entity *entity__dynamic_array_count     = NULL;
 gb_global Entity *entity__dynamic_array_capacity  = NULL;
@@ -1251,6 +1252,7 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
 	} else if (type->kind == Type_Slice) {
 		String data_str     = str_lit("data");
 		String count_str    = str_lit("count");
+		String capacity_str = str_lit("capacity");
 
 		if (str_eq(field_name, data_str)) {
 			selection_add_index(&sel, 0);
@@ -1265,7 +1267,16 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
 
 			sel.entity = entity__slice_count;
 			return sel;
+		}  else if (str_eq(field_name, capacity_str)) {
+			selection_add_index(&sel, 2);
+			if (entity__slice_capacity == NULL) {
+				entity__slice_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2);
+			}
+
+			sel.entity = entity__slice_capacity;
+			return sel;
 		}
+
 	} else if (type->kind == Type_DynamicArray) {
 		String data_str      = str_lit("data");
 		String count_str     = str_lit("count");
@@ -1779,7 +1790,7 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
 
 
 	case Type_Slice: // ptr + count
-		return 2 * build_context.word_size;
+		return 3 * build_context.word_size;
 
 	case Type_Map: {
 		if (t->Map.count == 0) { // Dynamic
@@ -1875,23 +1886,24 @@ i64 type_offset_of(gbAllocator allocator, Type *t, i32 index) {
 	}  else if (t->kind == Type_Basic) {
 		if (t->Basic.kind == Basic_string) {
 			switch (index) {
-			case 0: return 0;           // data
+			case 0: return 0;                       // data
 			case 1: return build_context.word_size; // count
 			}
 		} else if (t->Basic.kind == Basic_any) {
 			switch (index) {
-			case 0: return 0;           // type_info
+			case 0: return 0;                       // type_info
 			case 1: return build_context.word_size; // data
 			}
 		}
 	} else if (t->kind == Type_Slice) {
 		switch (index) {
-		case 0: return 0;             // data
+		case 0: return 0;                         // data
 		case 1: return 1*build_context.word_size; // count
+		case 2: return 2*build_context.word_size; // capacity
 		}
 	} else if (t->kind == Type_DynamicArray) {
 		switch (index) {
-		case 0: return 0;             // data
+		case 0: return 0;                         // data
 		case 1: return 1*build_context.word_size; // count
 		case 2: return 2*build_context.word_size; // capacity
 		case 3: return 3*build_context.word_size; // allocator
@@ -1928,6 +1940,13 @@ i64 type_offset_of_from_selection(gbAllocator allocator, Type *type, Selection s
 					}
 				}
 				break;
+			case Type_Slice:
+				switch (index) {
+				case 0: t = t_rawptr; break;
+				case 1: t = t_int;    break;
+				case 2: t = t_int;    break;
+				}
+				break;
 			case Type_DynamicArray:
 				switch (index) {
 				case 0: t = t_rawptr;    break;
@@ -1936,12 +1955,6 @@ i64 type_offset_of_from_selection(gbAllocator allocator, Type *type, Selection s
 				case 3: t = t_allocator; break;
 				}
 				break;
-			case Type_Slice:
-				switch (index) {
-				case 0: t = t_rawptr; break;
-				case 1: t = t_int;    break;
-				}
-				break;
 			}
 		}
 	}