소스 검색

Merge branch 'master' of https://github.com/odin-lang/Odin

Platin21 5 년 전
부모
커밋
d72a01a714
8개의 변경된 파일473개의 추가작업 그리고 202개의 파일을 삭제
  1. 3 49
      core/fmt/fmt.odin
  2. 3 1
      core/os/dir_windows.odin
  3. 2 0
      core/path/filepath/path.odin
  4. 12 147
      core/path/path.odin
  5. 295 4
      core/reflect/reflect.odin
  6. 1 1
      core/runtime/error_checks.odin
  7. 155 0
      core/unicode/utf8/utf8string/string.odin
  8. 2 0
      src/check_type.cpp

+ 3 - 49
core/fmt/fmt.odin

@@ -1811,7 +1811,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 		}
 
 
-		if info.maybe && len(info.variants) == 1 && reflect.is_pointer(info.variants[0]) {
+		if reflect.type_info_union_is_pure_maybe(info) {
 			if v.data == nil {
 				strings.write_string(fi.buf, "nil");
 			} else {
@@ -1877,59 +1877,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 		fmt_opaque(fi, v);
 
 	case runtime.Type_Info_Relative_Pointer:
-		ptr_any := any{v.data, info.base_integer.id};
-		ptr: rawptr;
-		switch i in &ptr_any {
-		case u8:    ptr = handle_relative_pointer(&i);
-		case u16:   ptr = handle_relative_pointer(&i);
-		case u32:   ptr = handle_relative_pointer(&i);
-		case u64:   ptr = handle_relative_pointer(&i);
-		case i8:    ptr = handle_relative_pointer(&i);
-		case i16:   ptr = handle_relative_pointer(&i);
-		case i32:   ptr = handle_relative_pointer(&i);
-		case i64:   ptr = handle_relative_pointer(&i);
-		case u16le: ptr = handle_relative_pointer(&i);
-		case u32le: ptr = handle_relative_pointer(&i);
-		case u64le: ptr = handle_relative_pointer(&i);
-		case i16le: ptr = handle_relative_pointer(&i);
-		case i32le: ptr = handle_relative_pointer(&i);
-		case i64le: ptr = handle_relative_pointer(&i);
-		case u16be: ptr = handle_relative_pointer(&i);
-		case u32be: ptr = handle_relative_pointer(&i);
-		case u64be: ptr = handle_relative_pointer(&i);
-		case i16be: ptr = handle_relative_pointer(&i);
-		case i32be: ptr = handle_relative_pointer(&i);
-		case i64be: ptr = handle_relative_pointer(&i);
-		}
+		ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id);
 		absolute_ptr := any{ptr, info.pointer.id};
 
 		fmt_value(fi, absolute_ptr, verb);
 
 	case runtime.Type_Info_Relative_Slice:
-		ptr_any := any{v.data, info.base_integer.id};
-		ptr: rawptr;
-		switch i in &ptr_any {
-		case u8:    ptr = handle_relative_pointer(&i);
-		case u16:   ptr = handle_relative_pointer(&i);
-		case u32:   ptr = handle_relative_pointer(&i);
-		case u64:   ptr = handle_relative_pointer(&i);
-		case i8:    ptr = handle_relative_pointer(&i);
-		case i16:   ptr = handle_relative_pointer(&i);
-		case i32:   ptr = handle_relative_pointer(&i);
-		case i64:   ptr = handle_relative_pointer(&i);
-		case u16le: ptr = handle_relative_pointer(&i);
-		case u32le: ptr = handle_relative_pointer(&i);
-		case u64le: ptr = handle_relative_pointer(&i);
-		case i16le: ptr = handle_relative_pointer(&i);
-		case i32le: ptr = handle_relative_pointer(&i);
-		case i64le: ptr = handle_relative_pointer(&i);
-		case u16be: ptr = handle_relative_pointer(&i);
-		case u32be: ptr = handle_relative_pointer(&i);
-		case u64be: ptr = handle_relative_pointer(&i);
-		case i16be: ptr = handle_relative_pointer(&i);
-		case i32be: ptr = handle_relative_pointer(&i);
-		case i64be: ptr = handle_relative_pointer(&i);
-		}
+		ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id);
 
 		if verb == 'p' {
 			fmt_pointer(fi, ptr, 'p');

+ 3 - 1
core/os/dir_windows.odin

@@ -4,7 +4,7 @@ import win32 "core:sys/windows"
 import "core:strings"
 import "core:time"
 
-read_dir :: proc(fd: Handle, n: int) -> (fi: []File_Info, err: Errno) {
+read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
 	find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
 		// Ignore "." and ".."
 		if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
@@ -53,6 +53,8 @@ read_dir :: proc(fd: Handle, n: int) -> (fi: []File_Info, err: Errno) {
 		return nil, ERROR_INVALID_HANDLE;
 	}
 
+	context.allocator = allocator;
+
 	h := win32.HANDLE(fd);
 
 	dir_fi, _ := stat_from_file_information("", h);

+ 2 - 0
core/path/filepath/path.odin

@@ -1,3 +1,5 @@
+// The path/filepath package uses either forward slashes or backslashes depending on the operating system
+// To process paths usch as URLs that depend on forward slashes regardless of the OS, use the path package
 package filepath
 
 import "core:os"

+ 12 - 147
core/path/path.odin

@@ -1,3 +1,8 @@
+// The path package is only to be used for paths separated by forward slashes,
+// e.g. paths in URLs
+//
+// This package does not deal with Windows/NT paths with volume letters or backslashes
+// To manipulate operating system specific paths, use the path/filepath package
 package path
 
 import "core:strings"
@@ -5,29 +10,14 @@ import "core:runtime"
 import "core:unicode/utf8"
 
 // is_separator checks whether the byte is a valid separator character
-is_separator :: proc(c: byte) -> bool {
-	switch c {
-	case '/':  return true;
-	case '\\': return ODIN_OS == "windows";
-	}
-	return false;
+is_separator :: inline proc(c: byte) -> bool {
+	return c == '/';
 }
 
 
 // is_abs checks whether the path is absolute
-is_abs :: proc(path: string) -> bool {
-	if len(path) > 0 && path[0] == '/' {
-		return true;
-	}
-	when ODIN_OS == "windows" {
-		if len(path) > 2 {
-			switch path[0] {
-			case 'A'..'Z', 'a'..'z':
-				return path[1] == ':' && is_separator(path[2]);
-			}
-		}
-	}
-	return false;
+is_abs :: inline proc(path: string) -> bool {
+	return len(path) > 0 && path[0] == '/';
 }
 
 
@@ -51,7 +41,7 @@ base :: proc(path: string, new := false, allocator := context.allocator) -> (las
 	for len(path) > 0 && is_separator(path[len(path)-1]) {
 		path = path[:len(path)-1];
 	}
-	if i := strings.last_index_any(path, OS_SEPARATORS); i >= 0 {
+	if i := strings.last_index(path, "/"); i >= 0 {
 		path = path[i+1:];
 	}
 
@@ -79,13 +69,13 @@ dir :: proc(path: string, allocator := context.allocator) -> string {
 // If there is no slash in path, it returns an empty dir and file set to path
 // The returned values have the property that path = dir+file
 split :: proc(path: string) -> (dir, file: string) {
-	i := strings.last_index_any(path, OS_SEPARATORS);
+	i := strings.last_index(path, "/");
 	return path[:i+1], path[i+1:];
 }
 
 // split_elements splits the path elements into slices of the original path string
 split_elements :: proc(path: string, allocator := context.allocator) -> []string {
-	return strings.split_multi(path, OS_SEPARATORS_ARRAY, true, allocator);
+	return strings.split(path, "/", allocator);
 }
 
 // clean returns the shortest path name equivalent to path through lexical analysis only
@@ -206,131 +196,6 @@ name :: proc(path: string, new := false, allocator := context.allocator) -> (nam
 
 
 
-rel :: proc{rel_between, rel_current};
-
-// returns the relative path from one path to another
-rel_between :: proc(from, to: string, allocator := context.allocator) -> string {
-	if from == "" || to == "" {
-		return "";
-	}
-
-	from, to := from, to;
-	from = full(from, context.temp_allocator);
-	to   = full(to,   context.temp_allocator);
-
-	from_is_dir := is_dir(from);
-	to_is_dir   := is_dir(to);
-
-	index, slash := 0, 0;
-
-	for {
-		if index >= len(from) {
-			if index >= len(to) || (from_is_dir && index < len(to) && (to[index] == '/' || to[index] == '\\')) {
-				slash = index;
-			}
-
-			break;
-		}
-		else if index >= len(to) {
-			if index >= len(from) || (to_is_dir && index < len(from) && (from[index] == '/' || from[index] == '\\')) {
-				slash = index;
-			}
-
-			break;
-		}
-
-		lchar, skip := utf8.decode_rune_in_string(from[index:]);
-		rchar, _    := utf8.decode_rune_in_string(to[index:]);
-
-		if (lchar == '/' || lchar == '\\') && (rchar == '/' || lchar == '\\') {
-			slash = index;
-		}
-		else if lchar != rchar {
-			break;
-		}
-
-		index += skip;
-	}
-
-	if slash < 1 {
-		// there is no common path, use the absolute `to` path
-		return strings.clone(to, allocator);
-	}
-
-	from_slashes, to_slashes := 0, 0;
-
-	if slash < len(from) {
-		from = from[slash+1:];
-
-		if from_is_dir {
-			from_slashes += 1;
-		}
-	}
-	else {
-		from = "";
-	}
-
-	if slash < len(to) {
-		to = to[slash+1:];
-
-		if to_is_dir {
-			to_slashes += 1;
-		}
-	}
-	else {
-		to = "";
-	}
-
-	for char in from {
-		if char == '/' || char == '\\' {
-			from_slashes += 1;
-		}
-	}
-
-	for char in to {
-		if char == '/' || char == '\\' {
-			to_slashes += 1;
-		}
-	}
-
-	if from_slashes == 0 {
-		buffer := make([]byte, 2 + len(to), allocator);
-
-		buffer[0] = '.';
-		buffer[1] = '/';
-		copy(buffer[2:], to);
-
-		return string(buffer);
-	}
-	else {
-		buffer := make([]byte, from_slashes*3 + len(to), allocator);
-
-		for i in 0..<from_slashes {
-			buffer[i*3+0] = '.';
-			buffer[i*3+1] = '.';
-			buffer[i*3+2] = '/';
-		}
-
-		copy(buffer[from_slashes*3:], to);
-
-		return string(buffer);
-	}
-
-	return "";
-}
-
-// returns the relative path from the current directory to another path
-rel_current :: proc(to: string, allocator := context.allocator) -> string {
-	return rel_between(current(context.allocator), to, allocator);
-}
-
-
-
-
-
-
-
-
 /*
 	Lazy_Buffer is a lazily made path buffer
 	When it does allocate, it uses the context.allocator

+ 295 - 4
core/reflect/reflect.odin

@@ -2,6 +2,8 @@ package reflect
 
 import "core:runtime"
 import "core:mem"
+import "intrinsics"
+_ :: intrinsics;
 
 Type_Info :: runtime.Type_Info;
 
@@ -551,11 +553,22 @@ union_variant_type_info :: proc(a: any) -> ^runtime.Type_Info {
 	return type_info_of(id);
 }
 
+type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool {
+	return info.maybe && len(info.variants) == 1 && is_pointer(info.variants[0]);
+}
+
 union_variant_typeid :: proc(a: any) -> typeid {
 	if a == nil { return nil; }
 
 	ti := runtime.type_info_base(type_info_of(a.id));
 	if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
+		if type_info_union_is_pure_maybe(info) {
+			if a.data != nil {
+				return info.variants[0].id;
+			}
+			return nil;
+		}
+
 		tag_ptr := uintptr(a.data) + info.tag_offset;
 		tag_any := any{rawptr(tag_ptr), info.tag_type.id};
 
@@ -573,15 +586,159 @@ union_variant_typeid :: proc(a: any) -> typeid {
 		}
 
 		if a.data != nil && tag != 0 {
-			return info.variants[tag-1].id;
+			i := tag if info.no_nil else tag-1;
+			return info.variants[i].id;
 		}
-	} else {
-		panic("expected a union to reflect.union_variant_typeid");
+
+		return nil;
 	}
+	panic("expected a union to reflect.union_variant_typeid");
 
-	return nil;
 }
 
+get_union_variant_raw_tag :: proc(a: any) -> i64 {
+	if a == nil { return -1; }
+
+	ti := runtime.type_info_base(type_info_of(a.id));
+	if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
+		if type_info_union_is_pure_maybe(info) {
+			return 1 if a.data != nil else 0;
+		}
+
+		tag_ptr := uintptr(a.data) + info.tag_offset;
+		tag_any := any{rawptr(tag_ptr), info.tag_type.id};
+
+		tag: i64 = ---;
+		switch i in tag_any {
+		case u8:   tag = i64(i);
+		case i8:   tag = i64(i);
+		case u16:  tag = i64(i);
+		case i16:  tag = i64(i);
+		case u32:  tag = i64(i);
+		case i32:  tag = i64(i);
+		case u64:  tag = i64(i);
+		case i64:  tag = i64(i);
+		case: unimplemented();
+		}
+
+		return tag;
+	}
+	panic("expected a union to reflect.get_union_variant_raw_tag");
+}
+
+
+set_union_variant_raw_tag :: proc(a: any, tag: i64) {
+	if a == nil { return; }
+
+	ti := runtime.type_info_base(type_info_of(a.id));
+	if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
+		if type_info_union_is_pure_maybe(info) {
+			// Cannot do anything
+			return;
+		}
+
+		tag_ptr := uintptr(a.data) + info.tag_offset;
+		tag_any := any{rawptr(tag_ptr), info.tag_type.id};
+
+		switch i in &tag_any {
+		case u8:   i = u8(tag);
+		case i8:   i = i8(tag);
+		case u16:  i = u16(tag);
+		case i16:  i = i16(tag);
+		case u32:  i = u32(tag);
+		case i32:  i = i32(tag);
+		case u64:  i = u64(tag);
+		case i64:  i = i64(tag);
+		case: unimplemented();
+		}
+
+		return;
+	}
+	panic("expected a union to reflect.set_union_variant_raw_tag");
+}
+
+set_union_variant_typeid :: proc(a: any, id: typeid) {
+	if a == nil { return; }
+
+	ti := runtime.type_info_base(type_info_of(a.id));
+	if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
+		if type_info_union_is_pure_maybe(info) {
+			// Cannot do anything
+			return;
+		}
+
+		if id == nil && !info.no_nil {
+			set_union_variant_raw_tag(a, 0);
+			return;
+		}
+
+		for variant, i in info.variants {
+			if variant.id == id {
+				tag := i64(i);
+				if !info.no_nil {
+					tag += 1;
+				}
+				set_union_variant_raw_tag(a, tag);
+				return;
+			}
+		}
+		return;
+	}
+	panic("expected a union to reflect.set_union_variant_typeid");
+}
+
+set_union_variant_type_info :: proc(a: any, tag_ti: ^Type_Info) {
+	if a == nil { return; }
+
+	ti := runtime.type_info_base(type_info_of(a.id));
+	if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
+		if type_info_union_is_pure_maybe(info) {
+			// Cannot do anything
+			return;
+		}
+
+		if tag_ti == nil && !info.no_nil {
+			set_union_variant_raw_tag(a, 0);
+			return;
+		}
+
+		for variant, i in info.variants {
+			if variant == tag_ti {
+				tag := i64(i);
+				if !info.no_nil {
+					tag += 1;
+				}
+				set_union_variant_raw_tag(a, tag);
+				return;
+			}
+		}
+		return;
+	}
+	panic("expected a union to reflect.set_union_variant_type_info");
+}
+
+
+as_bool :: proc(a: any) -> (value: bool, valid: bool) {
+	if a == nil { return; }
+	a := a;
+	ti := runtime.type_info_core(type_info_of(a.id));
+	a.id = ti.id;
+
+	#partial switch info in ti.variant {
+	case Type_Info_Boolean:
+		valid = true;
+		switch v in a {
+		case bool: value = bool(v);
+		case b8:   value = bool(v);
+		case b16:  value = bool(v);
+		case b32:  value = bool(v);
+		case b64:  value = bool(v);
+		case: valid = false;
+		}
+	}
+
+	return;
+}
 
 as_int :: proc(a: any) -> (value: int, valid: bool) {
 	v: i64;
@@ -915,3 +1072,137 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
 
 	return;
 }
+
+
+as_string :: proc(a: any) -> (value: string, valid: bool) {
+	if a == nil { return; }
+	a := a;
+	ti := runtime.type_info_core(type_info_of(a.id));
+	a.id = ti.id;
+
+	#partial switch info in ti.variant {
+	case Type_Info_String:
+		valid = true;
+		switch v in a {
+		case string:  value = string(v);
+		case cstring: value = string(v);
+		case: valid = false;
+		}
+	}
+
+	return;
+}
+
+relative_pointer_to_absolute :: proc(a: any) -> rawptr {
+	if a == nil { return nil; }
+	a := a;
+	ti := runtime.type_info_core(type_info_of(a.id));
+	a.id = ti.id;
+
+	#partial switch info in ti.variant {
+	case Type_Info_Relative_Pointer:
+		return relative_pointer_to_absolute_raw(a.data, info.base_integer.id);
+	}
+	return nil;
+}
+
+
+relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
+	_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
+		if ptr^ == 0 {
+			return nil;
+		}
+		when intrinsics.type_is_unsigned(T) {
+			return rawptr(uintptr(ptr) + uintptr(ptr^));
+		} else {
+			return rawptr(uintptr(ptr) + uintptr(i64(ptr^)));
+		}
+	}
+
+	ptr_any := any{data, base_integer_id};
+	ptr: rawptr;
+	switch i in &ptr_any {
+	case u8:    ptr = _handle(&i);
+	case u16:   ptr = _handle(&i);
+	case u32:   ptr = _handle(&i);
+	case u64:   ptr = _handle(&i);
+	case i8:    ptr = _handle(&i);
+	case i16:   ptr = _handle(&i);
+	case i32:   ptr = _handle(&i);
+	case i64:   ptr = _handle(&i);
+	case u16le: ptr = _handle(&i);
+	case u32le: ptr = _handle(&i);
+	case u64le: ptr = _handle(&i);
+	case i16le: ptr = _handle(&i);
+	case i32le: ptr = _handle(&i);
+	case i64le: ptr = _handle(&i);
+	case u16be: ptr = _handle(&i);
+	case u32be: ptr = _handle(&i);
+	case u64be: ptr = _handle(&i);
+	case i16be: ptr = _handle(&i);
+	case i32be: ptr = _handle(&i);
+	case i64be: ptr = _handle(&i);
+	}
+	return ptr;
+
+}
+
+
+
+as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
+	if a == nil { return; }
+	a := a;
+	ti := runtime.type_info_core(type_info_of(a.id));
+	a.id = ti.id;
+
+	#partial switch info in ti.variant {
+	case Type_Info_Pointer:
+		valid = true;
+		value = a.data;
+
+	case Type_Info_String:
+		valid = true;
+		switch v in a {
+		case cstring: value = rawptr(v);
+		case: valid = false;
+		}
+
+	case Type_Info_Relative_Pointer:
+		valid = true;
+		value = relative_pointer_to_absolute_raw(a.data, info.base_integer.id);
+	}
+
+	return;
+}
+
+
+as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
+	if a == nil { return; }
+	a := a;
+	ti := runtime.type_info_core(type_info_of(a.id));
+	a.id = ti.id;
+
+	#partial switch info in ti.variant {
+	case Type_Info_String:
+		valid = true;
+		switch v in a {
+		case string:  value = raw_data(v);
+		case cstring: value = rawptr(v); // just in case
+		case: valid = false;
+		}
+
+	case Type_Info_Array:
+		valid = true;
+		value = a.data;
+
+	case Type_Info_Slice:
+		valid = true;
+		value = (^mem.Raw_Slice)(a.data).data;
+
+	case Type_Info_Dynamic_Array:
+		valid = true;
+		value = (^mem.Raw_Dynamic_Array)(a.data).data;
+	}
+
+	return;
+}

+ 1 - 1
core/runtime/error_checks.odin

@@ -35,7 +35,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index
 	handle_error(file, line, column, index, count);
 }
 
-slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) {
+slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) -> ! {
 	context = default_context();
 	fd := os_stderr();
 	print_caller_location(fd, Source_Code_Location{file, line, column, "", 0});

+ 155 - 0
core/unicode/utf8/utf8string/string.odin

@@ -0,0 +1,155 @@
+package utf8string
+
+import "core:unicode/utf8"
+import "core:runtime"
+import "builtin"
+
+String :: struct {
+	contents:   string,
+	rune_count: int,
+
+	// cached information
+	non_ascii:  int, // index to non-ascii code points
+	width:      int, // 0 if ascii
+	byte_pos:   int,
+	rune_pos:   int,
+}
+
+@(private)
+_len :: builtin.len; // helper procedure
+
+init :: proc(s: ^String, contents: string) -> ^String {
+	s.contents = contents;
+	s.byte_pos = 0;
+	s.rune_pos = 0;
+
+	for i in 0..<_len(contents) {
+		if contents[i] >= utf8.RUNE_SELF {
+			s.rune_count = utf8.rune_count_in_string(contents);
+			_, s.width = utf8.decode_rune_in_string(contents);
+			s.non_ascii = i;
+			return s;
+		}
+	}
+
+	s.rune_count = _len(contents);
+	s.width = 0;
+	s.non_ascii = _len(contents);
+	return s;
+}
+
+to_string :: proc(s: ^String) -> string {
+	return s.contents;
+}
+
+len :: proc(s: ^String) -> int {
+	return s.rune_count;
+}
+
+
+is_ascii :: proc(s: ^String) -> bool {
+	return s.width == 0;
+}
+
+at :: proc(s: ^String, i: int, loc := #caller_location) -> (r: rune) {
+	runtime.bounds_check_error_loc(loc, i, s.rune_count);
+
+	if i < s.non_ascii {
+		return rune(s.contents[i]);
+	}
+
+	switch i {
+	case 0:
+		r, s.width = utf8.decode_rune_in_string(s.contents);
+		s.rune_pos = 0;
+		s.byte_pos = 0;
+		return;
+
+	case s.rune_count-1:
+		r, s.width = utf8.decode_rune_in_string(s.contents);
+		s.rune_pos = i;
+		s.byte_pos = _len(s.contents) - s.width;
+		return;
+
+	case s.rune_pos-1:
+		r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos]);
+		s.rune_pos = i;
+		s.byte_pos -= s.width;
+		return;
+
+	case s.rune_pos+1:
+		s.rune_pos = i;
+		s.byte_pos += s.width;
+		fallthrough;
+	case s.rune_pos:
+		r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
+		return;
+	}
+
+	// Linear scan
+	scan_forward := true;
+	if i < s.rune_pos {
+		if i < (s.rune_pos-s.non_ascii)/2 {
+			s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii;
+		} else {
+			scan_forward = false;
+		}
+	} else if i-s.rune_pos < (s.rune_count-s.rune_pos)/2 {
+		// scan_forward = true;
+	} else {
+		s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count;
+		scan_forward = false;
+	}
+
+	if scan_forward {
+		for {
+			r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
+			if s.rune_pos == i {
+				return;
+			}
+			s.rune_pos += 1;
+			s.byte_pos += s.width;
+
+		}
+	} else {
+		for {
+			r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos]);
+			s.rune_pos -= 1;
+			s.byte_pos -= s.width;
+			if s.rune_pos == i {
+				return;
+			}
+		}
+	}
+}
+
+slice :: proc(s: ^String, i, j: int, loc := #caller_location) -> string {
+	runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count);
+
+	if j < s.non_ascii {
+		return s.contents[i:j];
+	}
+
+	if i == j {
+		return "";
+	}
+
+	lo, hi: int;
+	if i < s.non_ascii {
+		lo = i;
+	} else if i == s.rune_count {
+		lo = _len(s.contents);
+	} else {
+		at(s, i, loc);
+		lo = s.byte_pos;
+	}
+
+	if j == s.rune_count {
+		hi = _len(s.contents);
+	} else {
+		at(s, j, loc);
+		hi = s.byte_pos;
+	}
+
+	return s.contents[lo:hi];
+}

+ 2 - 0
src/check_type.cpp

@@ -110,6 +110,8 @@ bool does_field_type_allow_using(Type *t) {
 		return true;
 	} else if (is_type_array(t)) {
 		return t->Array.count <= 4;
+	} else if (is_type_typeid(t)) {
+		return true;
 	}
 	return false;
 }