From 66d1656367387bcf17e7e6da4c387b2e95919801 Mon Sep 17 00:00:00 2001 From: Tetralux Date: Sat, 11 Jan 2020 17:44:49 +0000 Subject: [PATCH 01/28] Add os.get_current_directory / os.set_current_directory --- core/os/os_darwin.odin | 33 +++++ core/os/os_linux.odin | 253 ++++++++++++++++++++--------------- core/os/os_windows.odin | 35 +++++ core/sys/win32/kernel32.odin | 5 + 4 files changed, 215 insertions(+), 111 deletions(-) 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 0f244c526..b01d95330 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; +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 */ +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 */ +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 */ +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,29 +261,31 @@ 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="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---; - @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^Stat) -> int ---; - @(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---; + @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---; + @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> c.int ---; + @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^Stat) -> c.int ---; + @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---; - @(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---; - @(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---; + @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---; + @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> 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/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 ---; From a4b60b78c805c746e1033ca21a2b6f032a50db93 Mon Sep 17 00:00:00 2001 From: Tetralux Date: Tue, 14 Jan 2020 18:21:51 +0000 Subject: [PATCH 02/28] Fix heap allocator alignment on all platforms --- core/os/os.odin | 101 ++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 60 deletions(-) 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; + aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> rawptr { + a := max(alignment, align_of(rawptr)); + space := size + a - 1; + + 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))); + + ptr := uintptr(aligned_mem); + aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a); + diff := int(aligned_ptr - ptr); + if (size + diff) > space { + return nil; + } + + aligned_mem = rawptr(aligned_ptr); + mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem; + + return aligned_mem; } - 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_free :: proc(p: rawptr) { + if p != nil { + heap_free(mem.ptr_offset((^rawptr)(p), -1)^); + } } - 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_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 aligned_heap_alloc(size, alignment); + return aligned_alloc(size, alignment); case .Free: - if old_memory != nil { - ptr := recover_original_pointer(old_memory); - heap_free(ptr); - } - return nil; + aligned_free(old_memory); case .Free_All: - // NOTE(bill): Does nothing + // NOTE(tetra): Do nothing. case .Resize: if old_memory == nil { - return aligned_heap_alloc(size, alignment); + return aligned_alloc(size, alignment); } - ptr := recover_original_pointer(old_memory); - ptr = heap_resize(ptr, size); - assert(ptr != nil); - return align_and_store_padding(ptr, alignment); - } - - return nil; -*/ - switch mode { - case .Alloc: - return heap_alloc(size); - - case .Free: - if old_memory != nil { - heap_free(old_memory); - } - return nil; - - case .Free_All: - // NOTE(bill): Does nothing - - case .Resize: - return heap_resize(old_memory, size); + return aligned_resize(old_memory, old_size, size, alignment); } return nil; From 3a30f9fd71caeb7f556cc3b48f128c3177603825 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 29 Jan 2020 12:04:54 +0000 Subject: [PATCH 03/28] Create .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..6e3cae0f5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.odin linguist-language=Odin From e9f3ebba13e56233ff51fe47360a7e8ab213e809 Mon Sep 17 00:00:00 2001 From: Aleksander Birkeland Date: Thu, 30 Jan 2020 01:01:01 +0100 Subject: [PATCH 04/28] Update README.md to use Odin syntax highlighting instead of Go. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" From 7e6454b2f7dff65e3b9a9dbd644b5a0905b09fff Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 31 Jan 2020 23:34:00 +0000 Subject: [PATCH 05/28] Add `odin [command] -help` (not requiring a file to be passed) --- src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 87669f8e3..317e8f577 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1180,6 +1180,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)) { From 0f399a72941c7cebcb5ad0580a9d94d1a7a37ac0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 Feb 2020 11:10:28 +0000 Subject: [PATCH 06/28] Add `union #maybe` --- core/fmt/fmt.odin | 13 +++++- core/runtime/core.odin | 3 +- examples/demo/demo.odin | 23 ++++++++++ src/check_expr.cpp | 98 +++++++++++++++++++++++++---------------- src/check_type.cpp | 6 +++ src/ir.cpp | 60 +++++++++++++++++++------ src/ir_print.cpp | 32 ++++++++++++-- src/parser.cpp | 23 ++++++++-- src/parser.hpp | 1 + src/types.cpp | 51 ++++++++++++++++++--- 10 files changed, 244 insertions(+), 66 deletions(-) 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/runtime/core.odin b/core/runtime/core.odin index ad8b83fee..4ffd716ef 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, @@ -1131,7 +1132,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; 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/check_expr.cpp b/src/check_expr.cpp index b854a693b..52c1f38b7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8572,8 +8572,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 +8592,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_type.cpp b/src/check_type.cpp index f21c1563b..f21ffc956 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -748,11 +748,17 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, ArrayUnion.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; diff --git a/src/ir.cpp b/src/ir.cpp index 5d075d7dd..2f4d491e7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1222,7 +1222,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 +1238,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 +4358,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)) { @@ -5232,8 +5241,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) { @@ -5728,22 +5741,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); - - 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); - ir_emit_if(proc, cond, ok_block, end_block); - ir_start_block(proc, ok_block); + 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); - irValue *data = ir_emit_load(proc, ir_emit_conv(proc, value_, ir_type(gep0))); + 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"); + + 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); + + + + 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); @@ -11465,6 +11497,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 +11527,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..a3f28c59f 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; @@ -1850,6 +1862,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 +1887,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/parser.cpp b/src/parser.cpp index ecb02c803..2e2a1b97e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -929,7 +929,7 @@ Ast *ast_struct_type(AstFile *f, Token token, Array fields, isize field_c } -Ast *ast_union_type(AstFile *f, Token token, Array variants, Ast *polymorphic_params, Ast *align, bool no_nil, +Ast *ast_union_type(AstFile *f, Token token, Array variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe, Token where_token, Array 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 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 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); diff --git a/src/parser.hpp b/src/parser.hpp index 983db1042..cdfd4eba1 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -515,6 +515,7 @@ AST_KIND(_TypeBegin, "", bool) \ Array variants; \ Ast *polymorphic_params; \ Ast * align; \ + bool maybe; \ bool no_nil; \ Token where_token; \ Array 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; - return align_formula(size + tag_size, align); + 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; + + size += tag_size; + } + return align_formula(size, align); } break; From 391e2a5120039f9531d3d1ce8eecb7f0432f6130 Mon Sep 17 00:00:00 2001 From: Tyler Erickson Date: Mon, 3 Feb 2020 19:20:12 -0800 Subject: [PATCH 07/28] Removed 'where' clause from merge_sort_proc --- core/sort/sort.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From 0b299cb8b43b1382d81c8e1a2df14ca96c5921b2 Mon Sep 17 00:00:00 2001 From: Oskar Nordquist Date: Wed, 5 Feb 2020 20:48:18 +0100 Subject: [PATCH 08/28] Fix https://github.com/odin-lang/Odin/issues/555 --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 317e8f577..338bb3f5f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1504,11 +1504,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 = ""; From 0c09cb9c12d48f1cbce1f8f90cc1b3428d3d5de5 Mon Sep 17 00:00:00 2001 From: vassvik Date: Thu, 6 Feb 2020 13:35:55 +0100 Subject: [PATCH 09/28] Fix math.acos and math.asin, add f64 overloads. --- core/math/math.odin | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) 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 { From 6d67567453951c2cf00f17c5ed409d8e50aded45 Mon Sep 17 00:00:00 2001 From: vassvik Date: Sun, 9 Feb 2020 18:47:02 +0100 Subject: [PATCH 10/28] Fix linalg.matrix_mul_vector. Incorrect index. --- core/math/linalg/general.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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.. Date: Mon, 10 Feb 2020 01:26:04 +0100 Subject: [PATCH 11/28] Enhance logger interface with 'f' and not 'f' variants, also move level detection out --- core/log/file_console_logger.odin | 9 ++------ core/log/log.odin | 35 ++++++++++++++++++++++--------- core/runtime/core.odin | 9 ++++---- 3 files changed, 32 insertions(+), 21 deletions(-) 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/runtime/core.odin b/core/runtime/core.odin index 4ffd716ef..d459a27b6 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -276,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 { @@ -434,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}; } From 9821b2be251bf4ca0fcc9f0eacfb50395b49fb5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Tue, 11 Feb 2020 21:52:10 +0100 Subject: [PATCH 12/28] Create dockerimage.yml --- .github/workflows/dockerimage.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/dockerimage.yml diff --git a/.github/workflows/dockerimage.yml b/.github/workflows/dockerimage.yml new file mode 100644 index 000000000..01f7c15bd --- /dev/null +++ b/.github/workflows/dockerimage.yml @@ -0,0 +1,19 @@ +name: Docker Image CI + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag vpbot:dev + - name: Docker Publish + uses: manusa/actions-publish-docker@v1.0.1 + with: + name: vpbot + tag: dev + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.GITHUB_TOKEN }} + registry: docker.pkg.github.com From 0b7711684bae15ac5bfe3ac78230e9d6e9057673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Tue, 11 Feb 2020 21:53:45 +0100 Subject: [PATCH 13/28] Delete dockerimage.yml My bad --- .github/workflows/dockerimage.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/workflows/dockerimage.yml diff --git a/.github/workflows/dockerimage.yml b/.github/workflows/dockerimage.yml deleted file mode 100644 index 01f7c15bd..000000000 --- a/.github/workflows/dockerimage.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Docker Image CI - -on: [push] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Build the Docker image - run: docker build . --file Dockerfile --tag vpbot:dev - - name: Docker Publish - uses: manusa/actions-publish-docker@v1.0.1 - with: - name: vpbot - tag: dev - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.GITHUB_TOKEN }} - registry: docker.pkg.github.com From 1f0c1943daa21a4c06c21c8d3b8f704bb7f191f8 Mon Sep 17 00:00:00 2001 From: Tetralux Date: Wed, 12 Feb 2020 10:26:38 +0000 Subject: [PATCH 14/28] Fix #552. - Fix comparisons involving one or more empty string. - Fix comparisons against two strings of different lengths. --- core/mem/mem.odin | 12 +++++++++++- core/strings/strings.odin | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) 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/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); } From 81b00c7a3e26b5faf8f149d45c1e63ed431ab2c6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 Feb 2020 10:11:43 +0000 Subject: [PATCH 15/28] Fix #563 --- src/ir.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 2f4d491e7..f28179472 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7376,10 +7376,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); From 85e331d5e21ed01feb03efc02cf6517fa9c3a20e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 Feb 2020 10:13:42 +0000 Subject: [PATCH 16/28] Fix #566 --- src/check_expr.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 52c1f38b7..722acaa24 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5942,7 +5942,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); From 8a6777514985793bc607ea1b915675a566033730 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 Feb 2020 10:20:12 +0000 Subject: [PATCH 17/28] Fix #571 --- src/check_expr.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 722acaa24..d98b3d0d6 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; } From a72ac6f84140f3cb5f5ed790ec76182efa4f959a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 Feb 2020 10:26:49 +0000 Subject: [PATCH 18/28] Fix #572 --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index f28179472..b171e8589 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5714,7 +5714,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); } From 5073fcd39ee8693e8a8e781564f7c65184aec1b2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 Feb 2020 10:37:27 +0000 Subject: [PATCH 19/28] Improve error message on `using` with procedure parameters #568 --- src/check_decl.cpp | 2 +- src/check_expr.cpp | 7 +++++-- src/check_stmt.cpp | 8 +++++++- src/check_type.cpp | 4 ++-- 4 files changed, 15 insertions(+), 6 deletions(-) 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 d98b3d0d6..069605035 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6300,7 +6300,7 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize } -bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array *clauses, bool print_err) { +bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Array *clauses, bool print_err) { if (clauses != nullptr) { for_array(i, *clauses) { Ast *clause = (*clauses)[i]; @@ -6308,9 +6308,11 @@ bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array *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) { @@ -6352,6 +6354,7 @@ bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array *cla } } + if (call_expr) error(call_expr, "at caller location"); } return false; } @@ -6617,7 +6620,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; } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c365dca84..bef1919e4 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); diff --git a/src/check_type.cpp b/src/check_type.cpp index f21ffc956..97b9985c8 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, Arraywhere_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); } From 15f5c8537942e85d25819c7799991fab73b2564a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 Feb 2020 10:55:09 +0000 Subject: [PATCH 20/28] Fix comparison against nil for union #maybe pointers --- src/ir.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index b171e8589..e13530aca 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4405,6 +4405,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); From 4d7270cec982530431ab7759bc88b940ef3d88f7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 26 Feb 2020 12:55:56 +0000 Subject: [PATCH 21/28] Fix __dynamic_array_reserve to allow for zero sized elements --- core/runtime/core.odin | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index d459a27b6..d5a66e001 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -1141,11 +1141,9 @@ __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; + return new_data != nil || elem_size == 0; } __dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool { From 8f42958ba32444d83117a63b400baed072ad48b7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 26 Feb 2020 12:58:22 +0000 Subject: [PATCH 22/28] Fix __dynamic_array_reserve --- core/runtime/core.odin | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index d5a66e001..75ee84908 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -1141,9 +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); - array.data = new_data; - array.cap = cap; - return new_data != nil || elem_size == 0; + 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 { From 1596bca92d4d8b3457cbfacec24e2a2129bba40e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 26 Feb 2020 22:29:12 +0000 Subject: [PATCH 23/28] Add `intrinsics.cpu_relax` --- src/check_expr.cpp | 4 ++++ src/checker_builtin_procs.hpp | 4 ++++ src/ir.cpp | 12 ++++++++++++ src/ir_print.cpp | 12 ++++++++++++ 4 files changed, 32 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 069605035..b8fe76f6d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5280,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: 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/ir.cpp b/src/ir.cpp index e13530aca..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 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 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; @@ -6914,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: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a3f28c59f..bcbdca615 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1483,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) { From 3d74c2f6c0797345c2bdfae77c619057227e8181 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 26 Feb 2020 22:53:40 +0000 Subject: [PATCH 24/28] Add `proc(#const x: Type)` to enforce a constant parameter (but not polymorphic) to a procedure --- src/check_expr.cpp | 18 ++++++++++++++++++ src/check_type.cpp | 5 ++++- src/entity.cpp | 1 + src/parser.cpp | 8 ++++++++ src/parser.hpp | 3 ++- 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b8fe76f6d..1a01eef31 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5991,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)); @@ -6246,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; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 97b9985c8..6194951c9 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1722,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/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/parser.cpp b/src/parser.cpp index 2e2a1b97e..f89b5676b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2998,6 +2998,7 @@ enum FieldPrefixKind { FieldPrefix_Invalid = 0, FieldPrefix_using, + FieldPrefix_const, FieldPrefix_no_alias, FieldPrefix_c_var_arg, FieldPrefix_auto_cast, @@ -3024,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; } @@ -3036,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); @@ -3053,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; @@ -3066,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 cdfd4eba1..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, }; From 92e1c71dd6c43e28c2e0a2ffbcb7262363ed5c6b Mon Sep 17 00:00:00 2001 From: Tetralux Date: Fri, 28 Feb 2020 14:52:16 +0000 Subject: [PATCH 25/28] Fix encoding/base64 encoding null bytes incorrectly Fixes #574. Thanks @jroatch! --- core/encoding/base64/base64.odin | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 +} From ce20604e3c21062d5c836c5ace76c08fdeb447e3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 29 Feb 2020 09:35:41 +0000 Subject: [PATCH 26/28] Fix #578 --- src/check_stmt.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index bef1919e4..4b250c6a6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1108,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)); From 3f63e1219821813e4c7f975d58b27835215b1b65 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 2 Mar 2020 18:54:56 +0000 Subject: [PATCH 27/28] Add -subsystem:console and -subsystem:windows flags for windows --- src/build_settings.cpp | 1 + src/main.cpp | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 9 deletions(-) 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/main.cpp b/src/main.cpp index 338bb3f5f..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 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 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 @@ -1376,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\"", @@ -1392,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 ); } From 2fe0eaf2adf952867d4ce4fba53b4b3ac75e1ba5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 4 Mar 2020 13:10:39 +0000 Subject: [PATCH 28/28] Fix formatting --- core/runtime/core.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 75ee84908..27d40f4a3 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -277,9 +277,9 @@ Logger_Proc :: #type proc(data: rawptr, level: Logger_Level, text: string, optio Logger :: struct { procedure: Logger_Proc, - data: rawptr, + data: rawptr, lowest_level: Logger_Level, - options: Logger_Options, + options: Logger_Options, } Context :: struct {