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.. 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 c3aa76304..e94c6c881 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_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/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}; } 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/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 6d049747f..cf9e42810 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; } @@ -5942,7 +5944,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); @@ -6296,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]; @@ -6304,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) { @@ -6348,6 +6354,7 @@ bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array *cla } } + if (call_expr) error(call_expr, "at caller location"); } return false; } @@ -6613,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); } diff --git a/src/ir.cpp b/src/ir.cpp index 8171d774d..982576a2b 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); } @@ -7348,10 +7348,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); diff --git a/src/main.cpp b/src/main.cpp index 119e6fc62..e814f7e0d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1529,11 +1529,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 = "";