mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-06 04:57:55 +00:00
Merged with master
This commit is contained in:
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.odin linguist-language=Odin
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
|
||||
IS_NUMERIC(E) {
|
||||
for i in 0..<I {
|
||||
for j in 0..<J {
|
||||
c[i] += a[i][j] * b[i];
|
||||
c[j] += a[i][j] * b[i];
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
101
core/os/os.odin
101
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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,11 +261,11 @@ foreign libc {
|
||||
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
|
||||
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
|
||||
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: int, mode: int) -> Handle ---;
|
||||
@(link_name="close") _unix_close :: proc(fd: Handle) -> int ---;
|
||||
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
|
||||
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
|
||||
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 ---;
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
|
||||
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---;
|
||||
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
|
||||
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
|
||||
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---;
|
||||
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
|
||||
@(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---;
|
||||
@(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---;
|
||||
@@ -273,15 +275,17 @@ foreign libc {
|
||||
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---;
|
||||
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---;
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---;
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
|
||||
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---;
|
||||
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---;
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: int) -> ! ---;
|
||||
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---;
|
||||
}
|
||||
foreign dl {
|
||||
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---;
|
||||
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---;
|
||||
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
|
||||
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---;
|
||||
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---;
|
||||
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
|
||||
}
|
||||
|
||||
@@ -295,7 +299,7 @@ get_last_error :: proc() -> int {
|
||||
|
||||
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
||||
cstr := strings.clone_to_cstring(path);
|
||||
handle := _unix_open(cstr, flags, mode);
|
||||
handle := _unix_open(cstr, c.int(flags), c.int(mode));
|
||||
delete(cstr);
|
||||
if handle == -1 {
|
||||
return INVALID_HANDLE, Errno(get_last_error());
|
||||
@@ -312,26 +316,26 @@ close :: proc(fd: Handle) -> Errno {
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
bytes_read := _unix_read(fd, &data[0], len(data));
|
||||
bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)));
|
||||
if bytes_read == -1 {
|
||||
return -1, Errno(get_last_error());
|
||||
}
|
||||
return bytes_read, ERROR_NONE;
|
||||
return int(bytes_read), ERROR_NONE;
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
}
|
||||
bytes_written := _unix_write(fd, &data[0], len(data));
|
||||
bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)));
|
||||
if bytes_written == -1 {
|
||||
return -1, Errno(get_last_error());
|
||||
}
|
||||
return bytes_written, ERROR_NONE;
|
||||
return int(bytes_written), ERROR_NONE;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
res := _unix_seek(fd, offset, i32(whence));
|
||||
res := _unix_seek(fd, offset, c.int(whence));
|
||||
if res == -1 {
|
||||
return -1, Errno(get_last_error());
|
||||
}
|
||||
@@ -397,7 +401,7 @@ fstat :: inline proc(fd: Handle) -> (Stat, Errno) {
|
||||
access :: inline proc(path: string, mask: int) -> (bool, Errno) {
|
||||
cstr := strings.clone_to_cstring(path);
|
||||
defer delete(cstr);
|
||||
result := _unix_access(cstr, mask);
|
||||
result := _unix_access(cstr, c.int(mask));
|
||||
if result == -1 {
|
||||
return false, Errno(get_last_error());
|
||||
}
|
||||
@@ -406,11 +410,11 @@ access :: inline proc(path: string, mask: int) -> (bool, Errno) {
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0);
|
||||
return _unix_calloc(1, size);
|
||||
return _unix_calloc(1, c.size_t(size));
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return _unix_realloc(ptr, new_size);
|
||||
return _unix_realloc(ptr, c.size_t(new_size));
|
||||
}
|
||||
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
@@ -427,8 +431,35 @@ getenv :: proc(name: string) -> (string, bool) {
|
||||
return string(cstr), true;
|
||||
}
|
||||
|
||||
get_current_directory :: proc() -> string {
|
||||
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
|
||||
// an authoritative value for it across all systems.
|
||||
// The largest value I could find was 4096, so might as well use the page size.
|
||||
page_size := get_page_size();
|
||||
buf := make([dynamic]u8, page_size);
|
||||
for {
|
||||
cwd := _unix_getcwd(cstring(#no_bounds_check &buf[0]), c.size_t(len(buf)));
|
||||
if cwd != nil {
|
||||
return string(cwd);
|
||||
}
|
||||
if Errno(get_last_error()) != ERANGE {
|
||||
return "";
|
||||
}
|
||||
resize(&buf, len(buf)+page_size);
|
||||
}
|
||||
unreachable();
|
||||
return "";
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator);
|
||||
res := _unix_chdir(cstr);
|
||||
if res == -1 do return Errno(get_last_error());
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
exit :: proc(code: int) -> ! {
|
||||
_unix_exit(code);
|
||||
_unix_exit(c.int(code));
|
||||
}
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
@@ -438,7 +469,7 @@ current_thread_id :: proc "contextless" () -> int {
|
||||
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
|
||||
cstr := strings.clone_to_cstring(filename);
|
||||
defer delete(cstr);
|
||||
handle := _unix_dlopen(cstr, flags);
|
||||
handle := _unix_dlopen(cstr, c.int(flags));
|
||||
return handle;
|
||||
}
|
||||
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ Type_Info_Union :: struct {
|
||||
tag_type: ^Type_Info,
|
||||
custom_align: bool,
|
||||
no_nil: bool,
|
||||
maybe: bool,
|
||||
};
|
||||
Type_Info_Enum :: struct {
|
||||
base: ^Type_Info,
|
||||
@@ -275,9 +276,10 @@ Logger_Options :: bit_set[Logger_Option];
|
||||
Logger_Proc :: #type proc(data: rawptr, level: Logger_Level, text: string, options: Logger_Options, location := #caller_location);
|
||||
|
||||
Logger :: struct {
|
||||
procedure: Logger_Proc,
|
||||
data: rawptr,
|
||||
options: Logger_Options,
|
||||
procedure: Logger_Proc,
|
||||
data: rawptr,
|
||||
lowest_level: Logger_Level,
|
||||
options: Logger_Options,
|
||||
}
|
||||
|
||||
Context :: struct {
|
||||
@@ -433,7 +435,7 @@ default_logger_proc :: proc(data: rawptr, level: Logger_Level, text: string, opt
|
||||
}
|
||||
|
||||
default_logger :: proc() -> Logger {
|
||||
return Logger{default_logger_proc, nil, nil};
|
||||
return Logger{default_logger_proc, nil, Logger_Level.Debug, nil};
|
||||
}
|
||||
|
||||
|
||||
@@ -1131,7 +1133,7 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
|
||||
array.allocator = context.allocator;
|
||||
}
|
||||
assert(array.allocator.procedure != nil);
|
||||
|
||||
|
||||
if cap <= array.cap do return true;
|
||||
|
||||
old_size := array.cap * elem_size;
|
||||
@@ -1139,11 +1141,12 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
|
||||
allocator := array.allocator;
|
||||
|
||||
new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc);
|
||||
if new_data == nil do return false;
|
||||
|
||||
array.data = new_data;
|
||||
array.cap = cap;
|
||||
return true;
|
||||
if new_data != nil || elem_size == 0 {
|
||||
array.data = new_data;
|
||||
array.cap = cap;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 ---;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ struct BuildContext {
|
||||
bool use_lld;
|
||||
bool vet;
|
||||
bool cross_compiling;
|
||||
bool use_subsystem_windows;
|
||||
|
||||
QueryDataSetSettings query_data_set_settings;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2260,6 +2260,8 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
|
||||
x->mode = Addressing_Value;
|
||||
} else if (is_type_slice(type) && is_type_string(x->type)) {
|
||||
x->mode = Addressing_Value;
|
||||
} else if (is_type_union(type)) {
|
||||
x->mode = Addressing_Value;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -5278,6 +5280,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
break;
|
||||
}
|
||||
|
||||
case BuiltinProc_cpu_relax:
|
||||
operand->mode = Addressing_NoValue;
|
||||
break;
|
||||
|
||||
case BuiltinProc_atomic_fence:
|
||||
case BuiltinProc_atomic_fence_acq:
|
||||
case BuiltinProc_atomic_fence_rel:
|
||||
@@ -5942,7 +5948,9 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
||||
Entity *e = sig_params[operand_index];
|
||||
Type *t = e->type;
|
||||
Operand o = operands[operand_index];
|
||||
call->viral_state_flags |= o.expr->viral_state_flags;
|
||||
if (o.expr != nullptr) {
|
||||
call->viral_state_flags |= o.expr->viral_state_flags;
|
||||
}
|
||||
|
||||
if (e->kind == Entity_TypeName) {
|
||||
// GB_ASSERT(!variadic);
|
||||
@@ -5983,6 +5991,15 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
||||
}
|
||||
score += s;
|
||||
|
||||
if (e->flags & EntityFlag_ConstInput) {
|
||||
if (o.mode != Addressing_Constant) {
|
||||
if (show_error) {
|
||||
error(o.expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string));
|
||||
}
|
||||
err = CallArgumentError_NoneConstantParameter;
|
||||
}
|
||||
}
|
||||
|
||||
if (o.mode == Addressing_Type && is_type_typeid(e->type)) {
|
||||
add_type_info_type(c, o.type);
|
||||
add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type));
|
||||
@@ -6238,6 +6255,15 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
|
||||
}
|
||||
err = CallArgumentError_WrongTypes;
|
||||
}
|
||||
|
||||
if (e->flags & EntityFlag_ConstInput) {
|
||||
if (o->mode != Addressing_Constant) {
|
||||
if (show_error) {
|
||||
error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string));
|
||||
}
|
||||
err = CallArgumentError_NoneConstantParameter;
|
||||
}
|
||||
}
|
||||
}
|
||||
score += s;
|
||||
}
|
||||
@@ -6296,7 +6322,7 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize
|
||||
}
|
||||
|
||||
|
||||
bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array<Ast *> *clauses, bool print_err) {
|
||||
bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Array<Ast *> *clauses, bool print_err) {
|
||||
if (clauses != nullptr) {
|
||||
for_array(i, *clauses) {
|
||||
Ast *clause = (*clauses)[i];
|
||||
@@ -6304,9 +6330,11 @@ bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array<Ast *> *cla
|
||||
check_expr(ctx, &o, clause);
|
||||
if (o.mode != Addressing_Constant) {
|
||||
if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation");
|
||||
if (print_err && call_expr) error(call_expr, "at caller location");
|
||||
return false;
|
||||
} else if (o.value.kind != ExactValue_Bool) {
|
||||
if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation");
|
||||
if (print_err && call_expr) error(call_expr, "at caller location");
|
||||
return false;
|
||||
} else if (!o.value.value_bool) {
|
||||
if (print_err) {
|
||||
@@ -6348,6 +6376,7 @@ bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array<Ast *> *cla
|
||||
}
|
||||
}
|
||||
|
||||
if (call_expr) error(call_expr, "at caller location");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -6613,7 +6642,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
||||
ctx.curr_proc_sig = e->type;
|
||||
|
||||
GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
|
||||
if (!evaluate_where_clauses(&ctx, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) {
|
||||
if (!evaluate_where_clauses(&ctx, operand->expr, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -8572,8 +8601,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
o->expr = node;
|
||||
return kind;
|
||||
}
|
||||
Type *t = check_type(c, ta->type);
|
||||
|
||||
if (o->mode == Addressing_Constant) {
|
||||
gbString expr_str = expr_to_string(o->expr);
|
||||
error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str);
|
||||
@@ -8594,54 +8621,80 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
|
||||
bool src_is_ptr = is_type_pointer(o->type);
|
||||
Type *src = type_deref(o->type);
|
||||
Type *dst = t;
|
||||
Type *bsrc = base_type(src);
|
||||
Type *bdst = base_type(dst);
|
||||
|
||||
|
||||
if (is_type_union(src)) {
|
||||
bool ok = false;
|
||||
for_array(i, bsrc->Union.variants) {
|
||||
Type *vt = bsrc->Union.variants[i];
|
||||
if (are_types_identical(vt, dst)) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) {
|
||||
if (!is_type_union(src)) {
|
||||
gbString str = type_to_string(o->type);
|
||||
error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %s", str);
|
||||
gb_string_free(str);
|
||||
o->mode = Addressing_Invalid;
|
||||
o->expr = node;
|
||||
return kind;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
gbString expr_str = expr_to_string(o->expr);
|
||||
gbString dst_type_str = type_to_string(t);
|
||||
defer (gb_string_free(expr_str));
|
||||
defer (gb_string_free(dst_type_str));
|
||||
if (bsrc->Union.variants.count == 0) {
|
||||
error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
|
||||
} else {
|
||||
error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
|
||||
}
|
||||
if (bsrc->Union.variants.count != 1) {
|
||||
error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count);
|
||||
o->mode = Addressing_Invalid;
|
||||
o->expr = node;
|
||||
return kind;
|
||||
}
|
||||
|
||||
add_type_info_type(c, o->type);
|
||||
add_type_info_type(c, t);
|
||||
add_type_info_type(c, bsrc->Union.variants[0]);
|
||||
|
||||
o->type = t;
|
||||
o->type = bsrc->Union.variants[0];
|
||||
o->mode = Addressing_OptionalOk;
|
||||
} else if (is_type_any(src)) {
|
||||
o->type = t;
|
||||
o->mode = Addressing_OptionalOk;
|
||||
|
||||
add_type_info_type(c, o->type);
|
||||
add_type_info_type(c, t);
|
||||
} else {
|
||||
gbString str = type_to_string(o->type);
|
||||
error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
|
||||
gb_string_free(str);
|
||||
o->mode = Addressing_Invalid;
|
||||
o->expr = node;
|
||||
return kind;
|
||||
Type *t = check_type(c, ta->type);
|
||||
Type *dst = t;
|
||||
Type *bdst = base_type(dst);
|
||||
|
||||
|
||||
if (is_type_union(src)) {
|
||||
bool ok = false;
|
||||
for_array(i, bsrc->Union.variants) {
|
||||
Type *vt = bsrc->Union.variants[i];
|
||||
if (are_types_identical(vt, dst)) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
gbString expr_str = expr_to_string(o->expr);
|
||||
gbString dst_type_str = type_to_string(t);
|
||||
defer (gb_string_free(expr_str));
|
||||
defer (gb_string_free(dst_type_str));
|
||||
if (bsrc->Union.variants.count == 0) {
|
||||
error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
|
||||
} else {
|
||||
error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
|
||||
}
|
||||
o->mode = Addressing_Invalid;
|
||||
o->expr = node;
|
||||
return kind;
|
||||
}
|
||||
|
||||
add_type_info_type(c, o->type);
|
||||
add_type_info_type(c, t);
|
||||
|
||||
o->type = t;
|
||||
o->mode = Addressing_OptionalOk;
|
||||
} else if (is_type_any(src)) {
|
||||
o->type = t;
|
||||
o->mode = Addressing_OptionalOk;
|
||||
|
||||
add_type_info_type(c, o->type);
|
||||
add_type_info_type(c, t);
|
||||
} else {
|
||||
gbString str = type_to_string(o->type);
|
||||
error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
|
||||
gb_string_free(str);
|
||||
o->mode = Addressing_Invalid;
|
||||
o->expr = node;
|
||||
return kind;
|
||||
}
|
||||
}
|
||||
|
||||
add_package_dependency(c, "runtime", "type_assertion_check");
|
||||
|
||||
@@ -314,7 +314,11 @@ Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs)
|
||||
|
||||
gbString str = expr_to_string(lhs->expr);
|
||||
if (e != nullptr && e->flags & EntityFlag_Param) {
|
||||
error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str);
|
||||
if (e->flags & EntityFlag_Using) {
|
||||
error(lhs->expr, "Cannot assign to '%s' which is from a 'using' procedure parameter", str);
|
||||
} else {
|
||||
error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str);
|
||||
}
|
||||
} else {
|
||||
error(lhs->expr, "Cannot assign to '%s'", str);
|
||||
}
|
||||
@@ -497,6 +501,8 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
|
||||
Entity *f = found->elements.entries[i].value;
|
||||
if (f->kind == Entity_Variable) {
|
||||
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, expr);
|
||||
if (e->flags & EntityFlag_Value) uvar->flags |= EntityFlag_Value;
|
||||
if (e->flags & EntityFlag_Param) uvar->flags |= EntityFlag_Param;
|
||||
Entity *prev = scope_insert(ctx->scope, uvar);
|
||||
if (prev != nullptr) {
|
||||
gbString expr_str = expr_to_string(expr);
|
||||
@@ -1102,6 +1108,12 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
||||
if (type_expr != nullptr) { // Otherwise it's a default expression
|
||||
Operand y = {};
|
||||
check_expr_or_type(ctx, &y, type_expr);
|
||||
if (y.mode != Addressing_Type) {
|
||||
gbString str = expr_to_string(type_expr);
|
||||
error(type_expr, "Expected a type as a case, got %s", str);
|
||||
gb_string_free(str);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (switch_kind == TypeSwitch_Union) {
|
||||
GB_ASSERT(is_type_union(bt));
|
||||
|
||||
@@ -527,7 +527,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
|
||||
if (st->where_clauses.count > 0 && st->polymorphic_params == nullptr) {
|
||||
error(st->where_clauses[0], "'where' clauses can only be used on structures with polymorphic parameters");
|
||||
} else {
|
||||
bool where_clause_ok = evaluate_where_clauses(ctx, ctx->scope, &st->where_clauses, true);
|
||||
bool where_clause_ok = evaluate_where_clauses(ctx, node, ctx->scope, &st->where_clauses, true);
|
||||
}
|
||||
check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context);
|
||||
}
|
||||
@@ -714,7 +714,7 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
|
||||
if (ut->where_clauses.count > 0 && ut->polymorphic_params == nullptr) {
|
||||
error(ut->where_clauses[0], "'where' clauses can only be used on unions with polymorphic parameters");
|
||||
} else {
|
||||
bool where_clause_ok = evaluate_where_clauses(ctx, ctx->scope, &ut->where_clauses, true);
|
||||
bool where_clause_ok = evaluate_where_clauses(ctx, node, ctx->scope, &ut->where_clauses, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -748,11 +748,17 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
|
||||
|
||||
union_type->Union.variants = variants;
|
||||
union_type->Union.no_nil = ut->no_nil;
|
||||
union_type->Union.maybe = ut->maybe;
|
||||
if (union_type->Union.no_nil) {
|
||||
if (variants.count < 2) {
|
||||
error(ut->align, "A union with #no_nil must have at least 2 variants");
|
||||
}
|
||||
}
|
||||
if (union_type->Union.maybe) {
|
||||
if (variants.count != 1) {
|
||||
error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
|
||||
}
|
||||
}
|
||||
|
||||
if (ut->align != nullptr) {
|
||||
i64 custom_align = 1;
|
||||
@@ -1716,8 +1722,11 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
|
||||
if (p->flags&FieldFlag_auto_cast) {
|
||||
param->flags |= EntityFlag_AutoCast;
|
||||
}
|
||||
param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
|
||||
if (p->flags&FieldFlag_const) {
|
||||
param->flags |= EntityFlag_ConstInput;
|
||||
}
|
||||
|
||||
param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
|
||||
add_entity(ctx->checker, scope, name, param);
|
||||
if (is_using) {
|
||||
add_entity_use(ctx, name, param);
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
85
src/ir.cpp
85
src/ir.cpp
@@ -197,6 +197,7 @@ gbAllocator ir_allocator(void) {
|
||||
IR_INSTR_KIND(ZeroInit, struct { irValue *address; }) \
|
||||
IR_INSTR_KIND(Store, struct { irValue *address, *value; bool is_volatile; }) \
|
||||
IR_INSTR_KIND(Load, struct { Type *type; irValue *address; i64 custom_align; }) \
|
||||
IR_INSTR_KIND(InlineCode, struct { BuiltinProcId id; Array<irValue *> operands; }) \
|
||||
IR_INSTR_KIND(AtomicFence, struct { BuiltinProcId id; }) \
|
||||
IR_INSTR_KIND(AtomicStore, struct { \
|
||||
irValue *address, *value; \
|
||||
@@ -1063,6 +1064,14 @@ irValue *ir_instr_load(irProcedure *p, irValue *address) {
|
||||
return v;
|
||||
}
|
||||
|
||||
irValue *ir_instr_inline_code(irProcedure *p, BuiltinProcId id, Array<irValue *> operands) {
|
||||
irValue *v = ir_alloc_instr(p, irInstr_InlineCode);
|
||||
irInstr *i = &v->Instr;
|
||||
i->InlineCode.id = id;
|
||||
i->InlineCode.operands = operands;
|
||||
return v;
|
||||
}
|
||||
|
||||
irValue *ir_instr_atomic_fence(irProcedure *p, BuiltinProcId id) {
|
||||
irValue *v = ir_alloc_instr(p, irInstr_AtomicFence);
|
||||
irInstr *i = &v->Instr;
|
||||
@@ -1222,7 +1231,11 @@ irValue *ir_instr_union_tag_ptr(irProcedure *p, irValue *address) {
|
||||
|
||||
// i->UnionTagPtr.type = alloc_type_pointer(t_type_info_ptr);
|
||||
Type *u = type_deref(ir_type(address));
|
||||
if (is_type_union_maybe_pointer(u)) {
|
||||
GB_PANIC("union #maybe UnionTagPtr");
|
||||
}
|
||||
i->UnionTagPtr.type = alloc_type_pointer(union_tag_type(u));
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -1234,6 +1247,9 @@ irValue *ir_instr_union_tag_value(irProcedure *p, irValue *address) {
|
||||
if (address) address->uses += 1;
|
||||
|
||||
Type *u = type_deref(ir_type(address));
|
||||
if (is_type_union_maybe_pointer(u)) {
|
||||
GB_PANIC("union #maybe UnionTagValue");
|
||||
}
|
||||
i->UnionTagPtr.type = union_tag_type(u);
|
||||
return v;
|
||||
}
|
||||
@@ -4351,7 +4367,9 @@ irValue *ir_emit_union_tag_value(irProcedure *proc, irValue *u) {
|
||||
|
||||
irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue *x) {
|
||||
Type *t = ir_type(x);
|
||||
if (is_type_cstring(t)) {
|
||||
if (is_type_pointer(t)) {
|
||||
return ir_emit_comp(proc, op_kind, x, v_raw_nil);
|
||||
} else if (is_type_cstring(t)) {
|
||||
irValue *ptr = ir_emit_conv(proc, x, t_u8_ptr);
|
||||
return ir_emit_comp(proc, op_kind, ptr, v_raw_nil);
|
||||
} else if (is_type_any(t)) {
|
||||
@@ -4396,6 +4414,12 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue
|
||||
} else if (is_type_union(t)) {
|
||||
if (type_size_of(t) == 0) {
|
||||
return ir_emit_comp(proc, op_kind, v_zero, v_zero);
|
||||
} else if (is_type_union_maybe_pointer(t)) {
|
||||
Type *bt = base_type(t);
|
||||
irValue *ptr = ir_address_from_load_or_generate_local(proc, x);
|
||||
ptr = ir_emit_bitcast(proc, ptr, alloc_type_pointer(bt->Union.variants[0]));
|
||||
irValue *data = ir_emit_load(proc, ptr);
|
||||
return ir_emit_comp_against_nil(proc, op_kind, data);
|
||||
} else {
|
||||
irValue *tag = ir_emit_union_tag_value(proc, x);
|
||||
return ir_emit_comp(proc, op_kind, tag, v_zero);
|
||||
@@ -5232,8 +5256,12 @@ void ir_emit_store_union_variant(irProcedure *proc, irValue *parent, irValue *va
|
||||
|
||||
Type *t = type_deref(ir_type(parent));
|
||||
|
||||
irValue *tag_ptr = ir_emit_union_tag_ptr(proc, parent);
|
||||
ir_emit_store(proc, tag_ptr, ir_const_union_tag(t, variant_type));
|
||||
if (is_type_union_maybe_pointer(t)) {
|
||||
// No tag needed!
|
||||
} else {
|
||||
irValue *tag_ptr = ir_emit_union_tag_ptr(proc, parent);
|
||||
ir_emit_store(proc, tag_ptr, ir_const_union_tag(t, variant_type));
|
||||
}
|
||||
}
|
||||
|
||||
irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
|
||||
@@ -5701,7 +5729,7 @@ irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t) {
|
||||
}
|
||||
|
||||
// TODO(bill): Actually figure out what the conversion needs to be correctly 'cause LLVM
|
||||
return ir_emit_bitcast(proc, value, dst);
|
||||
return ir_emit_bitcast(proc, value, t);
|
||||
}
|
||||
|
||||
|
||||
@@ -5728,22 +5756,41 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, Token
|
||||
GB_ASSERT_MSG(is_type_union(src), "%s", type_to_string(src_type));
|
||||
Type *dst = tuple->Tuple.variables[0]->type;
|
||||
|
||||
|
||||
irValue *value_ = ir_address_from_load_or_generate_local(proc, value);
|
||||
irValue *tag = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, value_));
|
||||
irValue *dst_tag = ir_const_union_tag(src, dst);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
@@ -6876,6 +6923,9 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
|
||||
|
||||
|
||||
// "Intrinsics"
|
||||
case BuiltinProc_cpu_relax:
|
||||
return ir_emit(proc, ir_instr_inline_code(proc, id, {}));
|
||||
|
||||
case BuiltinProc_atomic_fence:
|
||||
case BuiltinProc_atomic_fence_acq:
|
||||
case BuiltinProc_atomic_fence_rel:
|
||||
@@ -7344,10 +7394,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
|
||||
GB_ASSERT(are_types_identical(ir_type(left), key_type));
|
||||
|
||||
Type *it = bit_set_to_int(rt);
|
||||
left = ir_emit_conv(proc, left, it);
|
||||
|
||||
irValue *lower = ir_value_constant(it, exact_value_i64(rt->BitSet.lower));
|
||||
irValue *key = ir_emit_arith(proc, Token_Sub, left, lower, ir_type(left));
|
||||
irValue *bit = ir_emit_arith(proc, Token_Shl, v_one, key, ir_type(left));
|
||||
irValue *key = ir_emit_arith(proc, Token_Sub, left, lower, it);
|
||||
irValue *bit = ir_emit_arith(proc, Token_Shl, v_one, key, it);
|
||||
bit = ir_emit_conv(proc, bit, it);
|
||||
|
||||
irValue *old_value = ir_emit_bitcast(proc, right, it);
|
||||
@@ -11465,6 +11516,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
||||
irValue *tag_type_ptr = ir_emit_struct_ep(proc, tag, 2);
|
||||
irValue *custom_align_ptr = ir_emit_struct_ep(proc, tag, 3);
|
||||
irValue *no_nil_ptr = ir_emit_struct_ep(proc, tag, 4);
|
||||
irValue *maybe_ptr = ir_emit_struct_ep(proc, tag, 5);
|
||||
|
||||
isize variant_count = gb_max(0, t->Union.variants.count);
|
||||
irValue *memory_types = ir_type_info_member_types_offset(proc, variant_count);
|
||||
@@ -11494,6 +11546,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
|
||||
ir_emit_store(proc, custom_align_ptr, is_custom_align);
|
||||
|
||||
ir_emit_store(proc, no_nil_ptr, ir_const_bool(t->Union.no_nil));
|
||||
ir_emit_store(proc, maybe_ptr, ir_const_bool(t->Union.maybe));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -508,13 +508,25 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
|
||||
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
|
||||
// LLVM takes the first element's alignment as the entire alignment (like C)
|
||||
i64 align = type_align_of(t);
|
||||
|
||||
if (is_type_union_maybe_pointer_original_alignment(t)) {
|
||||
ir_write_byte(f, '{');
|
||||
ir_print_type(f, m, t->Union.variants[0]);
|
||||
ir_write_byte(f, '}');
|
||||
return;
|
||||
}
|
||||
|
||||
i64 block_size = t->Union.variant_block_size;
|
||||
|
||||
ir_write_byte(f, '{');
|
||||
ir_print_alignment_prefix_hack(f, align);
|
||||
ir_fprintf(f, ", [%lld x i8], ", block_size);
|
||||
// ir_print_type(f, m, t_type_info_ptr);
|
||||
ir_print_type(f, m, union_tag_type(t));
|
||||
if (is_type_union_maybe_pointer(t)) {
|
||||
ir_fprintf(f, ", ");
|
||||
ir_print_type(f, m, t->Union.variants[0]);
|
||||
} else {
|
||||
ir_fprintf(f, ", [%lld x i8], ", block_size);
|
||||
ir_print_type(f, m, union_tag_type(t));
|
||||
}
|
||||
ir_write_byte(f, '}');
|
||||
}
|
||||
return;
|
||||
@@ -1471,6 +1483,18 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
break;
|
||||
}
|
||||
|
||||
case irInstr_InlineCode:
|
||||
{
|
||||
switch (instr->InlineCode.id) {
|
||||
case BuiltinProc_cpu_relax:
|
||||
ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
|
||||
break;
|
||||
default: GB_PANIC("Unknown inline code %d", instr->InlineCode.id); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case irInstr_AtomicFence:
|
||||
ir_write_str_lit(f, "fence ");
|
||||
switch (instr->AtomicFence.id) {
|
||||
@@ -1850,6 +1874,12 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
|
||||
case irInstr_UnionTagPtr: {
|
||||
Type *et = ir_type(instr->UnionTagPtr.address);
|
||||
|
||||
Type *ut = type_deref(et);
|
||||
if (is_type_union_maybe_pointer(ut)) {
|
||||
GB_PANIC("union #maybe UnionTagPtr");
|
||||
}
|
||||
|
||||
ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
|
||||
Type *t = base_type(type_deref(et));
|
||||
GB_ASSERT(is_type_union(t));
|
||||
@@ -1869,10 +1899,16 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
|
||||
case irInstr_UnionTagValue: {
|
||||
Type *et = ir_type(instr->UnionTagValue.address);
|
||||
ir_fprintf(f, "%%%d = extractvalue ", value->index);
|
||||
Type *t = base_type(et);
|
||||
|
||||
if (is_type_union_maybe_pointer(t)) {
|
||||
GB_PANIC("union #maybe UnionTagValue");
|
||||
}
|
||||
|
||||
ir_fprintf(f, "%%%d = extractvalue ", value->index);
|
||||
GB_ASSERT(is_type_union(t));
|
||||
|
||||
|
||||
ir_print_type(f, m, et);
|
||||
ir_write_byte(f, ' ');
|
||||
ir_print_value(f, m, instr->UnionTagValue.address, et);
|
||||
|
||||
46
src/main.cpp
46
src/main.cpp
@@ -243,6 +243,7 @@ enum BuildFlagKind {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
BuildFlag_ResourceFile,
|
||||
BuildFlag_WindowsPdbName,
|
||||
BuildFlag_Subsystem,
|
||||
#endif
|
||||
|
||||
BuildFlag_COUNT,
|
||||
@@ -331,8 +332,9 @@ bool parse_build_flags(Array<String> args) {
|
||||
add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None);
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String);
|
||||
add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String);
|
||||
add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String);
|
||||
add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String);
|
||||
add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String);
|
||||
#endif
|
||||
|
||||
GB_ASSERT(args.count >= 3);
|
||||
@@ -764,7 +766,19 @@ bool parse_build_flags(Array<String> args) {
|
||||
bad_flags = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BuildFlag_Subsystem: {
|
||||
GB_ASSERT(value.kind == ExactValue_String);
|
||||
String subsystem = value.value_string;
|
||||
if (str_eq_ignore_case(subsystem, str_lit("console"))) {
|
||||
build_context.use_subsystem_windows = false;
|
||||
} else if (str_eq_ignore_case(subsystem, str_lit("windows"))) {
|
||||
build_context.use_subsystem_windows = true;
|
||||
} else {
|
||||
gb_printf_err("Invalid -subsystem string, got %.*s, expected either 'console' or 'windows'\n", LIT(subsystem));
|
||||
bad_flags = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@@ -1180,6 +1194,10 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (init_filename == "-help") {
|
||||
build_context.show_help = true;
|
||||
}
|
||||
|
||||
build_context.command = command;
|
||||
|
||||
if (!parse_build_flags(args)) {
|
||||
@@ -1372,8 +1390,8 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
}
|
||||
|
||||
|
||||
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
|
||||
if (!build_context.use_lld) { // msvc
|
||||
|
||||
if (build_context.has_resource) {
|
||||
exit_code = system_exec_command_line_app("msvc-link",
|
||||
"\"%.*src.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
|
||||
@@ -1388,36 +1406,42 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
|
||||
exit_code = system_exec_command_line_app("msvc-link",
|
||||
"\"%.*slink.exe\" \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||
" %.*s "
|
||||
" %s "
|
||||
"",
|
||||
LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), LIT(output_base), output_ext,
|
||||
link_settings, LIT(build_context.link_flags),
|
||||
link_settings,
|
||||
subsystem_str,
|
||||
LIT(build_context.link_flags),
|
||||
lib_str
|
||||
);
|
||||
} else {
|
||||
exit_code = system_exec_command_line_app("msvc-link",
|
||||
"\"%.*slink.exe\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||
" %.*s "
|
||||
" %s "
|
||||
"",
|
||||
LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), output_ext,
|
||||
link_settings, LIT(build_context.link_flags),
|
||||
link_settings,
|
||||
subsystem_str,
|
||||
LIT(build_context.link_flags),
|
||||
lib_str
|
||||
);
|
||||
}
|
||||
} else { // lld
|
||||
exit_code = system_exec_command_line_app("msvc-link",
|
||||
"\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||
" %.*s "
|
||||
" %s "
|
||||
"",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
LIT(output_base), LIT(output_base), output_ext,
|
||||
link_settings, LIT(build_context.link_flags),
|
||||
link_settings,
|
||||
subsystem_str,
|
||||
LIT(build_context.link_flags),
|
||||
lib_str
|
||||
);
|
||||
}
|
||||
@@ -1500,11 +1524,11 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
// Shared libraries are .dylib on MacOS and .so on Linux.
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
output_ext = STR_LIT(".dylib");
|
||||
link_settings = "-dylib -dynamic";
|
||||
#else
|
||||
output_ext = STR_LIT(".so");
|
||||
link_settings = "-shared";
|
||||
#endif
|
||||
|
||||
link_settings = "-shared";
|
||||
} else {
|
||||
// TODO: Do I need anything here?
|
||||
link_settings = "";
|
||||
|
||||
@@ -929,7 +929,7 @@ Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_c
|
||||
}
|
||||
|
||||
|
||||
Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil,
|
||||
Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
|
||||
Token where_token, Array<Ast *> const &where_clauses) {
|
||||
Ast *result = alloc_ast_node(f, Ast_UnionType);
|
||||
result->UnionType.token = token;
|
||||
@@ -937,6 +937,7 @@ Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymor
|
||||
result->UnionType.polymorphic_params = polymorphic_params;
|
||||
result->UnionType.align = align;
|
||||
result->UnionType.no_nil = no_nil;
|
||||
result->UnionType.maybe = maybe;
|
||||
result->UnionType.where_token = where_token;
|
||||
result->UnionType.where_clauses = where_clauses;
|
||||
return result;
|
||||
@@ -2091,6 +2092,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
Ast *polymorphic_params = nullptr;
|
||||
Ast *align = nullptr;
|
||||
bool no_nil = false;
|
||||
bool maybe = false;
|
||||
|
||||
CommentGroup *docs = f->lead_comment;
|
||||
Token start_token = f->curr_token;
|
||||
@@ -2118,10 +2120,19 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
|
||||
}
|
||||
no_nil = true;
|
||||
} else {
|
||||
} else if (tag.string == "maybe") {
|
||||
if (maybe) {
|
||||
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
|
||||
}
|
||||
maybe = true;
|
||||
}else {
|
||||
syntax_error(tag, "Invalid union tag '#%.*s'", LIT(tag.string));
|
||||
}
|
||||
}
|
||||
if (no_nil && maybe) {
|
||||
syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together");
|
||||
}
|
||||
|
||||
|
||||
Token where_token = {};
|
||||
Array<Ast *> where_clauses = {};
|
||||
@@ -2150,7 +2161,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
|
||||
Token close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, where_token, where_clauses);
|
||||
return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses);
|
||||
} break;
|
||||
|
||||
case Token_enum: {
|
||||
@@ -2350,6 +2361,12 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
|
||||
operand = ast_type_assertion(f, operand, token, type);
|
||||
} break;
|
||||
|
||||
case Token_Question: {
|
||||
Token question = expect_token(f, Token_Question);
|
||||
Ast *type = ast_unary_expr(f, question, nullptr);
|
||||
operand = ast_type_assertion(f, operand, token, type);
|
||||
} break;
|
||||
|
||||
default:
|
||||
syntax_error(f->curr_token, "Expected a selector");
|
||||
advance_token(f);
|
||||
@@ -2981,6 +2998,7 @@ enum FieldPrefixKind {
|
||||
FieldPrefix_Invalid = 0,
|
||||
|
||||
FieldPrefix_using,
|
||||
FieldPrefix_const,
|
||||
FieldPrefix_no_alias,
|
||||
FieldPrefix_c_var_arg,
|
||||
FieldPrefix_auto_cast,
|
||||
@@ -3007,6 +3025,9 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
|
||||
return FieldPrefix_c_var_arg;
|
||||
}
|
||||
break;
|
||||
|
||||
case Token_const:
|
||||
return FieldPrefix_const;
|
||||
}
|
||||
return FieldPrefix_Unknown;
|
||||
}
|
||||
@@ -3019,6 +3040,7 @@ u32 parse_field_prefixes(AstFile *f) {
|
||||
i32 no_alias_count = 0;
|
||||
i32 c_vararg_count = 0;
|
||||
i32 auto_cast_count = 0;
|
||||
i32 const_count = 0;
|
||||
|
||||
for (;;) {
|
||||
FieldPrefixKind kind = is_token_field_prefix(f);
|
||||
@@ -3036,12 +3058,14 @@ u32 parse_field_prefixes(AstFile *f) {
|
||||
case FieldPrefix_no_alias: no_alias_count += 1; advance_token(f); break;
|
||||
case FieldPrefix_c_var_arg: c_vararg_count += 1; advance_token(f); break;
|
||||
case FieldPrefix_auto_cast: auto_cast_count += 1; advance_token(f); break;
|
||||
case FieldPrefix_const: const_count += 1; advance_token(f); break;
|
||||
}
|
||||
}
|
||||
if (using_count > 1) syntax_error(f->curr_token, "Multiple 'using' in this field list");
|
||||
if (no_alias_count > 1) syntax_error(f->curr_token, "Multiple '#no_alias' in this field list");
|
||||
if (c_vararg_count > 1) syntax_error(f->curr_token, "Multiple '#c_vararg' in this field list");
|
||||
if (auto_cast_count > 1) syntax_error(f->curr_token, "Multiple 'auto_cast' in this field list");
|
||||
if (const_count > 1) syntax_error(f->curr_token, "Multiple '#const' in this field list");
|
||||
|
||||
|
||||
u32 field_flags = 0;
|
||||
@@ -3049,6 +3073,7 @@ u32 parse_field_prefixes(AstFile *f) {
|
||||
if (no_alias_count > 0) field_flags |= FieldFlag_no_alias;
|
||||
if (c_vararg_count > 0) field_flags |= FieldFlag_c_vararg;
|
||||
if (auto_cast_count > 0) field_flags |= FieldFlag_auto_cast;
|
||||
if (const_count > 0) field_flags |= FieldFlag_const;
|
||||
return field_flags;
|
||||
}
|
||||
|
||||
|
||||
@@ -203,12 +203,13 @@ enum FieldFlag {
|
||||
FieldFlag_no_alias = 1<<2,
|
||||
FieldFlag_c_vararg = 1<<3,
|
||||
FieldFlag_auto_cast = 1<<4,
|
||||
FieldFlag_const = 1<<5,
|
||||
|
||||
FieldFlag_Tags = 1<<10,
|
||||
|
||||
FieldFlag_Results = 1<<16,
|
||||
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast,
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const,
|
||||
FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags,
|
||||
};
|
||||
|
||||
@@ -515,6 +516,7 @@ AST_KIND(_TypeBegin, "", bool) \
|
||||
Array<Ast *> variants; \
|
||||
Ast *polymorphic_params; \
|
||||
Ast * align; \
|
||||
bool maybe; \
|
||||
bool no_nil; \
|
||||
Token where_token; \
|
||||
Array<Ast *> where_clauses; \
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user