فهرست منبع

Make the `string` type elements "immutable", akin to `char const *` in C

Allows for extra security and optimization benefits
gingerBill 5 سال پیش
والد
کامیت
9db81498d8

+ 3 - 3
core/encoding/cel/cel.odin

@@ -91,7 +91,7 @@ print :: proc(p: ^Parser, pretty := false) {
 }
 
 create_from_string :: proc(src: string) -> (^Parser, bool) {
-	return init(cast([]byte)src);
+	return init(transmute([]byte)src);
 }
 
 
@@ -726,8 +726,8 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
 		case Kind.Add:
 			n := len(a) + len(b);
 			data := make([]byte, n);
-			copy(data[:], cast([]byte)a);
-			copy(data[len(a):], cast([]byte)b);
+			copy(data[:], a);
+			copy(data[len(a):], b);
 			s := string(data);
 			append(&p.allocated_strings, s);
 			return s, true;

+ 7 - 7
core/encoding/json/marshal.odin

@@ -95,17 +95,17 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
 		buf: [386]byte;
 
 		str := strconv.append_float(buf[1:], val, 'f', 2*ti.size, 8*ti.size);
-		str = string(buf[:len(str)+1]);
-		if str[1] == '+' || str[1] == '-' {
-			str = str[1:];
+		s := buf[:len(str)+1];
+		if s[1] == '+' || s[1] == '-' {
+			s = s[1:];
 		} else {
-			str[0] = '+';
+			s[0] = '+';
 		}
-		if str[0] == '+' {
-			str = str[1:];
+		if s[0] == '+' {
+			s = s[1:];
 		}
 
-		write_string(b, str);
+		write_string(b, string(s));
 
 	case Type_Info_Complex:
 		return Marshal_Error.Unsupported_Type;

+ 2 - 2
core/encoding/json/parser.odin

@@ -174,7 +174,7 @@ parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
 clone_string :: proc(s: string, allocator: mem.Allocator) -> string {
 	n := len(s);
 	b := make([]byte, n+1, allocator);
-	copy(b, cast([]byte)s);
+	copy(b, s);
 	b[n] = 0;
 	return string(b[:n]);
 }
@@ -349,7 +349,7 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
 	}
 
 	b := make([]byte, len(s) + 2*utf8.UTF_MAX, allocator);
-	w := copy(b, cast([]byte)s[0:i]);
+	w := copy(b, s[0:i]);
 	loop: for i < len(s) {
 		c := s[i];
 		switch {

+ 30 - 30
core/fmt/fmt.odin

@@ -654,32 +654,32 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
 		buf: [386]byte;
 
 		str := strconv.append_float(buf[1:], v, 'f', prec, bit_size);
-		str = string(buf[:len(str)+1]);
-		if str[1] == '+' || str[1] == '-' {
-			str = str[1:];
+		b := buf[:len(str)+1];
+		if b[1] == '+' || b[1] == '-' {
+			b = b[1:];
 		} else {
-			str[0] = '+';
+			b[0] = '+';
 		}
 
-		if fi.space && !fi.plus && str[0] == '+' {
-			str[0] = ' ';
+		if fi.space && !fi.plus && b[0] == '+' {
+			b[0] = ' ';
 		}
 
-		if len(str) > 1 && (str[1] == 'N' || str[1] == 'I') {
-			strings.write_string(fi.buf, str);
+		if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
+			strings.write_string(fi.buf, string(b));
 			return;
 		}
 
-		if fi.plus || str[0] != '+' {
-			if fi.zero && fi.width_set && fi.width > len(str) {
-				strings.write_byte(fi.buf, str[0]);
-				fmt_write_padding(fi, fi.width - len(str));
-				strings.write_string(fi.buf, str[1:]);
+		if fi.plus || b[0] != '+' {
+			if fi.zero && fi.width_set && fi.width > len(b) {
+				strings.write_byte(fi.buf, b[0]);
+				fmt_write_padding(fi, fi.width - len(b));
+				strings.write_string(fi.buf, string(b[1:]));
 			} else {
-				_pad(fi, str);
+				_pad(fi, string(b));
 			}
 		} else {
-			_pad(fi, str[1:]);
+			_pad(fi, string(b[1:]));
 		}
 
 	case 'e', 'E':
@@ -688,32 +688,32 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
 		buf: [386]byte;
 
 		str := strconv.append_float(buf[1:], v, 'e', prec, bit_size);
-		str = string(buf[:len(str)+1]);
-		if str[1] == '+' || str[1] == '-' {
-			str = str[1:];
+		b := buf[:len(str)+1];
+		if b[1] == '+' || b[1] == '-' {
+			b = b[1:];
 		} else {
-			str[0] = '+';
+			b[0] = '+';
 		}
 
-		if fi.space && !fi.plus && str[0] == '+' {
-			str[0] = ' ';
+		if fi.space && !fi.plus && b[0] == '+' {
+			b[0] = ' ';
 		}
 
-		if len(str) > 1 && (str[1] == 'N' || str[1] == 'I') {
-			strings.write_string(fi.buf, str);
+		if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
+			strings.write_string(fi.buf, string(b));
 			return;
 		}
 
 		if fi.plus || str[0] != '+' {
-			if fi.zero && fi.width_set && fi.width > len(str) {
-				strings.write_byte(fi.buf, str[0]);
-				fmt_write_padding(fi, fi.width - len(str));
-				strings.write_string(fi.buf, str[1:]);
+			if fi.zero && fi.width_set && fi.width > len(b) {
+				strings.write_byte(fi.buf, b[0]);
+				fmt_write_padding(fi, fi.width - len(b));
+				strings.write_string(fi.buf, string(b[1:]));
 			} else {
-				_pad(fi, str);
+				_pad(fi, string(b));
 			}
 		} else {
-			_pad(fi, str[1:]);
+			_pad(fi, string(b[1:]));
 		}
 
 	case 'h', 'H':
@@ -1353,7 +1353,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 			actual_field_count := len(info.names);
 
 			n := uintptr(info.soa_len);
-			
+
 			if info.soa_kind == .Slice {
 				actual_field_count = len(info.names)-1; // len
 

+ 1 - 1
core/os/os.odin

@@ -5,7 +5,7 @@ import "core:strconv"
 import "core:unicode/utf8"
 
 write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
-	return write(fd, cast([]byte)str);
+	return write(fd, transmute([]byte)str);
 }
 
 write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) {

+ 21 - 4
core/runtime/core.odin

@@ -418,11 +418,24 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code
 
 
 @builtin
-copy :: proc "contextless" (dst, src: $T/[]$E) -> int {
+copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
 	n := max(0, min(len(dst), len(src)));
 	if n > 0 do mem_copy(&dst[0], &src[0], n*size_of(E));
 	return n;
 }
+@builtin
+copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
+	n := max(0, min(len(dst), len(src)));
+	if n > 0 {
+		d := &dst[0];
+		s := (transmute(Raw_String)src).data;
+		mem_copy(d, s, n);
+	}
+	return n;
+}
+@builtin
+copy :: proc{copy_slice, copy_from_string};
+
 
 
 
@@ -559,6 +572,10 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
 		a.len += arg_len;
 	}
 }
+@builtin
+append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) {
+	append_elem(array, transmute([]E)arg, loc);
+}
 
 @builtin
 reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
@@ -754,7 +771,7 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
 	}
 }
 
-@builtin append :: proc{append_elem, append_elems};
+@builtin append :: proc{append_elem, append_elems, append_elem_string};
 @builtin append_soa :: proc{append_soa_elem, append_soa_elems};
 
 
@@ -1091,11 +1108,11 @@ _fnv64a :: proc(data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 {
 default_hash :: proc(data: []byte) -> u64 {
 	return _fnv64a(data);
 }
-default_hash_string :: proc(s: string) -> u64 do return default_hash(([]byte)(s));
+default_hash_string :: proc(s: string) -> u64 do return default_hash(transmute([]byte)(s));
 
 
 source_code_location_hash :: proc(s: Source_Code_Location) -> u64 {
-	hash := _fnv64a(cast([]byte)s.file_path);
+	hash := _fnv64a(transmute([]byte)s.file_path);
 	hash = hash ~ (u64(s.line) * 0x100000001b3);
 	hash = hash ~ (u64(s.column) * 0x100000001b3);
 	return hash;

+ 14 - 8
core/runtime/internal.odin

@@ -369,17 +369,27 @@ memory_compare_zero :: proc "contextless" (a: rawptr, n: int) -> int #no_bounds_
 	return 0;
 }
 
+@private
+Raw_String :: struct {
+	data: ^byte,
+	len: int,
+};
+
 string_eq :: proc "contextless" (a, b: string) -> bool {
+	x := transmute(Raw_String)a;
+	y := transmute(Raw_String)b;
 	switch {
-	case len(a) != len(b): return false;
-	case len(a) == 0:      return true;
-	case &a[0] == &b[0]:   return true;
+	case x.len != y.len: return false;
+	case x.len == 0:      return true;
+	case x.data == y.data:   return true;
 	}
 	return string_cmp(a, b) == 0;
 }
 
 string_cmp :: proc "contextless" (a, b: string) -> int {
-	return memory_compare(&a[0], &b[0], min(len(a), len(b)));
+	x := transmute(Raw_String)a;
+	y := transmute(Raw_String)b;
+	return memory_compare(x.data, y.data, min(x.len, y.len));
 }
 
 string_ne :: inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b); }
@@ -398,10 +408,6 @@ cstring_len :: proc "contextless" (s: cstring) -> int {
 }
 
 cstring_to_string :: proc "contextless" (s: cstring) -> string {
-	Raw_String :: struct {
-		data: ^byte,
-		len: int,
-	};
 	if s == nil do return "";
 	ptr := (^byte)(s);
 	n := cstring_len(s);

+ 3 - 1
core/sort/sort.odin

@@ -282,5 +282,7 @@ compare_f64s :: proc(a, b: f64) -> int {
 	return 0;
 }
 compare_strings :: proc(a, b: string) -> int {
-	return mem.compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
+	x := transmute(mem.Raw_String)a;
+	y := transmute(mem.Raw_String)b;
+	return mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len));
 }

+ 1 - 1
core/strconv/generic_float.odin

@@ -56,7 +56,7 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int)
 		} else {
 			s = "+Inf";
 		}
-		n := copy(buf, cast([]byte)s);
+		n := copy(buf, transmute([]byte)s);
 		return buf[:n];
 
 	case 0: // denormalized

+ 3 - 3
core/strconv/strconv.odin

@@ -187,8 +187,8 @@ parse_f64 :: proc(s: string) -> f64 {
 
 append_bool :: proc(buf: []byte, b: bool) -> string {
 	n := 0;
-	if b do n = copy(buf, cast([]byte)"true");
-	else do n = copy(buf, cast([]byte)"false");
+	if b do n = copy(buf, "true");
+	else do n = copy(buf, "false");
 	return string(buf[:n]);
 }
 
@@ -260,7 +260,7 @@ quote_rune :: proc(buf: []byte, r: rune) -> string {
 	}
 	write_string :: inline proc(buf: []byte, i: ^int, s: string) {
 		if i^ < len(buf) {
-			n := copy(buf[i^:], cast([]byte)s);
+			n := copy(buf[i^:], s);
 			i^ += n;
 		}
 	}

+ 1 - 1
core/strings/builder.odin

@@ -62,7 +62,7 @@ write_rune :: proc(b: ^Builder, r: rune) -> int {
 }
 
 write_string :: proc(b: ^Builder, s: string) {
-	write_bytes(b, cast([]byte)s);
+	write_bytes(b, transmute([]byte)s);
 }
 
 write_bytes :: proc(b: ^Builder, x: []byte) {

+ 14 - 14
core/strings/strings.odin

@@ -5,14 +5,14 @@ import "core:unicode/utf8"
 
 clone :: proc(s: string, allocator := context.allocator) -> string {
 	c := make([]byte, len(s)+1, allocator);
-	copy(c, cast([]byte)s);
+	copy(c, s);
 	c[len(s)] = 0;
 	return string(c[:len(s)]);
 }
 
 clone_to_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
 	c := make([]byte, len(s)+1, allocator);
-	copy(c, cast([]byte)s);
+	copy(c, s);
 	c[len(s)] = 0;
 	return cstring(&c[0]);
 }
@@ -20,7 +20,7 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
 @(deprecated="Please use 'strings.clone'")
 new_string :: proc(s: string, allocator := context.allocator) -> string {
 	c := make([]byte, len(s)+1, allocator);
-	copy(c, cast([]byte)s);
+	copy(c, s);
 	c[len(s)] = 0;
 	return string(c[:len(s)]);
 }
@@ -28,7 +28,7 @@ new_string :: proc(s: string, allocator := context.allocator) -> string {
 @(deprecated="Please use 'strings.clone_to_cstring'")
 new_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
 	c := make([]byte, len(s)+1, allocator);
-	copy(c, cast([]byte)s);
+	copy(c, s);
 	c[len(s)] = 0;
 	return cstring(&c[0]);
 }
@@ -43,7 +43,7 @@ string_from_ptr :: proc(ptr: ^byte, len: int) -> string {
 }
 
 compare :: proc(lhs, rhs: string) -> int {
-	return mem.compare(cast([]byte)lhs, cast([]byte)rhs);
+	return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs);
 }
 
 contains_rune :: proc(s: string, r: rune) -> int {
@@ -130,10 +130,10 @@ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string
 	}
 
 	b := make([]byte, n, allocator);
-	i := copy(b, cast([]byte)a[0]);
+	i := copy(b, a[0]);
 	for s in a[1:] {
-		i += copy(b[i:], cast([]byte)sep);
-		i += copy(b[i:], cast([]byte)s);
+		i += copy(b[i:], sep);
+		i += copy(b[i:], s);
 	}
 	return string(b);
 }
@@ -150,7 +150,7 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string {
 	b := make([]byte, n, allocator);
 	i := 0;
 	for s in a {
-		i += copy(b[i:], cast([]byte)s);
+		i += copy(b[i:], s);
 	}
 	return string(b);
 }
@@ -416,7 +416,7 @@ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string
 	}
 
 	b := make([]byte, len(s)*count, allocator);
-	i := copy(b, cast([]byte)s);
+	i := copy(b, s);
 	for i < len(b) { // 2^N trick to reduce the need to copy
 		copy(b[i:], b[:i]);
 		i *= 2;
@@ -460,11 +460,11 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
 		} else {
 			j += index(s[start:], old);
 		}
-		w += copy(t[w:], cast([]byte)s[start:j]);
-		w += copy(t[w:], cast([]byte)new);
+		w += copy(t[w:], s[start:j]);
+		w += copy(t[w:], new);
 		start = j + len(old);
 	}
-	w += copy(t[w:], cast([]byte)s[start:]);
+	w += copy(t[w:], s[start:]);
 	output = string(t[0:w]);
 	return;
 }
@@ -705,7 +705,7 @@ reverse :: proc(s: string, allocator := context.allocator) -> string {
 	for len(str) > 0 {
 		_, w := utf8.decode_rune_in_string(str);
 		i -= w;
-		copy(buf[i:], cast([]byte)str[:w]);
+		copy(buf[i:], str[:w]);
 		str = str[w:];
 	}
 	return string(buf);

+ 4 - 2
core/sys/win32/general.odin

@@ -752,14 +752,16 @@ utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
 		return nil;
 	}
 
-	n := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstring(&s[0]), i32(len(s)), nil, 0);
+	b := transmute([]byte)s;
+	cstr := cstring(&b[0]);
+	n := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstr, i32(len(s)), nil, 0);
 	if n == 0 {
 		return nil;
 	}
 
 	text := make([]u16, n+1, allocator);
 
-	n1 := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstring(&s[0]), i32(len(s)), Wstring(&text[0]), i32(n));
+	n1 := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstr, i32(len(s)), Wstring(&text[0]), i32(n));
 	if n1 == 0 {
 		delete(text, allocator);
 		return nil;

+ 3 - 3
core/unicode/utf8/utf8.odin

@@ -90,7 +90,7 @@ encode_rune :: proc(c: rune) -> ([4]u8, int) {
 	return buf, 4;
 }
 
-decode_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_rune(cast([]u8)s);
+decode_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_rune(transmute([]u8)s);
 decode_rune :: proc(s: []u8) -> (rune, int) {
 	n := len(s);
 	if n < 1 {
@@ -130,7 +130,7 @@ decode_rune :: proc(s: []u8) -> (rune, int) {
 
 
 
-decode_last_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_last_rune(cast([]u8)s);
+decode_last_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_last_rune(transmute([]u8)s);
 decode_last_rune :: proc(s: []u8) -> (rune, int) {
 	r: rune;
 	size: int;
@@ -260,7 +260,7 @@ valid_string :: proc(s: string) -> bool {
 
 rune_start :: inline proc(b: u8) -> bool do return b&0xc0 != 0x80;
 
-rune_count_in_string :: inline proc(s: string) -> int do return rune_count(cast([]u8)s);
+rune_count_in_string :: inline proc(s: string) -> int do return rune_count(transmute([]u8)s);
 rune_count :: proc(s: []u8) -> int {
 	count := 0;
 	n := len(s);

+ 2 - 2
src/build_settings.cpp

@@ -493,8 +493,8 @@ String path_to_fullpath(gbAllocator a, String s) {
 
 		// Replace Windows style separators
 		for (isize i = 0; i < result.len; i++) {
-			if (result[i] == '\\') {
-				result[i] = '/';
+			if (result.text[i] == '\\') {
+				result.text[i] = '/';
 			}
 		}
 	}

+ 9 - 9
src/check_expr.cpp

@@ -1559,7 +1559,7 @@ void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type
 	} else if (are_types_identical(src, dst)) {
 		error_line("\tSuggestion: the expression may be directly casted to type %s\n", b);
 	} else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) {
-		error_line("\tSuggestion: a string may be casted to %s\n", a, b);
+		error_line("\tSuggestion: a string may be transmuted to %s\n", b);
 	} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) {
 		error_line("\tSuggestion: the expression may be casted to %s\n", b);
 	}
@@ -1600,7 +1600,7 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
 			error_line("\tSuggestion: for an integer to be casted to a pointer, it must be converted to 'uintptr' first\n");
 		}
 	} else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) {
-		error_line("\tSuggestion: a string may be casted to %s\n", a, b);
+		error_line("\tSuggestion: a string may be transmuted to %s\n", b);
 	} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) {
 		error_line("\tSuggestion: the expression may be casted to %s\n", b);
 	}
@@ -2193,7 +2193,7 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
 	}
 	if ((is_type_string(src) && !is_type_cstring(src)) && is_type_u8_slice(dst)) {
 		// if (is_type_typed(src)) {
-			return true;
+			// return true;
 		// }
 	}
 	// cstring -> string
@@ -2726,7 +2726,7 @@ void update_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
 void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) {
 	gbString expr_str = expr_to_string(operand->expr);
 	gbString type_str = type_to_string(target_type);
-	char *extra_text = "";
+	char const *extra_text = "";
 
 	if (operand->mode == Addressing_Constant) {
 		if (big_int_is_zero(&operand->value.value_integer)) {
@@ -3397,7 +3397,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 	BuiltinProc *bp = &builtin_procs[id];
 	{
-		char *err = nullptr;
+		char const *err = nullptr;
 		if (ce->args.count < bp->arg_count) {
 			err = "Too few";
 		} else if (ce->args.count > bp->arg_count && !bp->variadic) {
@@ -5408,7 +5408,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 		}
 		if (error_code != 0) {
 			err = CallArgumentError_TooManyArguments;
-			char *err_fmt = "Too many arguments for '%s', expected %td arguments";
+			char const *err_fmt = "Too many arguments for '%s', expected %td arguments";
 			if (error_code < 0) {
 				err = CallArgumentError_TooFewArguments;
 				err_fmt = "Too few arguments for '%s', expected %td arguments";
@@ -6181,7 +6181,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
 				if (proc->kind == Entity_Variable) {
 					sep = ":=";
 				}
-				// error_line("\t%.*s %s %s at %.*s(%td:%td) with score %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, cast(long long)valids[i].score);
 				error_line("\t%.*s%.*s%.*s %s %s at %.*s(%td:%td)\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column);
 			}
 			if (procs.count > 0) {
@@ -6751,7 +6750,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t
 void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) {
 	check_expr_base(c, o, e, t);
 	check_not_tuple(c, o);
-	char *err_str = nullptr;
+	char const *err_str = nullptr;
 	switch (o->mode) {
 	case Addressing_NoValue:
 		err_str = "used as a value";
@@ -6779,7 +6778,8 @@ bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 *max_count,
 				*max_count = o->value.value_string.len;
 			}
 			if (o->mode != Addressing_Immutable && o->mode != Addressing_Constant) {
-				o->mode = Addressing_Variable;
+				// o->mode = Addressing_Variable;
+				o->mode = Addressing_Value;
 			}
 			o->type = t_u8;
 			return true;

+ 6 - 1
src/check_type.cpp

@@ -1186,7 +1186,12 @@ bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Typ
 		return false;
 	}
 
-	if (t->kind == Type_Struct) {
+	if (is_type_untyped(t)) {
+		Operand o = {Addressing_Value};
+		o.type = default_type(type);
+		bool can_convert = check_cast_internal(ctx, &o, specialization);
+		return can_convert;
+	} else if (t->kind == Type_Struct) {
 		if (t->Struct.polymorphic_parent == specialization) {
 			return true;
 		}

+ 1 - 1
src/checker.cpp

@@ -619,7 +619,7 @@ AstPackage *get_core_package(CheckerInfo *info, String name) {
 }
 
 
-void add_package_dependency(CheckerContext *c, char *package_name, char *name) {
+void add_package_dependency(CheckerContext *c, char const *package_name, char const *name) {
 	String n = make_string_c(name);
 	AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
 	Entity *e = scope_lookup(p->scope, n);

+ 2 - 2
src/common.cpp

@@ -753,8 +753,8 @@ String path_to_full_path(gbAllocator a, String path) {
 	String res = string_trim_whitespace(make_string_c(fullpath));
 #if defined(GB_SYSTEM_WINDOWS)
 	for (isize i = 0; i < res.len; i++) {
-		if (res[i] == '\\') {
-			res[i] = '/';
+		if (res.text[i] == '\\') {
+			res.text[i] = '/';
 		}
 	}
 #endif

+ 77 - 30
src/ir.cpp

@@ -38,6 +38,8 @@ struct irModule {
 	// NOTE(bill): To prevent strings from being copied a lot
 	// Mainly used for file names
 	Map<irValue *>        const_strings; // Key: String
+	Map<irValue *>        const_string_byte_slices; // Key: String
+	Map<irValue *>        constant_value_to_global; // Key: irValue *
 
 
 	Entity *              entry_point_entity;
@@ -892,6 +894,10 @@ irValue *ir_emit_array_epi(irProcedure *proc, irValue *s, i32 index);
 irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index);
 irValue *ir_emit_bitcast(irProcedure *proc, irValue *data, Type *type);
 irValue *ir_emit_byte_swap(irProcedure *proc, irValue *value, Type *t);
+irValue *ir_find_or_add_entity_string(irModule *m, String str);
+irValue *ir_find_or_add_entity_string_byte_slice(irModule *m, String str);
+
+
 
 irValue *ir_alloc_value(irValueKind kind) {
 	irValue *v = gb_alloc_item(ir_allocator(), irValue);
@@ -1404,8 +1410,6 @@ irValue *ir_de_emit(irProcedure *proc, irValue *instr) {
 	return instr;
 }
 
-
-
 irValue *ir_const_int(i64 i) {
 	return ir_value_constant(t_int, exact_value_i64(i));
 }
@@ -1436,8 +1440,9 @@ irValue *ir_const_f64(f64 f) {
 irValue *ir_const_bool(bool b) {
 	return ir_value_constant(t_bool, exact_value_bool(b != 0));
 }
-irValue *ir_const_string(String s) {
-	return ir_value_constant(t_string, exact_value_string(s));
+irValue *ir_const_string(irModule *m, String s) {
+	return ir_find_or_add_entity_string(m, s);
+	// return ir_value_constant(t_string, exact_value_string(s));
 }
 
 irValue *ir_value_procedure(irModule *m, Entity *entity, Type *type, Ast *type_expr, Ast *body, String name) {
@@ -1479,7 +1484,7 @@ irValue *ir_generate_array(irModule *m, Type *elem_type, i64 count, String prefi
 	return value;
 }
 
-irBlock *ir_new_block(irProcedure *proc, Ast *node, char *label) {
+irBlock *ir_new_block(irProcedure *proc, Ast *node, char const *label) {
 	Scope *scope = nullptr;
 	if (node != nullptr) {
 		scope = scope_of_node(node);
@@ -1557,7 +1562,7 @@ irValue *ir_add_module_constant(irModule *m, Type *type, ExactValue value) {
 	if (is_type_slice(type)) {
 		if (value.kind == ExactValue_String) {
 			GB_ASSERT(is_type_u8_slice(type));
-			return ir_value_constant(type, value);
+			return ir_find_or_add_entity_string_byte_slice(m, value.value_string);
 		} else {
 			ast_node(cl, CompoundLit, value.value_compound);
 
@@ -1591,6 +1596,27 @@ irValue *ir_add_module_constant(irModule *m, Type *type, ExactValue value) {
 }
 
 irValue *ir_add_global_string_array(irModule *m, String string) {
+
+	irValue *global_constant_value = nullptr;
+	{
+		HashKey key = hash_string(string);
+		irValue **found = map_get(&m->const_string_byte_slices, key);
+		if (found != nullptr) {
+			global_constant_value = *found;
+
+			irValue **global_found = map_get(&m->constant_value_to_global, hash_pointer(global_constant_value));
+			if (global_found != nullptr) {
+				return *global_found;
+			}
+		}
+	}
+
+	if (global_constant_value == nullptr) {
+		global_constant_value = ir_find_or_add_entity_string_byte_slice(m, string);
+	}
+	Type *type = ir_type(global_constant_value);
+
+
 	isize max_len = 6+8+1;
 	u8 *str = cast(u8 *)gb_alloc_array(ir_allocator(), u8, max_len);
 	isize len = gb_snprintf(cast(char *)str, max_len, "str$%x", m->global_string_index);
@@ -1599,13 +1625,16 @@ irValue *ir_add_global_string_array(irModule *m, String string) {
 	String name = make_string(str, len-1);
 	Token token = {Token_String};
 	token.string = name;
-	Type *type = alloc_type_array(t_u8, string.len+1);
-	ExactValue ev = exact_value_string(string);
-	Entity *entity = alloc_entity_constant(nullptr, token, type, ev);
-	irValue *g = ir_value_global(entity, ir_add_module_constant(m, type, ev));
+
+	Entity *entity = alloc_entity_constant(nullptr, token, type, exact_value_string(string));
+
+	irValue *g = ir_value_global(entity, global_constant_value);
 	g->Global.is_private      = true;
 	g->Global.is_unnamed_addr = true;
-	// g->Global.is_constant = true;
+	g->Global.is_constant = true;
+
+	map_set(&m->constant_value_to_global, hash_pointer(global_constant_value), g);
+
 
 	ir_module_add_value(m, entity, g);
 	map_set(&m->members, hash_string(name), g);
@@ -4506,7 +4535,7 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal
 			right = ir_emit_conv(proc, right, t_string);
 		}
 
-		char *runtime_proc = nullptr;
+		char const *runtime_proc = nullptr;
 		switch (op_kind) {
 		case Token_CmpEq: runtime_proc = "string_eq"; break;
 		case Token_NotEq: runtime_proc = "string_ne"; break;
@@ -5023,12 +5052,26 @@ irValue *ir_add_local_slice(irProcedure *proc, Type *slice_type, irValue *base,
 
 
 irValue *ir_find_or_add_entity_string(irModule *m, String str) {
-	irValue **found = map_get(&m->const_strings, hash_string(str));
+	HashKey key = hash_string(str);
+	irValue **found = map_get(&m->const_strings, key);
+	if (found != nullptr) {
+		return *found;
+	}
+	irValue *v = ir_value_constant(t_string, exact_value_string(str));
+	map_set(&m->const_strings, key, v);
+	return v;
+
+}
+
+irValue *ir_find_or_add_entity_string_byte_slice(irModule *m, String str) {
+	HashKey key = hash_string(str);
+	irValue **found = map_get(&m->const_string_byte_slices, key);
 	if (found != nullptr) {
 		return *found;
 	}
-	irValue *v = ir_const_string(str);
-	map_set(&m->const_strings, hash_string(str), v);
+	Type *t = alloc_type_array(t_u8, str.len+1);
+	irValue *v = ir_value_constant(t, exact_value_string(str));
+	map_set(&m->const_string_byte_slices, key, v);
 	return v;
 
 }
@@ -10497,15 +10540,17 @@ void ir_init_module(irModule *m, Checker *c) {
 		m->generate_debug_info = build_context.ODIN_OS == "windows" && build_context.word_size == 8;
 	}
 
-	map_init(&m->values,                  heap_allocator());
-	map_init(&m->members,                 heap_allocator());
-	map_init(&m->debug_info,              heap_allocator());
-	map_init(&m->entity_names,            heap_allocator());
-	map_init(&m->anonymous_proc_lits,     heap_allocator());
-	array_init(&m->procs,                 heap_allocator());
-	array_init(&m->procs_to_generate,     heap_allocator());
-	array_init(&m->foreign_library_paths, heap_allocator());
-	map_init(&m->const_strings,           heap_allocator());
+	map_init(&m->values,                   heap_allocator());
+	map_init(&m->members,                  heap_allocator());
+	map_init(&m->debug_info,               heap_allocator());
+	map_init(&m->entity_names,             heap_allocator());
+	map_init(&m->anonymous_proc_lits,      heap_allocator());
+	array_init(&m->procs,                  heap_allocator());
+	array_init(&m->procs_to_generate,      heap_allocator());
+	array_init(&m->foreign_library_paths,  heap_allocator());
+	map_init(&m->const_strings,            heap_allocator());
+	map_init(&m->const_string_byte_slices, heap_allocator());
+	map_init(&m->constant_value_to_global, heap_allocator());
 
 	// Default states
 	m->stmt_state_flags = 0;
@@ -10644,6 +10689,8 @@ void ir_destroy_module(irModule *m) {
 	map_destroy(&m->anonymous_proc_lits);
 	map_destroy(&m->debug_info);
 	map_destroy(&m->const_strings);
+	map_destroy(&m->const_string_byte_slices);
+	map_destroy(&m->constant_value_to_global);
 	array_free(&m->procs);
 	array_free(&m->procs_to_generate);
 	array_free(&m->foreign_library_paths);
@@ -10805,7 +10852,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 			tag = ir_emit_conv(proc, variant_ptr, t_type_info_named_ptr);
 
 			// TODO(bill): Which is better? The mangled name or actual name?
-			irValue *name = ir_const_string(t->Named.type_name->token.string);
+			irValue *name = ir_const_string(proc->module, t->Named.type_name->token.string);
 			irValue *gtip = ir_get_type_info_ptr(proc, t->Named.base);
 
 			ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), name);
@@ -10996,7 +11043,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 				ir_emit_store(proc, type_info, ir_type_info(proc, f->type));
 				if (f->token.string.len > 0) {
 					irValue *name = ir_emit_ptr_offset(proc, memory_names, index);
-					ir_emit_store(proc, name, ir_const_string(f->token.string));
+					ir_emit_store(proc, name, ir_const_string(proc->module, f->token.string));
 				}
 			}
 
@@ -11030,7 +11077,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 						irValue *v = ir_value_constant(t->Enum.base_type, value);
 
 						ir_emit_store_union_variant(proc, value_ep, v, ir_type(v));
-						ir_emit_store(proc, name_ep, ir_const_string(fields[i]->token.string));
+						ir_emit_store(proc, name_ep, ir_const_string(proc->module, fields[i]->token.string));
 					}
 
 					irValue *v_count = ir_const_int(fields.count);
@@ -11144,7 +11191,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 					ir_emit_store(proc, type_info, ir_type_info(proc, f->type));
 					if (f->token.string.len > 0) {
 						irValue *name = ir_emit_ptr_offset(proc, memory_names,   index);
-						ir_emit_store(proc, name, ir_const_string(f->token.string));
+						ir_emit_store(proc, name, ir_const_string(proc->module, f->token.string));
 					}
 					ir_emit_store(proc, offset, ir_const_uintptr(foffset));
 					ir_emit_store(proc, is_using, ir_const_bool((f->flags&EntityFlag_Using) != 0));
@@ -11153,7 +11200,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 						String tag_string = t->Struct.tags[source_index];
 						if (tag_string.len > 0) {
 							irValue *tag_ptr = ir_emit_ptr_offset(proc, memory_tags, index);
-							ir_emit_store(proc, tag_ptr, ir_const_string(tag_string));
+							ir_emit_store(proc, tag_ptr, ir_const_string(proc->module, tag_string));
 						}
 					}
 
@@ -11204,7 +11251,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 					irValue *bit_ep    = ir_emit_array_epi(proc, bit_array,    cast(i32)i);
 					irValue *offset_ep = ir_emit_array_epi(proc, offset_array, cast(i32)i);
 
-					ir_emit_store(proc, name_ep, ir_const_string(f->token.string));
+					ir_emit_store(proc, name_ep, ir_const_string(proc->module, f->token.string));
 					ir_emit_store(proc, bit_ep, ir_const_i32(f->type->BitFieldValue.bits));
 					ir_emit_store(proc, offset_ep, ir_const_i32(t->BitField.offsets[i]));
 

+ 1 - 1
src/ir_print.cpp

@@ -44,7 +44,7 @@ void ir_file_buffer_write(irFileBuffer *f, void const *data, isize len) {
 }
 
 
-void ir_fprintf(irFileBuffer *f, char *fmt, ...) {
+void ir_fprintf(irFileBuffer *f, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	isize len = gb_snprintf_va(f->buf, IR_FILE_BUFFER_BUF_LEN-1, fmt, va);

+ 8 - 8
src/main.cpp

@@ -29,7 +29,7 @@ gb_global Timings global_timings = {0};
 
 
 // NOTE(bill): 'name' is used in debugging and profiling modes
-i32 system_exec_command_line_app(char *name, char *fmt, ...) {
+i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
 #if defined(GB_SYSTEM_WINDOWS)
 	STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
 	PROCESS_INFORMATION pi = {0};
@@ -121,7 +121,7 @@ i32 system_exec_command_line_app(char *name, char *fmt, ...) {
 
 
 
-Array<String> setup_args(int argc, char **argv) {
+Array<String> setup_args(int argc, char const **argv) {
 	gbAllocator a = heap_allocator();
 
 #if defined(GB_SYSTEM_WINDOWS)
@@ -154,7 +154,7 @@ Array<String> setup_args(int argc, char **argv) {
 
 
 
-void print_usage_line(i32 indent, char *fmt, ...) {
+void print_usage_line(i32 indent, char const *fmt, ...) {
 	while (indent --> 0) {
 		gb_printf_err("\t");
 	}
@@ -858,7 +858,7 @@ void remove_temp_files(String output_base) {
 	gb_memmove(data.data, output_base.text, n);
 #define EXT_REMOVE(s) do {                         \
 		gb_memmove(data.data+n, s, gb_size_of(s)); \
-		gb_file_remove(cast(char *)data.data);     \
+		gb_file_remove(cast(char const *)data.data);     \
 	} while (0)
 	EXT_REMOVE(".ll");
 	EXT_REMOVE(".bc");
@@ -923,7 +923,7 @@ i32 exec_llvm_llc(String output_base) {
 #endif
 }
 
-int main(int arg_count, char **arg_ptr) {
+int main(int arg_count, char const **arg_ptr) {
 	if (arg_count < 2) {
 		usage(make_string_c(arg_ptr[0]));
 		return 1;
@@ -1153,7 +1153,7 @@ int main(int arg_count, char **arg_ptr) {
 			lib_str = gb_string_appendc(lib_str, lib_str_buf);
 		}
 
-		char *output_ext = "exe";
+		char const *output_ext = "exe";
 		gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
 		defer (gb_string_free(link_settings));
 
@@ -1319,8 +1319,8 @@ int main(int arg_count, char **arg_ptr) {
 		// Unlike the Win32 linker code, the output_ext includes the dot, because
 		// typically executable files on *NIX systems don't have extensions.
 		String output_ext = {};
-		char *link_settings = "";
-		char *linker;
+		char const *link_settings = "";
+		char const *linker;
 		if (build_context.is_dll) {
 			// Shared libraries are .dylib on MacOS and .so on Linux.
 			#if defined(GB_SYSTEM_OSX)

+ 5 - 5
src/parser.cpp

@@ -379,7 +379,7 @@ Ast *clone_ast(Ast *node) {
 }
 
 
-void error(Ast *node, char *fmt, ...) {
+void error(Ast *node, char const *fmt, ...) {
 	Token token = {};
 	if (node != nullptr) {
 		token = ast_token(node);
@@ -393,7 +393,7 @@ void error(Ast *node, char *fmt, ...) {
 	}
 }
 
-void error_no_newline(Ast *node, char *fmt, ...) {
+void error_no_newline(Ast *node, char const *fmt, ...) {
 	Token token = {};
 	if (node != nullptr) {
 		token = ast_token(node);
@@ -407,14 +407,14 @@ void error_no_newline(Ast *node, char *fmt, ...) {
 	}
 }
 
-void warning(Ast *node, char *fmt, ...) {
+void warning(Ast *node, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	warning_va(ast_token(node), fmt, va);
 	va_end(va);
 }
 
-void syntax_error(Ast *node, char *fmt, ...) {
+void syntax_error(Ast *node, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	syntax_error_va(ast_token(node), fmt, va);
@@ -1178,7 +1178,7 @@ Token expect_token(AstFile *f, TokenKind kind) {
 	return prev;
 }
 
-Token expect_token_after(AstFile *f, TokenKind kind, char *msg) {
+Token expect_token_after(AstFile *f, TokenKind kind, char const *msg) {
 	Token prev = f->curr_token;
 	if (prev.kind != kind) {
 		String p = token_strings[prev.kind];

+ 20 - 20
src/string.cpp

@@ -15,10 +15,10 @@ struct String {
 	u8 *  text;
 	isize len;
 
-	u8 &operator[](isize i) {
-		GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
-		return text[i];
-	}
+	// u8 &operator[](isize i) {
+	// 	GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
+	// 	return text[i];
+	// }
 	u8 const &operator[](isize i) const {
 		GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
 		return text[i];
@@ -48,29 +48,29 @@ struct String16 {
 };
 
 
-gb_inline String make_string(u8 *text, isize len) {
+gb_inline String make_string(u8 const *text, isize len) {
 	String s;
-	s.text = text;
+	s.text = cast(u8 *)text;
 	if (len < 0) {
-		len = gb_strlen(cast(char *)text);
+		len = gb_strlen(cast(char const *)text);
 	}
 	s.len = len;
 	return s;
 }
 
 
-gb_inline String16 make_string16(wchar_t *text, isize len) {
+gb_inline String16 make_string16(wchar_t const *text, isize len) {
 	String16 s;
-	s.text = text;
+	s.text = cast(wchar_t *)text;
 	s.len = len;
 	return s;
 }
 
-isize string16_len(wchar_t *s) {
+isize string16_len(wchar_t const *s) {
 	if (s == nullptr) {
 		return 0;
 	}
-	wchar_t *p = s;
+	wchar_t const *p = s;
 	while (*p) {
 		p++;
 	}
@@ -78,11 +78,11 @@ isize string16_len(wchar_t *s) {
 }
 
 
-gb_inline String make_string_c(char *text) {
+gb_inline String make_string_c(char const *text) {
 	return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
 }
 
-gb_inline String16 make_string16_c(wchar_t *text) {
+gb_inline String16 make_string16_c(wchar_t const *text) {
 	return make_string16(text, string16_len(text));
 }
 
@@ -366,30 +366,30 @@ String copy_string(gbAllocator a, String const &s) {
 
 
 #if defined(GB_SYSTEM_WINDOWS)
-	int convert_multibyte_to_widechar(char *multibyte_input, int input_length, wchar_t *output, int output_size) {
+	int convert_multibyte_to_widechar(char const *multibyte_input, int input_length, wchar_t *output, int output_size) {
 		return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, multibyte_input, input_length, output, output_size);
 	}
-	int convert_widechar_to_multibyte(wchar_t *widechar_input, int input_length, char *output, int output_size) {
+	int convert_widechar_to_multibyte(wchar_t const *widechar_input, int input_length, char *output, int output_size) {
 		return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, widechar_input, input_length, output, output_size, nullptr, nullptr);
 	}
 #elif defined(GB_SYSTEM_UNIX) || defined(GB_SYSTEM_OSX)
 
 	#include <iconv.h>
 
-	int convert_multibyte_to_widechar(char *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
+	int convert_multibyte_to_widechar(char const *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
 		iconv_t conv = iconv_open("WCHAR_T", "UTF-8");
 		size_t result = iconv(conv, cast(char **)&multibyte_input, &input_length, cast(char **)&output, &output_size);
 		iconv_close(conv);
 
-		return (int) result;
+		return cast(int)result;
 	}
 
-	int convert_widechar_to_multibyte(wchar_t* widechar_input, usize input_length, char* output, usize output_size) {
+	int convert_widechar_to_multibyte(wchar_t const *widechar_input, usize input_length, char* output, usize output_size) {
 		iconv_t conv = iconv_open("UTF-8", "WCHAR_T");
-		size_t result = iconv(conv, (char**) &widechar_input, &input_length, (char**) &output, &output_size);
+		size_t result = iconv(conv, cast(char**) &widechar_input, &input_length, cast(char **)&output, &output_size);
 		iconv_close(conv);
 
-		return (int) result;
+		return cast(int)result;
 	}
 #else
 #error Implement system

+ 17 - 17
src/tokenizer.cpp

@@ -227,7 +227,7 @@ void end_error_block(void) {
 }
 
 
-#define ERROR_OUT_PROC(name) void name(char *fmt, va_list va)
+#define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va)
 typedef ERROR_OUT_PROC(ErrorOutProc);
 
 ERROR_OUT_PROC(default_error_out_va) {
@@ -259,14 +259,14 @@ ERROR_OUT_PROC(default_error_out_va) {
 
 ErrorOutProc *error_out_va = default_error_out_va;
 
-void error_out(char *fmt, ...) {
+void error_out(char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	error_out_va(fmt, va);
 	va_end(va);
 }
 
-void warning_va(Token token, char *fmt, va_list va) {
+void warning_va(Token token, char const *fmt, va_list va) {
 	gb_mutex_lock(&global_error_collector.mutex);
 	global_error_collector.warning_count++;
 	// NOTE(bill): Duplicate error, skip it
@@ -283,7 +283,7 @@ void warning_va(Token token, char *fmt, va_list va) {
 }
 
 
-void error_va(Token token, char *fmt, va_list va) {
+void error_va(Token token, char const *fmt, va_list va) {
 	gb_mutex_lock(&global_error_collector.mutex);
 	global_error_collector.count++;
 	// NOTE(bill): Duplicate error, skip it
@@ -301,13 +301,13 @@ void error_va(Token token, char *fmt, va_list va) {
 	}
 }
 
-void error_line_va(char *fmt, va_list va) {
+void error_line_va(char const *fmt, va_list va) {
 	gb_mutex_lock(&global_error_collector.mutex);
 	error_out_va(fmt, va);
 	gb_mutex_unlock(&global_error_collector.mutex);
 }
 
-void error_no_newline_va(Token token, char *fmt, va_list va) {
+void error_no_newline_va(Token token, char const *fmt, va_list va) {
 	gb_mutex_lock(&global_error_collector.mutex);
 	global_error_collector.count++;
 	// NOTE(bill): Duplicate error, skip it
@@ -326,7 +326,7 @@ void error_no_newline_va(Token token, char *fmt, va_list va) {
 }
 
 
-void syntax_error_va(Token token, char *fmt, va_list va) {
+void syntax_error_va(Token token, char const *fmt, va_list va) {
 	gb_mutex_lock(&global_error_collector.mutex);
 	global_error_collector.count++;
 	// NOTE(bill): Duplicate error, skip it
@@ -345,7 +345,7 @@ void syntax_error_va(Token token, char *fmt, va_list va) {
 	}
 }
 
-void syntax_warning_va(Token token, char *fmt, va_list va) {
+void syntax_warning_va(Token token, char const *fmt, va_list va) {
 	gb_mutex_lock(&global_error_collector.mutex);
 	global_error_collector.warning_count++;
 	// NOTE(bill): Duplicate error, skip it
@@ -363,21 +363,21 @@ void syntax_warning_va(Token token, char *fmt, va_list va) {
 
 
 
-void warning(Token token, char *fmt, ...) {
+void warning(Token token, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	warning_va(token, fmt, va);
 	va_end(va);
 }
 
-void error(Token token, char *fmt, ...) {
+void error(Token token, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	error_va(token, fmt, va);
 	va_end(va);
 }
 
-void error(TokenPos pos, char *fmt, ...) {
+void error(TokenPos pos, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	Token token = {};
@@ -386,7 +386,7 @@ void error(TokenPos pos, char *fmt, ...) {
 	va_end(va);
 }
 
-void error_line(char *fmt, ...) {
+void error_line(char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	error_line_va(fmt, va);
@@ -394,14 +394,14 @@ void error_line(char *fmt, ...) {
 }
 
 
-void syntax_error(Token token, char *fmt, ...) {
+void syntax_error(Token token, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	syntax_error_va(token, fmt, va);
 	va_end(va);
 }
 
-void syntax_error(TokenPos pos, char *fmt, ...) {
+void syntax_error(TokenPos pos, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	Token token = {};
@@ -410,7 +410,7 @@ void syntax_error(TokenPos pos, char *fmt, ...) {
 	va_end(va);
 }
 
-void syntax_warning(Token token, char *fmt, ...) {
+void syntax_warning(Token token, char const *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
 	syntax_warning_va(token, fmt, va);
@@ -418,7 +418,7 @@ void syntax_warning(Token token, char *fmt, ...) {
 }
 
 
-void compiler_error(char *fmt, ...) {
+void compiler_error(char const *fmt, ...) {
 	va_list va;
 
 	va_start(va, fmt);
@@ -506,7 +506,7 @@ void restore_tokenizer_state(Tokenizer *t, TokenizerState *state) {
 }
 
 
-void tokenizer_err(Tokenizer *t, char *msg, ...) {
+void tokenizer_err(Tokenizer *t, char const *msg, ...) {
 	va_list va;
 	isize column = t->read_curr - t->line+1;
 	if (column < 1) {