aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler Erickson <me@tylererickson.com>2020-03-05 16:49:53 -0800
committerTyler Erickson <me@tylererickson.com>2020-03-05 16:49:53 -0800
commitbb026c99a93bc27ac89a8b1bf727b5b09799fe1c (patch)
tree07a3707a3d0b665cfb03c4066d0c4552dc5b0cec
parent2817bab494a093b861b33cba982d87d178e80501 (diff)
parent2fe0eaf2adf952867d4ce4fba53b4b3ac75e1ba5 (diff)
Merged with master
-rw-r--r--.gitattributes1
-rw-r--r--README.md2
-rw-r--r--core/encoding/base64/base64.odin10
-rw-r--r--core/fmt/fmt.odin13
-rw-r--r--core/log/file_console_logger.odin9
-rw-r--r--core/log/log.odin35
-rw-r--r--core/math/linalg/general.odin2
-rw-r--r--core/math/math.odin20
-rw-r--r--core/mem/mem.odin12
-rw-r--r--core/os/os.odin101
-rw-r--r--core/os/os_darwin.odin33
-rw-r--r--core/os/os_linux.odin247
-rw-r--r--core/os/os_windows.odin35
-rw-r--r--core/runtime/core.odin23
-rw-r--r--core/sort/sort.odin2
-rw-r--r--core/strings/strings.odin2
-rw-r--r--core/sys/win32/kernel32.odin5
-rw-r--r--examples/demo/demo.odin23
-rw-r--r--src/build_settings.cpp1
-rw-r--r--src/check_decl.cpp2
-rw-r--r--src/check_expr.cpp133
-rw-r--r--src/check_stmt.cpp14
-rw-r--r--src/check_type.cpp15
-rw-r--r--src/checker_builtin_procs.hpp4
-rw-r--r--src/entity.cpp1
-rw-r--r--src/ir.cpp79
-rw-r--r--src/ir_print.cpp44
-rw-r--r--src/main.cpp46
-rw-r--r--src/parser.cpp31
-rw-r--r--src/parser.hpp4
-rw-r--r--src/types.cpp51
31 files changed, 702 insertions, 298 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..6e3cae0f5
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+*.odin linguist-language=Odin
diff --git a/README.md b/README.md
index 4abaea859..4101319c2 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
Website: [https://odin-lang.org/](https://odin-lang.org/)
-```go
+```odin
package main
import "core:fmt"
diff --git a/core/encoding/base64/base64.odin b/core/encoding/base64/base64.odin
index 0224e93e4..2724aa0e5 100644
--- a/core/encoding/base64/base64.odin
+++ b/core/encoding/base64/base64.odin
@@ -49,7 +49,7 @@ encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocato
c0, c1, c2, block: int;
for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
- c0, c1, c2 = int(data[i]), 0, 0;
+ c0, c1, c2 = int(data[i]), -1, -1;
if i + 1 < length do c1 = int(data[i + 1]);
if i + 2 < length do c2 = int(data[i + 2]);
@@ -58,13 +58,13 @@ encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocato
out[d] = ENC_TBL[block >> 18 & 63];
out[d + 1] = ENC_TBL[block >> 12 & 63];
- out[d + 2] = c1 == 0 ? PADDING : ENC_TBL[block >> 6 & 63];
- out[d + 3] = c2 == 0 ? PADDING : ENC_TBL[block & 63];
+ out[d + 2] = c1 == -1 ? PADDING : ENC_TBL[block >> 6 & 63];
+ out[d + 3] = c2 == -1 ? PADDING : ENC_TBL[block & 63];
}
return string(out);
}
-decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
+decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check {
length := len(data);
if length == 0 do return []byte{};
@@ -90,4 +90,4 @@ decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocato
out[j + 2] = byte(b2);
}
return out;
-} \ No newline at end of file
+}
diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin
index ec9a0888d..c061c1c47 100644
--- a/core/fmt/fmt.odin
+++ b/core/fmt/fmt.odin
@@ -1525,10 +1525,21 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
return;
}
+
+ if info.maybe && len(info.variants) == 1 && reflect.is_pointer(info.variants[0]) {
+ if v.data == nil {
+ strings.write_string(fi.buf, "nil");
+ } else {
+ id := info.variants[0].id;
+ fmt_arg(fi, any{v.data, id}, verb);
+ }
+ return;
+ }
+
+ tag: i64 = -1;
tag_ptr := uintptr(v.data) + info.tag_offset;
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
- tag: i64 = -1;
switch i in tag_any {
case u8: tag = i64(i);
case i8: tag = i64(i);
diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin
index 0f44347f9..89e88d75e 100644
--- a/core/log/file_console_logger.odin
+++ b/core/log/file_console_logger.odin
@@ -30,17 +30,15 @@ Default_File_Logger_Opts :: Options{
File_Console_Logger_Data :: struct {
- lowest_level: Level,
file_handle: os.Handle,
ident : string,
}
create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "") -> Logger {
data := new(File_Console_Logger_Data);
- data.lowest_level = lowest;
data.file_handle = h;
data.ident = ident;
- return Logger{file_console_logger_proc, data, opt};
+ return Logger{file_console_logger_proc, data, lowest, opt};
}
destroy_file_logger ::proc(log : ^Logger) {
@@ -52,10 +50,9 @@ destroy_file_logger ::proc(log : ^Logger) {
create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "") -> Logger {
data := new(File_Console_Logger_Data);
- data.lowest_level = lowest;
data.file_handle = os.INVALID_HANDLE;
data.ident = ident;
- return Logger{file_console_logger_proc, data, opt};
+ return Logger{file_console_logger_proc, data, lowest, opt};
}
destroy_console_logger ::proc(log : ^Logger) {
@@ -65,8 +62,6 @@ destroy_console_logger ::proc(log : ^Logger) {
file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
data := cast(^File_Console_Logger_Data)logger_data;
- if level < data.lowest_level do return;
-
h : os.Handle;
if(data.file_handle != os.INVALID_HANDLE) do h = data.file_handle;
else do h = level <= Level.Error ? context.stdout : context.stderr;
diff --git a/core/log/log.odin b/core/log/log.odin
index e6a22a3a1..899f9274e 100644
--- a/core/log/log.odin
+++ b/core/log/log.odin
@@ -60,9 +60,10 @@ Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Opt
Logger :: runtime.Logger;
/*
Logger :: struct {
- procedure: Logger_Proc,
- data: rawptr,
- options: Options,
+ procedure: Logger_Proc,
+ data: rawptr,
+ lowest_level: Level,
+ options: Logger_Options,
}
*/
@@ -74,7 +75,7 @@ create_multi_logger :: proc(logs: ..Logger) -> Logger {
data := new(Multi_Logger_Data);
data.loggers = make([]Logger, len(logs));
copy(data.loggers, logs);
- return Logger{multi_logger_proc, data, nil};
+ return Logger{multi_logger_proc, data, Level.Debug, nil};
}
destroy_multi_logger :: proc(log : ^Logger) {
@@ -98,18 +99,32 @@ nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Optio
}
nil_logger :: proc() -> Logger {
- return Logger{nil_logger_proc, nil, nil};
+ return Logger{nil_logger_proc, nil, Level.Debug, nil};
}
// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
-debug :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Debug, fmt_str=fmt_str, args=args, location=location);
-info :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Info, fmt_str=fmt_str, args=args, location=location);
-warn :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Warning, fmt_str=fmt_str, args=args, location=location);
-error :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Error, fmt_str=fmt_str, args=args, location=location);
-fatal :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Fatal, fmt_str=fmt_str, args=args, location=location);
+debugf :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Debug, fmt_str=fmt_str, args=args, location=location);
+infof :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Info, fmt_str=fmt_str, args=args, location=location);
+warnf :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Warning, fmt_str=fmt_str, args=args, location=location);
+errorf :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Error, fmt_str=fmt_str, args=args, location=location);
+fatalf :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Fatal, fmt_str=fmt_str, args=args, location=location);
+
+debug :: proc(args : ..any, location := #caller_location) do log(level=Level.Debug, args=args, location=location);
+info :: proc(args : ..any, location := #caller_location) do log(level=Level.Info, args=args, location=location);
+warn :: proc(args : ..any, location := #caller_location) do log(level=Level.Warning, args=args, location=location);
+error :: proc(args : ..any, location := #caller_location) do log(level=Level.Error, args=args, location=location);
+fatal :: proc(args : ..any, location := #caller_location) do log(level=Level.Fatal, args=args, location=location);
+
+log :: proc(level : Level, args : ..any, location := #caller_location) {
+ logger := context.logger;
+ if level < logger.lowest_level do return;
+ str := fmt.tprint(..args); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
+ logger.procedure(logger.data, level, str, logger.options, location);
+}
logf :: proc(level : Level, fmt_str : string, args : ..any, location := #caller_location) {
logger := context.logger;
+ if level < logger.lowest_level do return;
str := len(args) > 0 ? fmt.tprintf(fmt_str, ..args) : fmt.tprint(fmt_str); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
logger.procedure(logger.data, level, str, logger.options, location);
}
diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin
index c24d23acf..921d56c80 100644
--- a/core/math/linalg/general.odin
+++ b/core/math/linalg/general.odin
@@ -357,7 +357,7 @@ matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
IS_NUMERIC(E) {
for i in 0..<I {
for j in 0..<J {
- c[i] += a[i][j] * b[i];
+ c[j] += a[i][j] * b[i];
}
}
return;
diff --git a/core/math/math.odin b/core/math/math.odin
index b070129a5..d5ec9f3ae 100644
--- a/core/math/math.odin
+++ b/core/math/math.odin
@@ -742,26 +742,28 @@ atan2_f64 :: proc(y, x: f64) -> f64 {
atan2 :: proc{atan2_f32, atan2_f64};
atan_f32 :: proc(x: f32) -> f32 {
- return atan2_f32(1.0, x);
+ return atan2_f32(1, x);
}
-atan :: proc{atan_f32};
-
+atan_f64 :: proc(x: f64) -> f64 {
+ return atan2_f64(1, x);
+}
+atan :: proc{atan_f32, atan_f64};
asin_f32 :: proc(x: f32) -> f32 {
- return atan2_f32(x, sqrt_f32(1 - x*x));
+ return atan2_f32(x, 1 + sqrt_f32(1 - x*x));
}
asin_f64 :: proc(x: f64) -> f64 {
- return atan2_f64(x, sqrt_f64(1 - x*x));
+ return atan2_f64(x, 1 + sqrt_f64(1 - x*x));
}
-asin :: proc{asin_f32};
+asin :: proc{asin_f32, asin_f64};
acos_f32 :: proc(x: f32) -> f32 {
- return atan2_f32(sqrt_f32(1 - x), sqrt_f32(1 + x));
+ return 2 * atan2_f32(sqrt_f32(1 - x), sqrt_f32(1 + x));
}
acos_f64 :: proc(x: f64) -> f64 {
- return atan2_f64(sqrt_f64(1 - x), sqrt_f64(1 + x));
+ return 2 * atan2_f64(sqrt_f64(1 - x), sqrt_f64(1 + x));
}
-acos :: proc{acos_f32};
+acos :: proc{acos_f32, acos_f64};
sinh_f32 :: proc(x: f32) -> f32 {
diff --git a/core/mem/mem.odin b/core/mem/mem.odin
index 144ba07a8..6dd4e9c27 100644
--- a/core/mem/mem.odin
+++ b/core/mem/mem.odin
@@ -45,8 +45,18 @@ copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawpt
return runtime.mem_copy_non_overlapping(dst, src, len);
}
compare :: inline proc "contextless" (a, b: []byte) -> int {
- return compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
+ // NOTE(tetra): no-abc is okay here because if the slices are empty, `&a[0]` is just nil+0 == nil, which
+ // compare_byte_ptrs handles fine when the passed length is also zero.
+ res := #no_bounds_check compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
+ if res == 0 && len(a) != len(b) {
+ return len(a) <= len(b) ? -1 : +1;
+ } else if len(a) == 0 && len(b) == 0 {
+ return 0;
+ } else {
+ return res;
+ }
}
+
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
x := slice_ptr(a, n);
y := slice_ptr(b, n);
diff --git a/core/os/os.odin b/core/os/os.odin
index 11cc59a8f..2c7205a54 100644
--- a/core/os/os.odin
+++ b/core/os/os.odin
@@ -129,85 +129,66 @@ read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
-/*
+
//
- // NOTE(tetra, 2019-11-10): The heap doesn't respect alignment.
- // HACK: Overallocate, align forwards, and then use the two bytes immediately before
- // the address we return, to store the padding we inserted.
- // This allows us to pass the original pointer we got back from the heap to `free` later.
+ // NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
+ // Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
+ // padding. We also store the original pointer returned by heap_alloc right before
+ // the pointer we return to the user.
//
- align_and_store_padding :: proc(ptr: rawptr, alignment: int) -> rawptr {
- ptr := mem.ptr_offset(cast(^u8) ptr, 2);
- new_ptr := cast(^u8) mem.align_forward(ptr, uintptr(alignment));
- offset := mem.ptr_sub(new_ptr, cast(^u8) ptr) + 2;
- assert(offset < int(max(u16)));
- (^[2]u8)(mem.ptr_offset(new_ptr, -2))^ = transmute([2]u8) u16(offset);
- return new_ptr;
- }
-
- recover_original_pointer :: proc(ptr: rawptr) -> rawptr {
- ptr := cast(^u8) ptr;
- offset := transmute(u16) (^[2]u8)(mem.ptr_offset(ptr, -2))^;
- ptr = mem.ptr_offset(ptr, -int(offset));
- return ptr;
- }
-
- aligned_heap_alloc :: proc(size: int, alignment: int) -> rawptr {
- // NOTE(tetra): Alignment 1 will mean we only have one extra byte.
- // This is not enough for a u16 - so we ensure there is at least two bytes extra.
- // This also means that the pointer is always aligned to at least 2.
- extra := alignment;
- if extra <= 1 do extra = 2;
-
- orig := cast(^u8) heap_alloc(size + extra);
- if orig == nil do return nil;
- ptr := align_and_store_padding(orig, alignment);
- assert(recover_original_pointer(ptr) == orig);
- return ptr;
- }
+ aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> rawptr {
+ a := max(alignment, align_of(rawptr));
+ space := size + a - 1;
- switch mode {
- case .Alloc:
- return aligned_heap_alloc(size, alignment);
+ allocated_mem: rawptr;
+ if old_ptr != nil {
+ original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^;
+ allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr));
+ } else {
+ allocated_mem = heap_alloc(space+size_of(rawptr));
+ }
+ aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)));
- case .Free:
- if old_memory != nil {
- ptr := recover_original_pointer(old_memory);
- heap_free(ptr);
+ ptr := uintptr(aligned_mem);
+ aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
+ diff := int(aligned_ptr - ptr);
+ if (size + diff) > space {
+ return nil;
}
- return nil;
- case .Free_All:
- // NOTE(bill): Does nothing
+ aligned_mem = rawptr(aligned_ptr);
+ mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
- case .Resize:
- if old_memory == nil {
- return aligned_heap_alloc(size, alignment);
+ return aligned_mem;
+ }
+
+ aligned_free :: proc(p: rawptr) {
+ if p != nil {
+ heap_free(mem.ptr_offset((^rawptr)(p), -1)^);
}
- ptr := recover_original_pointer(old_memory);
- ptr = heap_resize(ptr, size);
- assert(ptr != nil);
- return align_and_store_padding(ptr, alignment);
}
- return nil;
-*/
+ aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr {
+ if p == nil do return nil;
+ return aligned_alloc(new_size, new_alignment, p);
+ }
+
switch mode {
case .Alloc:
- return heap_alloc(size);
+ return aligned_alloc(size, alignment);
case .Free:
- if old_memory != nil {
- heap_free(old_memory);
- }
- return nil;
+ aligned_free(old_memory);
case .Free_All:
- // NOTE(bill): Does nothing
+ // NOTE(tetra): Do nothing.
case .Resize:
- return heap_resize(old_memory, size);
+ if old_memory == nil {
+ return aligned_alloc(size, alignment);
+ }
+ return aligned_resize(old_memory, old_size, size, alignment);
}
return nil;
diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin
index 3bca732ec..d70a2d1c7 100644
--- a/core/os/os_darwin.odin
+++ b/core/os/os_darwin.odin
@@ -5,6 +5,7 @@ foreign import libc "system:c"
import "core:runtime"
import "core:strings"
+import "core:c"
OS :: "darwin";
@@ -263,6 +264,8 @@ X_OK :: 1; // Test for execute permission
F_OK :: 0; // Test for file existance
foreign libc {
+ @(link_name="__error") __error :: proc() -> ^int ---;
+
@(link_name="open") _unix_open :: proc(path: cstring, flags: int, #c_vararg mode: ..any) -> Handle ---;
@(link_name="close") _unix_close :: proc(handle: Handle) ---;
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---;
@@ -278,6 +281,8 @@ foreign libc {
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
+ @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---;
+ @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---;
@(link_name="exit") _unix_exit :: proc(status: int) ---;
}
@@ -289,6 +294,10 @@ foreign dl {
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
}
+get_last_error :: proc() -> int {
+ return __error()^;
+}
+
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
cstr := strings.clone_to_cstring(path);
handle := _unix_open(cstr, flags, mode);
@@ -394,6 +403,30 @@ getenv :: proc(name: string) -> (string, bool) {
return string(cstr), true;
}
+get_current_directory :: proc() -> string {
+ page_size := get_page_size(); // NOTE(tetra): See note in os_linux.odin/get_current_directory.
+ buf := make([dynamic]u8, page_size);
+ for {
+ cwd := _unix_getcwd(cstring(#no_bounds_check &buf[0]), c.size_t(len(buf)));
+ if cwd != nil {
+ return string(cwd);
+ }
+ if Errno(get_last_error()) != ERANGE {
+ return "";
+ }
+ resize(&buf, len(buf)+page_size);
+ }
+ unreachable();
+ return "";
+}
+
+set_current_directory :: proc(path: string) -> (err: Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator);
+ res := _unix_chdir(cstr);
+ if res == -1 do return Errno(get_last_error());
+ return ERROR_NONE;
+}
+
exit :: inline proc(code: int) -> ! {
_unix_exit(code);
}
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index 3b66bfd82..e49f0f611 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -5,6 +5,7 @@ foreign import libc "system:c"
import "core:runtime"
import "core:strings"
+import "core:c"
OS :: "linux";
@@ -15,94 +16,95 @@ Syscall :: distinct int;
INVALID_HANDLE :: ~Handle(0);
-ERROR_NONE: Errno : 0;
-EPERM: Errno : 1;
-ENOENT: Errno : 2;
-ESRCH: Errno : 3;
-EINTR: Errno : 4;
-EIO: Errno : 5;
-ENXIO: Errno : 6;
-EBADF: Errno : 9;
-EAGAIN: Errno : 11;
-ENOMEM: Errno : 12;
-EACCES: Errno : 13;
-EFAULT: Errno : 14;
-EEXIST: Errno : 17;
-ENODEV: Errno : 19;
-ENOTDIR: Errno : 20;
-EISDIR: Errno : 21;
-EINVAL: Errno : 22;
-ENFILE: Errno : 23;
-EMFILE: Errno : 24;
-ETXTBSY: Errno : 26;
-EFBIG: Errno : 27;
-ENOSPC: Errno : 28;
-ESPIPE: Errno : 29;
-EROFS: Errno : 30;
-EPIPE: Errno : 32;
-
-EDEADLK: Errno : 35; /* Resource deadlock would occur */
-ENAMETOOLONG: Errno : 36; /* File name too long */
-ENOLCK: Errno : 37; /* No record locks available */
-
-ENOSYS: Errno : 38; /* Invalid system call number */
-
-ENOTEMPTY: Errno : 39; /* Directory not empty */
-ELOOP: Errno : 40; /* Too many symbolic links encountered */
-EWOULDBLOCK: Errno : EAGAIN; /* Operation would block */
-ENOMSG: Errno : 42; /* No message of desired type */
-EIDRM: Errno : 43; /* Identifier removed */
-ECHRNG: Errno : 44; /* Channel number out of range */
-EL2NSYNC: Errno : 45; /* Level 2 not synchronized */
-EL3HLT: Errno : 46; /* Level 3 halted */
-EL3RST: Errno : 47; /* Level 3 reset */
-ELNRNG: Errno : 48; /* Link number out of range */
-EUNATCH: Errno : 49; /* Protocol driver not attached */
-ENOCSI: Errno : 50; /* No CSI structure available */
-EL2HLT: Errno : 51; /* Level 2 halted */
-EBADE: Errno : 52; /* Invalid exchange */
-EBADR: Errno : 53; /* Invalid request descriptor */
-EXFULL: Errno : 54; /* Exchange full */
-ENOANO: Errno : 55; /* No anode */
-EBADRQC: Errno : 56; /* Invalid request code */
-EBADSLT: Errno : 57; /* Invalid slot */
-EDEADLOCK: Errno : EDEADLK;
-EBFONT: Errno : 59; /* Bad font file format */
-ENOSTR: Errno : 60; /* Device not a stream */
-ENODATA: Errno : 61; /* No data available */
-ETIME: Errno : 62; /* Timer expired */
-ENOSR: Errno : 63; /* Out of streams resources */
-ENONET: Errno : 64; /* Machine is not on the network */
-ENOPKG: Errno : 65; /* Package not installed */
-EREMOTE: Errno : 66; /* Object is remote */
-ENOLINK: Errno : 67; /* Link has been severed */
-EADV: Errno : 68; /* Advertise error */
-ESRMNT: Errno : 69; /* Srmount error */
-ECOMM: Errno : 70; /* Communication error on send */
-EPROTO: Errno : 71; /* Protocol error */
-EMULTIHOP: Errno : 72; /* Multihop attempted */
-EDOTDOT: Errno : 73; /* RFS specific error */
-EBADMSG: Errno : 74; /* Not a data message */
-EOVERFLOW: Errno : 75; /* Value too large for defined data type */
-ENOTUNIQ: Errno : 76; /* Name not unique on network */
-EBADFD: Errno : 77; /* File descriptor in bad state */
-EREMCHG: Errno : 78; /* Remote address changed */
-ELIBACC: Errno : 79; /* Can not access a needed shared library */
-ELIBBAD: Errno : 80; /* Accessing a corrupted shared library */
-ELIBSCN: Errno : 81; /* .lib section in a.out corrupted */
-ELIBMAX: Errno : 82; /* Attempting to link in too many shared libraries */
-ELIBEXEC: Errno : 83; /* Cannot exec a shared library directly */
-EILSEQ: Errno : 84; /* Illegal byte sequence */
-ERESTART: Errno : 85; /* Interrupted system call should be restarted */
-ESTRPIPE: Errno : 86; /* Streams pipe error */
-EUSERS: Errno : 87; /* Too many users */
-ENOTSOCK: Errno : 88; /* Socket operation on non-socket */
-EDESTADDRREQ: Errno : 89; /* Destination address required */
-EMSGSIZE: Errno : 90; /* Message too long */
-EPROTOTYPE: Errno : 91; /* Protocol wrong type for socket */
-ENOPROTOOPT: Errno : 92; /* Protocol not available */
-EPROTONOSUPPORT: Errno : 93; /* Protocol not supported */
-ESOCKTNOSUPPORT: Errno : 94; /* Socket type not supported */
+ERROR_NONE: Errno : 0;
+EPERM: Errno : 1;
+ENOENT: Errno : 2;
+ESRCH: Errno : 3;
+EINTR: Errno : 4;
+EIO: Errno : 5;
+ENXIO: Errno : 6;
+EBADF: Errno : 9;
+EAGAIN: Errno : 11;
+ENOMEM: Errno : 12;
+EACCES: Errno : 13;
+EFAULT: Errno : 14;
+EEXIST: Errno : 17;
+ENODEV: Errno : 19;
+ENOTDIR: Errno : 20;
+EISDIR: Errno : 21;
+EINVAL: Errno : 22;
+ENFILE: Errno : 23;
+EMFILE: Errno : 24;
+ETXTBSY: Errno : 26;
+EFBIG: Errno : 27;
+ENOSPC: Errno : 28;
+ESPIPE: Errno : 29;
+EROFS: Errno : 30;
+EPIPE: Errno : 32;
+
+ERANGE: Errno : 34; /* Result too large */
+EDEADLK: Errno : 35; /* Resource deadlock would occur */
+ENAMETOOLONG: Errno : 36; /* File name too long */
+ENOLCK: Errno : 37; /* No record locks available */
+
+ENOSYS: Errno : 38; /* Invalid system call number */
+
+ENOTEMPTY: Errno : 39; /* Directory not empty */
+ELOOP: Errno : 40; /* Too many symbolic links encountered */
+EWOULDBLOCK: Errno : EAGAIN; /* Operation would block */
+ENOMSG: Errno : 42; /* No message of desired type */
+EIDRM: Errno : 43; /* Identifier removed */
+ECHRNG: Errno : 44; /* Channel number out of range */
+EL2NSYNC: Errno : 45; /* Level 2 not synchronized */
+EL3HLT: Errno : 46; /* Level 3 halted */
+EL3RST: Errno : 47; /* Level 3 reset */
+ELNRNG: Errno : 48; /* Link number out of range */
+EUNATCH: Errno : 49; /* Protocol driver not attached */
+ENOCSI: Errno : 50; /* No CSI structure available */
+EL2HLT: Errno : 51; /* Level 2 halted */
+EBADE: Errno : 52; /* Invalid exchange */
+EBADR: Errno : 53; /* Invalid request descriptor */
+EXFULL: Errno : 54; /* Exchange full */
+ENOANO: Errno : 55; /* No anode */
+EBADRQC: Errno : 56; /* Invalid request code */
+EBADSLT: Errno : 57; /* Invalid slot */
+EDEADLOCK: Errno : EDEADLK;
+EBFONT: Errno : 59; /* Bad font file format */
+ENOSTR: Errno : 60; /* Device not a stream */
+ENODATA: Errno : 61; /* No data available */
+ETIME: Errno : 62; /* Timer expired */
+ENOSR: Errno : 63; /* Out of streams resources */
+ENONET: Errno : 64; /* Machine is not on the network */
+ENOPKG: Errno : 65; /* Package not installed */
+EREMOTE: Errno : 66; /* Object is remote */
+ENOLINK: Errno : 67; /* Link has been severed */
+EADV: Errno : 68; /* Advertise error */
+ESRMNT: Errno : 69; /* Srmount error */
+ECOMM: Errno : 70; /* Communication error on send */
+EPROTO: Errno : 71; /* Protocol error */
+EMULTIHOP: Errno : 72; /* Multihop attempted */
+EDOTDOT: Errno : 73; /* RFS specific error */
+EBADMSG: Errno : 74; /* Not a data message */
+EOVERFLOW: Errno : 75; /* Value too large for defined data type */
+ENOTUNIQ: Errno : 76; /* Name not unique on network */
+EBADFD: Errno : 77; /* File descriptor in bad state */
+EREMCHG: Errno : 78; /* Remote address changed */
+ELIBACC: Errno : 79; /* Can not access a needed shared library */
+ELIBBAD: Errno : 80; /* Accessing a corrupted shared library */
+ELIBSCN: Errno : 81; /* .lib section in a.out corrupted */
+ELIBMAX: Errno : 82; /* Attempting to link in too many shared libraries */
+ELIBEXEC: Errno : 83; /* Cannot exec a shared library directly */
+EILSEQ: Errno : 84; /* Illegal byte sequence */
+ERESTART: Errno : 85; /* Interrupted system call should be restarted */
+ESTRPIPE: Errno : 86; /* Streams pipe error */
+EUSERS: Errno : 87; /* Too many users */
+ENOTSOCK: Errno : 88; /* Socket operation on non-socket */
+EDESTADDRREQ: Errno : 89; /* Destination address required */
+EMSGSIZE: Errno : 90; /* Message too long */
+EPROTOTYPE: Errno : 91; /* Protocol wrong type for socket */
+ENOPROTOOPT: Errno : 92; /* Protocol not available */
+EPROTONOSUPPORT:Errno : 93; /* Protocol not supported */
+ESOCKTNOSUPPORT:Errno : 94; /* Socket type not supported */
EOPNOTSUPP: Errno : 95; /* Operation not supported on transport endpoint */
EPFNOSUPPORT: Errno : 96; /* Protocol family not supported */
EAFNOSUPPORT: Errno : 97; /* Address family not supported by protocol */
@@ -259,11 +261,11 @@ foreign libc {
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
- @(link_name="open") _unix_open :: proc(path: cstring, flags: int, mode: int) -> Handle ---;
- @(link_name="close") _unix_close :: proc(fd: Handle) -> int ---;
- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
- @(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 ---;
+ @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
+ @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---;
+ @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
+ @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
+ @(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---;
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
@(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---;
@(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---;
@@ -273,15 +275,17 @@ foreign libc {
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---;
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---;
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
- @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
+ @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---;
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
+ @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---;
+ @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---;
- @(link_name="exit") _unix_exit :: proc(status: int) -> ! ---;
+ @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---;
}
foreign dl {
- @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---;
+ @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---;
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
- @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---;
+ @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---;
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
}
@@ -295,7 +299,7 @@ get_last_error :: proc() -> int {
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
cstr := strings.clone_to_cstring(path);
- handle := _unix_open(cstr, flags, mode);
+ handle := _unix_open(cstr, c.int(flags), c.int(mode));
delete(cstr);
if handle == -1 {
return INVALID_HANDLE, Errno(get_last_error());
@@ -312,26 +316,26 @@ close :: proc(fd: Handle) -> Errno {
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
- bytes_read := _unix_read(fd, &data[0], len(data));
+ bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)));
if bytes_read == -1 {
return -1, Errno(get_last_error());
}
- return bytes_read, ERROR_NONE;
+ return int(bytes_read), ERROR_NONE;
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE;
}
- bytes_written := _unix_write(fd, &data[0], len(data));
+ bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)));
if bytes_written == -1 {
return -1, Errno(get_last_error());
}
- return bytes_written, ERROR_NONE;
+ return int(bytes_written), ERROR_NONE;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
- res := _unix_seek(fd, offset, i32(whence));
+ res := _unix_seek(fd, offset, c.int(whence));
if res == -1 {
return -1, Errno(get_last_error());
}
@@ -397,7 +401,7 @@ fstat :: inline proc(fd: Handle) -> (Stat, Errno) {
access :: inline proc(path: string, mask: int) -> (bool, Errno) {
cstr := strings.clone_to_cstring(path);
defer delete(cstr);
- result := _unix_access(cstr, mask);
+ result := _unix_access(cstr, c.int(mask));
if result == -1 {
return false, Errno(get_last_error());
}
@@ -406,11 +410,11 @@ access :: inline proc(path: string, mask: int) -> (bool, Errno) {
heap_alloc :: proc(size: int) -> rawptr {
assert(size >= 0);
- return _unix_calloc(1, size);
+ return _unix_calloc(1, c.size_t(size));
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
- return _unix_realloc(ptr, new_size);
+ return _unix_realloc(ptr, c.size_t(new_size));
}
heap_free :: proc(ptr: rawptr) {
@@ -427,8 +431,35 @@ getenv :: proc(name: string) -> (string, bool) {
return string(cstr), true;
}
+get_current_directory :: proc() -> string {
+ // NOTE(tetra): I would use PATH_MAX here, but I was not able to find
+ // an authoritative value for it across all systems.
+ // The largest value I could find was 4096, so might as well use the page size.
+ page_size := get_page_size();
+ buf := make([dynamic]u8, page_size);
+ for {
+ cwd := _unix_getcwd(cstring(#no_bounds_check &buf[0]), c.size_t(len(buf)));
+ if cwd != nil {
+ return string(cwd);
+ }
+ if Errno(get_last_error()) != ERANGE {
+ return "";
+ }
+ resize(&buf, len(buf)+page_size);
+ }
+ unreachable();
+ return "";
+}
+
+set_current_directory :: proc(path: string) -> (err: Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator);
+ res := _unix_chdir(cstr);
+ if res == -1 do return Errno(get_last_error());
+ return ERROR_NONE;
+}
+
exit :: proc(code: int) -> ! {
- _unix_exit(code);
+ _unix_exit(c.int(code));
}
current_thread_id :: proc "contextless" () -> int {
@@ -438,7 +469,7 @@ current_thread_id :: proc "contextless" () -> int {
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
cstr := strings.clone_to_cstring(filename);
defer delete(cstr);
- handle := _unix_dlopen(cstr, flags);
+ handle := _unix_dlopen(cstr, c.int(flags));
return handle;
}
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin
index 33d1f5e62..0b1a53955 100644
--- a/core/os/os_windows.odin
+++ b/core/os/os_windows.odin
@@ -2,6 +2,7 @@
package os
import "core:sys/win32"
+import "core:intrinsics"
OS :: "windows";
@@ -265,6 +266,40 @@ get_page_size :: proc() -> int {
}
+
+// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName;
+// The current directory is stored as a global variable in the process.
+@private cwd_gate := false;
+
+get_current_directory :: proc() -> string {
+ for intrinsics.atomic_xchg(&cwd_gate, true) {}
+
+ sz_utf16 := win32.get_current_directory_w(0, nil);
+ dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator); // the first time, it _includes_ the NUL.
+
+ sz_utf16 = win32.get_current_directory_w(u32(len(dir_buf_wstr)), cast(win32.Wstring) &dir_buf_wstr[0]);
+ assert(int(sz_utf16)+1 == len(dir_buf_wstr)); // the second time, it _excludes_ the NUL.
+
+ intrinsics.atomic_store(&cwd_gate, false);
+
+ dir_utf8 := win32.utf16_to_utf8(dir_buf_wstr);
+ return dir_utf8[:len(dir_utf8)-1]; // NOTE(tetra): Remove the NUL.
+}
+
+set_current_directory :: proc(path: string) -> (err: Errno) {
+ wstr := win32.utf8_to_wstring(path);
+
+ for intrinsics.atomic_xchg(&cwd_gate, true) {}
+ defer intrinsics.atomic_store(&cwd_gate, false);
+
+ res := win32.set_current_directory_w(wstr);
+ if res == 0 do return Errno(win32.get_last_error());
+
+ return;
+}
+
+
+
exit :: proc(code: int) -> ! {
win32.exit_process(u32(code));
}
diff --git a/core/runtime/core.odin b/core/runtime/core.odin
index ad8b83fee..27d40f4a3 100644
--- a/core/runtime/core.odin
+++ b/core/runtime/core.odin
@@ -111,6 +111,7 @@ Type_Info_Union :: struct {
tag_type: ^Type_Info,
custom_align: bool,
no_nil: bool,
+ maybe: bool,
};
Type_Info_Enum :: struct {
base: ^Type_Info,
@@ -275,9 +276,10 @@ Logger_Options :: bit_set[Logger_Option];
Logger_Proc :: #type proc(data: rawptr, level: Logger_Level, text: string, options: Logger_Options, location := #caller_location);
Logger :: struct {
- procedure: Logger_Proc,
- data: rawptr,
- options: Logger_Options,
+ procedure: Logger_Proc,
+ data: rawptr,
+ lowest_level: Logger_Level,
+ options: Logger_Options,
}
Context :: struct {
@@ -433,7 +435,7 @@ default_logger_proc :: proc(data: rawptr, level: Logger_Level, text: string, opt
}
default_logger :: proc() -> Logger {
- return Logger{default_logger_proc, nil, nil};
+ return Logger{default_logger_proc, nil, Logger_Level.Debug, nil};
}
@@ -1131,7 +1133,7 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
array.allocator = context.allocator;
}
assert(array.allocator.procedure != nil);
-
+
if cap <= array.cap do return true;
old_size := array.cap * elem_size;
@@ -1139,11 +1141,12 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
allocator := array.allocator;
new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc);
- if new_data == nil do return false;
-
- array.data = new_data;
- array.cap = cap;
- return true;
+ if new_data != nil || elem_size == 0 {
+ array.data = new_data;
+ array.cap = cap;
+ return true;
+ }
+ return false;
}
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
diff --git a/core/sort/sort.odin b/core/sort/sort.odin
index 24ca8fafc..dadb0d309 100644
--- a/core/sort/sort.odin
+++ b/core/sort/sort.odin
@@ -103,7 +103,7 @@ _log2 :: proc(x: int) -> int {
return res;
}
-merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) where intrinsics.type_is_ordered(T) {
+merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
merge :: proc(a: A, start, mid, end: int, f: proc(T, T) -> int) {
s, m := start, mid;
diff --git a/core/strings/strings.odin b/core/strings/strings.odin
index 6e004993a..a477b9e13 100644
--- a/core/strings/strings.odin
+++ b/core/strings/strings.odin
@@ -52,6 +52,8 @@ unsafe_string_to_cstring :: proc(str: string) -> cstring {
return cstring(d.data);
}
+// Compares two strings, returning a value representing which one comes first lexiographically.
+// -1 for `a`; 1 for `b`, or 0 if they are equal.
compare :: proc(lhs, rhs: string) -> int {
return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs);
}
diff --git a/core/sys/win32/kernel32.odin b/core/sys/win32/kernel32.odin
index 3caeb4963..ff2400556 100644
--- a/core/sys/win32/kernel32.odin
+++ b/core/sys/win32/kernel32.odin
@@ -35,6 +35,11 @@ foreign kernel32 {
@(link_name="GetVersionExA") get_version :: proc(osvi: ^OS_Version_Info_Ex_A) ---;
@(link_name="GetCurrentThreadId") get_current_thread_id :: proc() -> u32 ---;
+ // NOTE(tetra): Not thread safe with SetCurrentDirectory and GetFullPathName;
+ // The current directory is stored as a global variable in the process.
+ @(link_name="GetCurrentDirectoryW") get_current_directory_w :: proc(len: u32, buf: Wstring) -> u32 ---;
+ @(link_name="SetCurrentDirectoryW") set_current_directory_w :: proc(buf: Wstring) -> u32 ---;
+
@(link_name="GetSystemTimeAsFileTime") get_system_time_as_file_time :: proc(system_time_as_file_time: ^Filetime) ---;
@(link_name="FileTimeToLocalFileTime") file_time_to_local_file_time :: proc(file_time: ^Filetime, local_file_time: ^Filetime) -> Bool ---;
@(link_name="FileTimeToSystemTime") file_time_to_system_time :: proc(file_time: ^Filetime, system_time: ^Systemtime) -> Bool ---;
diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin
index a6b7e44f5..9704d3e36 100644
--- a/examples/demo/demo.odin
+++ b/examples/demo/demo.odin
@@ -1907,6 +1907,28 @@ constant_literal_expressions :: proc() {
fmt.println(STRING_CONST[3:][:4]);
}
+union_maybe :: proc() {
+ fmt.println("\n#union #maybe");
+
+ Maybe :: union(T: typeid) #maybe {T};
+
+ i: Maybe(u8);
+ p: Maybe(^u8); // No tag is stored for pointers, nil is the sentinel value
+
+ #assert(size_of(i) == size_of(u8) + size_of(u8));
+ #assert(size_of(p) == size_of(^u8));
+
+ i = 123;
+ x := i.?;
+ y, y_ok := p.?;
+ p = &x;
+ z, z_ok := p.?;
+
+ fmt.println(i, p);
+ fmt.println(x, &x);
+ fmt.println(y, y_ok);
+ fmt.println(z, z_ok);
+}
main :: proc() {
when true {
@@ -1937,5 +1959,6 @@ main :: proc() {
threading_example();
soa_struct_layout();
constant_literal_expressions();
+ union_maybe();
}
}
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 5a1ec0f30..c264fbaaa 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -126,6 +126,7 @@ struct BuildContext {
bool use_lld;
bool vet;
bool cross_compiling;
+ bool use_subsystem_windows;
QueryDataSetSettings query_data_set_settings;
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 0e669e473..ece38e84f 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -1207,7 +1207,7 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
}
- bool where_clause_ok = evaluate_where_clauses(ctx, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+ bool where_clause_ok = evaluate_where_clauses(ctx, nullptr, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
if (!where_clause_ok) {
// NOTE(bill, 2019-08-31): Don't check the body as the where clauses failed
return;
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index b854a693b..1a01eef31 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -2260,6 +2260,8 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
x->mode = Addressing_Value;
} else if (is_type_slice(type) && is_type_string(x->type)) {
x->mode = Addressing_Value;
+ } else if (is_type_union(type)) {
+ x->mode = Addressing_Value;
}
return true;
}
@@ -5278,6 +5280,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
+ case BuiltinProc_cpu_relax:
+ operand->mode = Addressing_NoValue;
+ break;
+
case BuiltinProc_atomic_fence:
case BuiltinProc_atomic_fence_acq:
case BuiltinProc_atomic_fence_rel:
@@ -5942,7 +5948,9 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
Entity *e = sig_params[operand_index];
Type *t = e->type;
Operand o = operands[operand_index];
- call->viral_state_flags |= o.expr->viral_state_flags;
+ if (o.expr != nullptr) {
+ call->viral_state_flags |= o.expr->viral_state_flags;
+ }
if (e->kind == Entity_TypeName) {
// GB_ASSERT(!variadic);
@@ -5983,6 +5991,15 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
}
score += s;
+ if (e->flags & EntityFlag_ConstInput) {
+ if (o.mode != Addressing_Constant) {
+ if (show_error) {
+ error(o.expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string));
+ }
+ err = CallArgumentError_NoneConstantParameter;
+ }
+ }
+
if (o.mode == Addressing_Type && is_type_typeid(e->type)) {
add_type_info_type(c, o.type);
add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type));
@@ -6238,6 +6255,15 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
}
err = CallArgumentError_WrongTypes;
}
+
+ if (e->flags & EntityFlag_ConstInput) {
+ if (o->mode != Addressing_Constant) {
+ if (show_error) {
+ error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string));
+ }
+ err = CallArgumentError_NoneConstantParameter;
+ }
+ }
}
score += s;
}
@@ -6296,7 +6322,7 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize
}
-bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array<Ast *> *clauses, bool print_err) {
+bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Array<Ast *> *clauses, bool print_err) {
if (clauses != nullptr) {
for_array(i, *clauses) {
Ast *clause = (*clauses)[i];
@@ -6304,9 +6330,11 @@ bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array<Ast *> *cla
check_expr(ctx, &o, clause);
if (o.mode != Addressing_Constant) {
if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation");
+ if (print_err && call_expr) error(call_expr, "at caller location");
return false;
} else if (o.value.kind != ExactValue_Bool) {
if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation");
+ if (print_err && call_expr) error(call_expr, "at caller location");
return false;
} else if (!o.value.value_bool) {
if (print_err) {
@@ -6348,6 +6376,7 @@ bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array<Ast *> *cla
}
}
+ if (call_expr) error(call_expr, "at caller location");
}
return false;
}
@@ -6613,7 +6642,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
ctx.curr_proc_sig = e->type;
GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- if (!evaluate_where_clauses(&ctx, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) {
+ if (!evaluate_where_clauses(&ctx, operand->expr, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) {
continue;
}
}
@@ -8572,8 +8601,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->expr = node;
return kind;
}
- Type *t = check_type(c, ta->type);
-
if (o->mode == Addressing_Constant) {
gbString expr_str = expr_to_string(o->expr);
error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str);
@@ -8594,54 +8621,80 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
bool src_is_ptr = is_type_pointer(o->type);
Type *src = type_deref(o->type);
- Type *dst = t;
Type *bsrc = base_type(src);
- Type *bdst = base_type(dst);
- if (is_type_union(src)) {
- bool ok = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
- if (are_types_identical(vt, dst)) {
- ok = true;
- break;
- }
+ if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) {
+ if (!is_type_union(src)) {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %s", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
}
-
- if (!ok) {
- gbString expr_str = expr_to_string(o->expr);
- gbString dst_type_str = type_to_string(t);
- defer (gb_string_free(expr_str));
- defer (gb_string_free(dst_type_str));
- if (bsrc->Union.variants.count == 0) {
- error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
- } else {
- error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
- }
+ if (bsrc->Union.variants.count != 1) {
+ error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
add_type_info_type(c, o->type);
- add_type_info_type(c, t);
+ add_type_info_type(c, bsrc->Union.variants[0]);
- o->type = t;
+ o->type = bsrc->Union.variants[0];
o->mode = Addressing_OptionalOk;
- } else if (is_type_any(src)) {
- o->type = t;
- o->mode = Addressing_OptionalOk;
-
- add_type_info_type(c, o->type);
- add_type_info_type(c, t);
} else {
- gbString str = type_to_string(o->type);
- error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
+ Type *t = check_type(c, ta->type);
+ Type *dst = t;
+ Type *bdst = base_type(dst);
+
+
+ if (is_type_union(src)) {
+ bool ok = false;
+ for_array(i, bsrc->Union.variants) {
+ Type *vt = bsrc->Union.variants[i];
+ if (are_types_identical(vt, dst)) {
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ gbString expr_str = expr_to_string(o->expr);
+ gbString dst_type_str = type_to_string(t);
+ defer (gb_string_free(expr_str));
+ defer (gb_string_free(dst_type_str));
+ if (bsrc->Union.variants.count == 0) {
+ error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
+ } else {
+ error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
+ }
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, t);
+
+ o->type = t;
+ o->mode = Addressing_OptionalOk;
+ } else if (is_type_any(src)) {
+ o->type = t;
+ o->mode = Addressing_OptionalOk;
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, t);
+ } else {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
}
add_package_dependency(c, "runtime", "type_assertion_check");
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index c365dca84..4b250c6a6 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -314,7 +314,11 @@ Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs)
gbString str = expr_to_string(lhs->expr);
if (e != nullptr && e->flags & EntityFlag_Param) {
- error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str);
+ if (e->flags & EntityFlag_Using) {
+ error(lhs->expr, "Cannot assign to '%s' which is from a 'using' procedure parameter", str);
+ } else {
+ error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str);
+ }
} else {
error(lhs->expr, "Cannot assign to '%s'", str);
}
@@ -497,6 +501,8 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
Entity *f = found->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, expr);
+ if (e->flags & EntityFlag_Value) uvar->flags |= EntityFlag_Value;
+ if (e->flags & EntityFlag_Param) uvar->flags |= EntityFlag_Param;
Entity *prev = scope_insert(ctx->scope, uvar);
if (prev != nullptr) {
gbString expr_str = expr_to_string(expr);
@@ -1102,6 +1108,12 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (type_expr != nullptr) { // Otherwise it's a default expression
Operand y = {};
check_expr_or_type(ctx, &y, type_expr);
+ if (y.mode != Addressing_Type) {
+ gbString str = expr_to_string(type_expr);
+ error(type_expr, "Expected a type as a case, got %s", str);
+ gb_string_free(str);
+ continue;
+ }
if (switch_kind == TypeSwitch_Union) {
GB_ASSERT(is_type_union(bt));
diff --git a/src/check_type.cpp b/src/check_type.cpp
index f21c1563b..6194951c9 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -527,7 +527,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
if (st->where_clauses.count > 0 && st->polymorphic_params == nullptr) {
error(st->where_clauses[0], "'where' clauses can only be used on structures with polymorphic parameters");
} else {
- bool where_clause_ok = evaluate_where_clauses(ctx, ctx->scope, &st->where_clauses, true);
+ bool where_clause_ok = evaluate_where_clauses(ctx, node, ctx->scope, &st->where_clauses, true);
}
check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context);
}
@@ -714,7 +714,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
if (ut->where_clauses.count > 0 && ut->polymorphic_params == nullptr) {
error(ut->where_clauses[0], "'where' clauses can only be used on unions with polymorphic parameters");
} else {
- bool where_clause_ok = evaluate_where_clauses(ctx, ctx->scope, &ut->where_clauses, true);
+ bool where_clause_ok = evaluate_where_clauses(ctx, node, ctx->scope, &ut->where_clauses, true);
}
@@ -748,11 +748,17 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
union_type->Union.variants = variants;
union_type->Union.no_nil = ut->no_nil;
+ union_type->Union.maybe = ut->maybe;
if (union_type->Union.no_nil) {
if (variants.count < 2) {
error(ut->align, "A union with #no_nil must have at least 2 variants");
}
}
+ if (union_type->Union.maybe) {
+ if (variants.count != 1) {
+ error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
+ }
+ }
if (ut->align != nullptr) {
i64 custom_align = 1;
@@ -1716,8 +1722,11 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
if (p->flags&FieldFlag_auto_cast) {
param->flags |= EntityFlag_AutoCast;
}
- param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
+ if (p->flags&FieldFlag_const) {
+ param->flags |= EntityFlag_ConstInput;
+ }
+ param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
add_entity(ctx->checker, scope, name, param);
if (is_using) {
add_entity_use(ctx, name, param);
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index 4981fdedb..7ef1be8b8 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -36,6 +36,8 @@ enum BuiltinProcId {
BuiltinProc_simd_vector,
BuiltinProc_soa_struct,
+ BuiltinProc_cpu_relax,
+
BuiltinProc_atomic_fence,
BuiltinProc_atomic_fence_acq,
BuiltinProc_atomic_fence_rel,
@@ -214,6 +216,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
{STR_LIT("soa_struct"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
+ {STR_LIT("cpu_relax"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+
{STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
diff --git a/src/entity.cpp b/src/entity.cpp
index 8273af3f1..b89522b07 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -46,6 +46,7 @@ enum EntityFlag {
EntityFlag_BitFieldValue = 1<<12,
EntityFlag_PolyConst = 1<<13,
EntityFlag_NotExported = 1<<14,
+ EntityFlag_ConstInput = 1<<15,
EntityFlag_Static = 1<<16,
diff --git a/src/ir.cpp b/src/ir.cpp
index 5d075d7dd..6a56eb387 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -197,6 +197,7 @@ gbAllocator ir_allocator(void) {
IR_INSTR_KIND(ZeroInit, struct { irValue *address; }) \
IR_INSTR_KIND(Store, struct { irValue *address, *value; bool is_volatile; }) \
IR_INSTR_KIND(Load, struct { Type *type; irValue *address; i64 custom_align; }) \
+ IR_INSTR_KIND(InlineCode, struct { BuiltinProcId id; Array<irValue *> operands; }) \
IR_INSTR_KIND(AtomicFence, struct { BuiltinProcId id; }) \
IR_INSTR_KIND(AtomicStore, struct { \
irValue *address, *value; \
@@ -1063,6 +1064,14 @@ irValue *ir_instr_load(irProcedure *p, irValue *address) {
return v;
}
+irValue *ir_instr_inline_code(irProcedure *p, BuiltinProcId id, Array<irValue *> operands) {
+ irValue *v = ir_alloc_instr(p, irInstr_InlineCode);
+ irInstr *i = &v->Instr;
+ i->InlineCode.id = id;
+ i->InlineCode.operands = operands;
+ return v;
+}
+
irValue *ir_instr_atomic_fence(irProcedure *p, BuiltinProcId id) {
irValue *v = ir_alloc_instr(p, irInstr_AtomicFence);
irInstr *i = &v->Instr;
@@ -1222,7 +1231,11 @@ irValue *ir_instr_union_tag_ptr(irProcedure *p, irValue *address) {
// i->UnionTagPtr.type = alloc_type_pointer(t_type_info_ptr);
Type *u = type_deref(ir_type(address));
+ if (is_type_union_maybe_pointer(u)) {
+ GB_PANIC("union #maybe UnionTagPtr");
+ }
i->UnionTagPtr.type = alloc_type_pointer(union_tag_type(u));
+
return v;
}
@@ -1234,6 +1247,9 @@ irValue *ir_instr_union_tag_value(irProcedure *p, irValue *address) {
if (address) address->uses += 1;
Type *u = type_deref(ir_type(address));
+ if (is_type_union_maybe_pointer(u)) {
+ GB_PANIC("union #maybe UnionTagValue");
+ }
i->UnionTagPtr.type = union_tag_type(u);
return v;
}
@@ -4351,7 +4367,9 @@ irValue *ir_emit_union_tag_value(irProcedure *proc, irValue *u) {
irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue *x) {
Type *t = ir_type(x);
- if (is_type_cstring(t)) {
+ if (is_type_pointer(t)) {
+ return ir_emit_comp(proc, op_kind, x, v_raw_nil);
+ } else if (is_type_cstring(t)) {
irValue *ptr = ir_emit_conv(proc, x, t_u8_ptr);
return ir_emit_comp(proc, op_kind, ptr, v_raw_nil);
} else if (is_type_any(t)) {
@@ -4396,6 +4414,12 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
} else if (is_type_union(t)) {
if (type_size_of(t) == 0) {
return ir_emit_comp(proc, op_kind, v_zero, v_zero);
+ } else if (is_type_union_maybe_pointer(t)) {
+ Type *bt = base_type(t);
+ irValue *ptr = ir_address_from_load_or_generate_local(proc, x);
+ ptr = ir_emit_bitcast(proc, ptr, alloc_type_pointer(bt->Union.variants[0]));
+ irValue *data = ir_emit_load(proc, ptr);
+ return ir_emit_comp_against_nil(proc, op_kind, data);
} else {
irValue *tag = ir_emit_union_tag_value(proc, x);
return ir_emit_comp(proc, op_kind, tag, v_zero);
@@ -5232,8 +5256,12 @@ void ir_emit_store_union_variant(irProcedure *proc, irValue *parent, irValue *va
Type *t = type_deref(ir_type(parent));
- irValue *tag_ptr = ir_emit_union_tag_ptr(proc, parent);
- ir_emit_store(proc, tag_ptr, ir_const_union_tag(t, variant_type));
+ if (is_type_union_maybe_pointer(t)) {
+ // No tag needed!
+ } else {
+ irValue *tag_ptr = ir_emit_union_tag_ptr(proc, parent);
+ ir_emit_store(proc, tag_ptr, ir_const_union_tag(t, variant_type));
+ }
}
irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
@@ -5701,7 +5729,7 @@ irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t) {
}
// TODO(bill): Actually figure out what the conversion needs to be correctly 'cause LLVM
- return ir_emit_bitcast(proc, value, dst);
+ return ir_emit_bitcast(proc, value, t);
}
@@ -5728,22 +5756,41 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token
GB_ASSERT_MSG(is_type_union(src), "%s", type_to_string(src_type));
Type *dst = tuple->Tuple.variables[0]->type;
-
irValue *value_ = ir_address_from_load_or_generate_local(proc, value);
- irValue *tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value_));
- irValue *dst_tag = ir_const_union_tag(src, dst);
+ irValue *tag = nullptr;
+ irValue *dst_tag = nullptr;
+ irValue *cond = nullptr;
+ irValue *data = nullptr;
+
+ irValue *gep0 = ir_emit_struct_ep(proc, v, 0);
+ irValue *gep1 = ir_emit_struct_ep(proc, v, 1);
+
+ if (is_type_union_maybe_pointer(src)) {
+ data = ir_emit_load(proc, ir_emit_conv(proc, value_, ir_type(gep0)));
+ } else {
+ tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value_));
+ dst_tag = ir_const_union_tag(src, dst);
+ }
irBlock *ok_block = ir_new_block(proc, nullptr, "union_cast.ok");
irBlock *end_block = ir_new_block(proc, nullptr, "union_cast.end");
- irValue *cond = ir_emit_comp(proc, Token_CmpEq, tag, dst_tag);
+
+ if (data != nullptr) {
+ GB_ASSERT(is_type_union_maybe_pointer(src));
+ cond = ir_emit_comp_against_nil(proc, Token_NotEq, data);
+ } else {
+ cond = ir_emit_comp(proc, Token_CmpEq, tag, dst_tag);
+ }
+
ir_emit_if(proc, cond, ok_block, end_block);
ir_start_block(proc, ok_block);
- irValue *gep0 = ir_emit_struct_ep(proc, v, 0);
- irValue *gep1 = ir_emit_struct_ep(proc, v, 1);
- irValue *data = ir_emit_load(proc, ir_emit_conv(proc, value_, ir_type(gep0)));
+
+ if (data == nullptr) {
+ data = ir_emit_load(proc, ir_emit_conv(proc, value_, ir_type(gep0)));
+ }
ir_emit_store(proc, gep0, data);
ir_emit_store(proc, gep1, v_true);
@@ -6876,6 +6923,9 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
// "Intrinsics"
+ case BuiltinProc_cpu_relax:
+ return ir_emit(proc, ir_instr_inline_code(proc, id, {}));
+
case BuiltinProc_atomic_fence:
case BuiltinProc_atomic_fence_acq:
case BuiltinProc_atomic_fence_rel:
@@ -7344,10 +7394,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
GB_ASSERT(are_types_identical(ir_type(left), key_type));
Type *it = bit_set_to_int(rt);
+ left = ir_emit_conv(proc, left, it);
irValue *lower = ir_value_constant(it, exact_value_i64(rt->BitSet.lower));
- irValue *key = ir_emit_arith(proc, Token_Sub, left, lower, ir_type(left));
- irValue *bit = ir_emit_arith(proc, Token_Shl, v_one, key, ir_type(left));
+ irValue *key = ir_emit_arith(proc, Token_Sub, left, lower, it);
+ irValue *bit = ir_emit_arith(proc, Token_Shl, v_one, key, it);
bit = ir_emit_conv(proc, bit, it);
irValue *old_value = ir_emit_bitcast(proc, right, it);
@@ -11465,6 +11516,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
irValue *tag_type_ptr = ir_emit_struct_ep(proc, tag, 2);
irValue *custom_align_ptr = ir_emit_struct_ep(proc, tag, 3);
irValue *no_nil_ptr = ir_emit_struct_ep(proc, tag, 4);
+ irValue *maybe_ptr = ir_emit_struct_ep(proc, tag, 5);
isize variant_count = gb_max(0, t->Union.variants.count);
irValue *memory_types = ir_type_info_member_types_offset(proc, variant_count);
@@ -11494,6 +11546,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
ir_emit_store(proc, custom_align_ptr, is_custom_align);
ir_emit_store(proc, no_nil_ptr, ir_const_bool(t->Union.no_nil));
+ ir_emit_store(proc, maybe_ptr, ir_const_bool(t->Union.maybe));
}
break;
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 2baf1c2a0..bcbdca615 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -508,13 +508,25 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
// LLVM takes the first element's alignment as the entire alignment (like C)
i64 align = type_align_of(t);
+
+ if (is_type_union_maybe_pointer_original_alignment(t)) {
+ ir_write_byte(f, '{');
+ ir_print_type(f, m, t->Union.variants[0]);
+ ir_write_byte(f, '}');
+ return;
+ }
+
i64 block_size = t->Union.variant_block_size;
ir_write_byte(f, '{');
ir_print_alignment_prefix_hack(f, align);
- ir_fprintf(f, ", [%lld x i8], ", block_size);
- // ir_print_type(f, m, t_type_info_ptr);
- ir_print_type(f, m, union_tag_type(t));
+ if (is_type_union_maybe_pointer(t)) {
+ ir_fprintf(f, ", ");
+ ir_print_type(f, m, t->Union.variants[0]);
+ } else {
+ ir_fprintf(f, ", [%lld x i8], ", block_size);
+ ir_print_type(f, m, union_tag_type(t));
+ }
ir_write_byte(f, '}');
}
return;
@@ -1471,6 +1483,18 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
break;
}
+ case irInstr_InlineCode:
+ {
+ switch (instr->InlineCode.id) {
+ case BuiltinProc_cpu_relax:
+ ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
+ break;
+ default: GB_PANIC("Unknown inline code %d", instr->InlineCode.id); break;
+ }
+ }
+ break;
+
+
case irInstr_AtomicFence:
ir_write_str_lit(f, "fence ");
switch (instr->AtomicFence.id) {
@@ -1850,6 +1874,12 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_UnionTagPtr: {
Type *et = ir_type(instr->UnionTagPtr.address);
+
+ Type *ut = type_deref(et);
+ if (is_type_union_maybe_pointer(ut)) {
+ GB_PANIC("union #maybe UnionTagPtr");
+ }
+
ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
Type *t = base_type(type_deref(et));
GB_ASSERT(is_type_union(t));
@@ -1869,10 +1899,16 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_UnionTagValue: {
Type *et = ir_type(instr->UnionTagValue.address);
- ir_fprintf(f, "%%%d = extractvalue ", value->index);
Type *t = base_type(et);
+
+ if (is_type_union_maybe_pointer(t)) {
+ GB_PANIC("union #maybe UnionTagValue");
+ }
+
+ ir_fprintf(f, "%%%d = extractvalue ", value->index);
GB_ASSERT(is_type_union(t));
+
ir_print_type(f, m, et);
ir_write_byte(f, ' ');
ir_print_value(f, m, instr->UnionTagValue.address, et);
diff --git a/src/main.cpp b/src/main.cpp
index 87669f8e3..077603b30 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -243,6 +243,7 @@ enum BuildFlagKind {
#if defined(GB_SYSTEM_WINDOWS)
BuildFlag_ResourceFile,
BuildFlag_WindowsPdbName,
+ BuildFlag_Subsystem,
#endif
BuildFlag_COUNT,
@@ -331,8 +332,9 @@ bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None);
#if defined(GB_SYSTEM_WINDOWS)
- add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String);
- add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String);
+ add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String);
+ add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String);
+ add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String);
#endif
GB_ASSERT(args.count >= 3);
@@ -764,7 +766,19 @@ bool parse_build_flags(Array<String> args) {
bad_flags = true;
}
break;
+ }
+ case BuildFlag_Subsystem: {
+ GB_ASSERT(value.kind == ExactValue_String);
+ String subsystem = value.value_string;
+ if (str_eq_ignore_case(subsystem, str_lit("console"))) {
+ build_context.use_subsystem_windows = false;
+ } else if (str_eq_ignore_case(subsystem, str_lit("windows"))) {
+ build_context.use_subsystem_windows = true;
+ } else {
+ gb_printf_err("Invalid -subsystem string, got %.*s, expected either 'console' or 'windows'\n", LIT(subsystem));
+ bad_flags = true;
+ }
break;
}
#endif
@@ -1180,6 +1194,10 @@ int main(int arg_count, char const **arg_ptr) {
return 1;
}
+ if (init_filename == "-help") {
+ build_context.show_help = true;
+ }
+
build_context.command = command;
if (!parse_build_flags(args)) {
@@ -1372,8 +1390,8 @@ int main(int arg_count, char const **arg_ptr) {
}
+ char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
if (!build_context.use_lld) { // msvc
-
if (build_context.has_resource) {
exit_code = system_exec_command_line_app("msvc-link",
"\"%.*src.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
@@ -1388,36 +1406,42 @@ int main(int arg_count, char const **arg_ptr) {
exit_code = system_exec_command_line_app("msvc-link",
"\"%.*slink.exe\" \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
+ "/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %s "
"",
LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), LIT(output_base), output_ext,
- link_settings, LIT(build_context.link_flags),
+ link_settings,
+ subsystem_str,
+ LIT(build_context.link_flags),
lib_str
);
} else {
exit_code = system_exec_command_line_app("msvc-link",
"\"%.*slink.exe\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
+ "/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %s "
"",
LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), output_ext,
- link_settings, LIT(build_context.link_flags),
+ link_settings,
+ subsystem_str,
+ LIT(build_context.link_flags),
lib_str
);
}
} else { // lld
exit_code = system_exec_command_line_app("msvc-link",
"\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
+ "/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %s "
"",
LIT(build_context.ODIN_ROOT),
LIT(output_base), LIT(output_base), output_ext,
- link_settings, LIT(build_context.link_flags),
+ link_settings,
+ subsystem_str,
+ LIT(build_context.link_flags),
lib_str
);
}
@@ -1500,11 +1524,11 @@ int main(int arg_count, char const **arg_ptr) {
// Shared libraries are .dylib on MacOS and .so on Linux.
#if defined(GB_SYSTEM_OSX)
output_ext = STR_LIT(".dylib");
+ link_settings = "-dylib -dynamic";
#else
output_ext = STR_LIT(".so");
+ link_settings = "-shared";
#endif
-
- link_settings = "-shared";
} else {
// TODO: Do I need anything here?
link_settings = "";
diff --git a/src/parser.cpp b/src/parser.cpp
index ecb02c803..f89b5676b 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -929,7 +929,7 @@ Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_c
}
-Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil,
+Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_UnionType);
result->UnionType.token = token;
@@ -937,6 +937,7 @@ Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymor
result->UnionType.polymorphic_params = polymorphic_params;
result->UnionType.align = align;
result->UnionType.no_nil = no_nil;
+ result->UnionType.maybe = maybe;
result->UnionType.where_token = where_token;
result->UnionType.where_clauses = where_clauses;
return result;
@@ -2091,6 +2092,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Ast *polymorphic_params = nullptr;
Ast *align = nullptr;
bool no_nil = false;
+ bool maybe = false;
CommentGroup *docs = f->lead_comment;
Token start_token = f->curr_token;
@@ -2118,10 +2120,19 @@ Ast *parse_operand(AstFile *f, bool lhs) {
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
}
no_nil = true;
- } else {
+ } else if (tag.string == "maybe") {
+ if (maybe) {
+ syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
+ }
+ maybe = true;
+ }else {
syntax_error(tag, "Invalid union tag '#%.*s'", LIT(tag.string));
}
}
+ if (no_nil && maybe) {
+ syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together");
+ }
+
Token where_token = {};
Array<Ast *> where_clauses = {};
@@ -2150,7 +2161,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Token close = expect_token(f, Token_CloseBrace);
- return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, where_token, where_clauses);
+ return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses);
} break;
case Token_enum: {
@@ -2350,6 +2361,12 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
operand = ast_type_assertion(f, operand, token, type);
} break;
+ case Token_Question: {
+ Token question = expect_token(f, Token_Question);
+ Ast *type = ast_unary_expr(f, question, nullptr);
+ operand = ast_type_assertion(f, operand, token, type);
+ } break;
+
default:
syntax_error(f->curr_token, "Expected a selector");
advance_token(f);
@@ -2981,6 +2998,7 @@ enum FieldPrefixKind {
FieldPrefix_Invalid = 0,
FieldPrefix_using,
+ FieldPrefix_const,
FieldPrefix_no_alias,
FieldPrefix_c_var_arg,
FieldPrefix_auto_cast,
@@ -3007,6 +3025,9 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
return FieldPrefix_c_var_arg;
}
break;
+
+ case Token_const:
+ return FieldPrefix_const;
}
return FieldPrefix_Unknown;
}
@@ -3019,6 +3040,7 @@ u32 parse_field_prefixes(AstFile *f) {
i32 no_alias_count = 0;
i32 c_vararg_count = 0;
i32 auto_cast_count = 0;
+ i32 const_count = 0;
for (;;) {
FieldPrefixKind kind = is_token_field_prefix(f);
@@ -3036,12 +3058,14 @@ u32 parse_field_prefixes(AstFile *f) {
case FieldPrefix_no_alias: no_alias_count += 1; advance_token(f); break;
case FieldPrefix_c_var_arg: c_vararg_count += 1; advance_token(f); break;
case FieldPrefix_auto_cast: auto_cast_count += 1; advance_token(f); break;
+ case FieldPrefix_const: const_count += 1; advance_token(f); break;
}
}
if (using_count > 1) syntax_error(f->curr_token, "Multiple 'using' in this field list");
if (no_alias_count > 1) syntax_error(f->curr_token, "Multiple '#no_alias' in this field list");
if (c_vararg_count > 1) syntax_error(f->curr_token, "Multiple '#c_vararg' in this field list");
if (auto_cast_count > 1) syntax_error(f->curr_token, "Multiple 'auto_cast' in this field list");
+ if (const_count > 1) syntax_error(f->curr_token, "Multiple '#const' in this field list");
u32 field_flags = 0;
@@ -3049,6 +3073,7 @@ u32 parse_field_prefixes(AstFile *f) {
if (no_alias_count > 0) field_flags |= FieldFlag_no_alias;
if (c_vararg_count > 0) field_flags |= FieldFlag_c_vararg;
if (auto_cast_count > 0) field_flags |= FieldFlag_auto_cast;
+ if (const_count > 0) field_flags |= FieldFlag_const;
return field_flags;
}
diff --git a/src/parser.hpp b/src/parser.hpp
index 983db1042..6426cc96b 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -203,12 +203,13 @@ enum FieldFlag {
FieldFlag_no_alias = 1<<2,
FieldFlag_c_vararg = 1<<3,
FieldFlag_auto_cast = 1<<4,
+ FieldFlag_const = 1<<5,
FieldFlag_Tags = 1<<10,
FieldFlag_Results = 1<<16,
- FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast,
+ FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const,
FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags,
};
@@ -515,6 +516,7 @@ AST_KIND(_TypeBegin, "", bool) \
Array<Ast *> variants; \
Ast *polymorphic_params; \
Ast * align; \
+ bool maybe; \
bool no_nil; \
Token where_token; \
Array<Ast *> where_clauses; \
diff --git a/src/types.cpp b/src/types.cpp
index e120c77c0..d2a040b0b 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -152,6 +152,7 @@ struct TypeUnion {
Type * polymorphic_params; // Type_Tuple
Type * polymorphic_parent;
bool no_nil;
+ bool maybe;
bool is_polymorphic;
bool is_poly_specialized;
};
@@ -1219,6 +1220,32 @@ bool is_type_map(Type *t) {
return t->kind == Type_Map;
}
+bool is_type_union_maybe_pointer(Type *t) {
+ t = base_type(t);
+ if (t->kind == Type_Union && t->Union.maybe) {
+ if (t->Union.variants.count == 1) {
+ return is_type_pointer(t->Union.variants[0]);
+ }
+ }
+ return false;
+}
+
+
+bool is_type_union_maybe_pointer_original_alignment(Type *t) {
+ t = base_type(t);
+ if (t->kind == Type_Union && t->Union.maybe) {
+ if (t->Union.variants.count == 1) {
+ Type *v = t->Union.variants[0];
+ if (is_type_pointer(v)) {
+ return type_align_of(v) == type_align_of(t);
+ }
+ }
+ }
+ return false;
+}
+
+
+
bool is_type_integer_endian_big(Type *t) {
t = core_type(t);
@@ -2024,6 +2051,7 @@ i64 union_tag_size(Type *u) {
Type *union_tag_type(Type *u) {
i64 s = union_tag_size(u);
switch (s) {
+ case 0: return t_u8;
case 1: return t_u8;
case 2: return t_u16;
case 4: return t_u32;
@@ -2934,14 +2962,23 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
}
}
- // NOTE(bill): Align to tag
- i64 tag_size = union_tag_size(t);
- i64 size = align_formula(max, tag_size);
- // NOTE(bill): Calculate the padding between the common fields and the tag
- t->Union.tag_size = tag_size;
- t->Union.variant_block_size = size - field_size;
+ i64 size = 0;
+
+ if (is_type_union_maybe_pointer(t)) {
+ size = max;
+ t->Union.tag_size = 0;
+ t->Union.variant_block_size = size;
+ } else {
+ // NOTE(bill): Align to tag
+ i64 tag_size = union_tag_size(t);
+ size = align_formula(max, tag_size);
+ // NOTE(bill): Calculate the padding between the common fields and the tag
+ t->Union.tag_size = tag_size;
+ t->Union.variant_block_size = size - field_size;
- return align_formula(size + tag_size, align);
+ size += tag_size;
+ }
+ return align_formula(size, align);
} break;