mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-04 09:44:40 +00:00
Merge pull request #5930 from odin-lang/bill/os2-file-stream
`os2.File_Stream`
This commit is contained in:
@@ -215,7 +215,8 @@ type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V --
|
||||
type_is_specialized_polymorphic_record :: proc($T: typeid) -> bool ---
|
||||
type_is_unspecialized_polymorphic_record :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_subtype_of :: proc($T, $U: typeid) -> bool ---
|
||||
type_is_subtype_of :: proc($T, $U: typeid) -> bool ---
|
||||
type_is_superset_of :: proc($Super, $Sub: typeid) -> bool ---
|
||||
|
||||
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@ import "base:runtime"
|
||||
*/
|
||||
File :: struct {
|
||||
impl: rawptr,
|
||||
stream: io.Stream,
|
||||
fstat: Fstat_Callback,
|
||||
stream: File_Stream,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -218,7 +217,11 @@ name :: proc(f: ^File) -> string {
|
||||
*/
|
||||
close :: proc(f: ^File) -> Error {
|
||||
if f != nil {
|
||||
return io.close(f.stream)
|
||||
if f.stream.procedure == nil {
|
||||
return .Unsupported
|
||||
}
|
||||
_, err := f.stream.procedure(f, .Close, nil, 0, nil, runtime.nil_allocator())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -235,7 +238,10 @@ close :: proc(f: ^File) -> Error {
|
||||
*/
|
||||
seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
|
||||
if f != nil {
|
||||
return io.seek(f.stream, offset, whence)
|
||||
if f.stream.procedure == nil {
|
||||
return 0, .Unsupported
|
||||
}
|
||||
return f.stream.procedure(f, .Seek, nil, offset, whence, runtime.nil_allocator())
|
||||
}
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
@@ -247,7 +253,12 @@ seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Err
|
||||
*/
|
||||
read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
if f != nil {
|
||||
return io.read(f.stream, p)
|
||||
if f.stream.procedure == nil {
|
||||
return 0, .Unsupported
|
||||
}
|
||||
n64: i64
|
||||
n64, err = f.stream.procedure(f, .Read, p, 0, nil, runtime.nil_allocator())
|
||||
return int(n64), err
|
||||
}
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
@@ -260,7 +271,12 @@ read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
*/
|
||||
read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if f != nil {
|
||||
return io.read_at(f.stream, p, offset)
|
||||
if f.stream.procedure == nil {
|
||||
return 0, .Unsupported
|
||||
}
|
||||
n64: i64
|
||||
n64, err = f.stream.procedure(f, .Read_At, p, offset, nil, runtime.nil_allocator())
|
||||
return int(n64), err
|
||||
}
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
@@ -272,7 +288,12 @@ read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
*/
|
||||
write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
if f != nil {
|
||||
return io.write(f.stream, p)
|
||||
if f.stream.procedure == nil {
|
||||
return 0, .Unsupported
|
||||
}
|
||||
n64: i64
|
||||
n64, err = f.stream.procedure(f, .Write, p, 0, nil, runtime.nil_allocator())
|
||||
return int(n64), err
|
||||
}
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
@@ -284,7 +305,12 @@ write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
*/
|
||||
write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if f != nil {
|
||||
return io.write_at(f.stream, p, offset)
|
||||
if f.stream.procedure == nil {
|
||||
return 0, .Unsupported
|
||||
}
|
||||
n64: i64
|
||||
n64, err = f.stream.procedure(f, .Write_At, p, offset, nil, runtime.nil_allocator())
|
||||
return int(n64), err
|
||||
}
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
@@ -294,7 +320,18 @@ write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
*/
|
||||
file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
if f != nil {
|
||||
return io.size(f.stream)
|
||||
if f.stream.procedure == nil {
|
||||
return 0, .Unsupported
|
||||
}
|
||||
n, err = f.stream.procedure(f, .Size, nil, 0, nil, runtime.nil_allocator())
|
||||
if err == .Unsupported {
|
||||
n = 0
|
||||
curr := seek(f, 0, .Current) or_return
|
||||
end := seek(f, 0, .End) or_return
|
||||
seek(f, curr, .Start) or_return
|
||||
n = end
|
||||
}
|
||||
return
|
||||
}
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
@@ -304,7 +341,11 @@ file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
*/
|
||||
flush :: proc(f: ^File) -> Error {
|
||||
if f != nil {
|
||||
return io.flush(f.stream)
|
||||
if f.stream.procedure == nil {
|
||||
return .Unsupported
|
||||
}
|
||||
_, err := f.stream.procedure(f, .Flush, nil, 0, nil, runtime.nil_allocator())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -29,19 +29,16 @@ _stdin := File{
|
||||
stream = {
|
||||
procedure = _file_stream_proc,
|
||||
},
|
||||
fstat = _fstat,
|
||||
}
|
||||
_stdout := File{
|
||||
stream = {
|
||||
procedure = _file_stream_proc,
|
||||
},
|
||||
fstat = _fstat,
|
||||
}
|
||||
_stderr := File{
|
||||
stream = {
|
||||
procedure = _file_stream_proc,
|
||||
},
|
||||
fstat = _fstat,
|
||||
}
|
||||
|
||||
@init
|
||||
@@ -55,7 +52,6 @@ _standard_stream_init :: proc "contextless" () {
|
||||
data = impl,
|
||||
procedure = _file_stream_proc,
|
||||
}
|
||||
impl.file.fstat = _fstat
|
||||
return &impl.file
|
||||
}
|
||||
|
||||
@@ -109,7 +105,6 @@ _new_file :: proc(fd: uintptr, _: string, allocator: runtime.Allocator) -> (f: ^
|
||||
data = impl,
|
||||
procedure = _file_stream_proc,
|
||||
}
|
||||
impl.file.fstat = _fstat
|
||||
return &impl.file, nil
|
||||
}
|
||||
|
||||
@@ -476,88 +471,76 @@ _read_entire_pseudo_file_cstring :: proc(name: cstring, allocator: runtime.Alloc
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
_file_stream_proc :: proc(stream_data: rawptr, mode: File_Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From, allocator: runtime.Allocator) -> (n: i64, err: Error) {
|
||||
f := (^File_Impl)(stream_data)
|
||||
ferr: Error
|
||||
switch mode {
|
||||
case .Read:
|
||||
n, ferr = _read(f, p)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _read(f, p)
|
||||
return
|
||||
case .Read_At:
|
||||
n, ferr = _read_at(f, p, offset)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _read_at(f, p, offset)
|
||||
return
|
||||
case .Write:
|
||||
n, ferr = _write(f, p)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _write(f, p)
|
||||
return
|
||||
case .Write_At:
|
||||
n, ferr = _write_at(f, p, offset)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _write_at(f, p, offset)
|
||||
return
|
||||
case .Seek:
|
||||
n, ferr = _seek(f, offset, whence)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _seek(f, offset, whence)
|
||||
return
|
||||
case .Size:
|
||||
n, ferr = _file_size(f)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _file_size(f)
|
||||
return
|
||||
case .Flush:
|
||||
ferr = _flush(f)
|
||||
err = error_to_io_error(ferr)
|
||||
err = _flush(f)
|
||||
return
|
||||
case .Close, .Destroy:
|
||||
ferr = _close(f)
|
||||
err = error_to_io_error(ferr)
|
||||
err = _close(f)
|
||||
return
|
||||
case .Query:
|
||||
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
|
||||
case .Fstat:
|
||||
err = file_stream_fstat_utility(f, p, allocator)
|
||||
return
|
||||
}
|
||||
return 0, .Unsupported
|
||||
}
|
||||
|
||||
|
||||
@(private="package")
|
||||
_file_stream_buffered_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
_file_stream_buffered_proc :: proc(stream_data: rawptr, mode: File_Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From, allocator: runtime.Allocator) -> (n: i64, err: Error) {
|
||||
f := (^File_Impl)(stream_data)
|
||||
ferr: Error
|
||||
switch mode {
|
||||
case .Read:
|
||||
n, ferr = _read(f, p)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _read(f, p)
|
||||
return
|
||||
case .Read_At:
|
||||
n, ferr = _read_at(f, p, offset)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _read_at(f, p, offset)
|
||||
return
|
||||
case .Write:
|
||||
n, ferr = _write(f, p)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _write(f, p)
|
||||
return
|
||||
case .Write_At:
|
||||
n, ferr = _write_at(f, p, offset)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _write_at(f, p, offset)
|
||||
return
|
||||
case .Seek:
|
||||
n, ferr = _seek(f, offset, whence)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _seek(f, offset, whence)
|
||||
return
|
||||
case .Size:
|
||||
n, ferr = _file_size(f)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _file_size(f)
|
||||
return
|
||||
case .Flush:
|
||||
ferr = _flush(f)
|
||||
err = error_to_io_error(ferr)
|
||||
err = _flush(f)
|
||||
return
|
||||
case .Close, .Destroy:
|
||||
ferr = _close(f)
|
||||
err = error_to_io_error(ferr)
|
||||
err = _close(f)
|
||||
return
|
||||
case .Query:
|
||||
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
|
||||
case .Fstat:
|
||||
err = file_stream_fstat_utility(f, p, allocator)
|
||||
return
|
||||
}
|
||||
return 0, .Unsupported
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ init_std_files :: proc "contextless" () {
|
||||
data = impl,
|
||||
procedure = _file_stream_proc,
|
||||
}
|
||||
impl.file.fstat = _fstat
|
||||
return &impl.file
|
||||
}
|
||||
|
||||
@@ -110,7 +109,6 @@ __new_file :: proc(handle: posix.FD, allocator: runtime.Allocator) -> ^File {
|
||||
data = impl,
|
||||
procedure = _file_stream_proc,
|
||||
}
|
||||
impl.file.fstat = _fstat
|
||||
return &impl.file
|
||||
}
|
||||
|
||||
@@ -371,7 +369,7 @@ _exists :: proc(path: string) -> bool {
|
||||
return posix.access(cpath) == .OK
|
||||
}
|
||||
|
||||
_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
_file_stream_proc :: proc(stream_data: rawptr, mode: File_Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From, allocator: runtime.Allocator) -> (n: i64, err: Error) {
|
||||
f := (^File_Impl)(stream_data)
|
||||
fd := f.fd
|
||||
|
||||
@@ -489,18 +487,19 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
|
||||
return
|
||||
|
||||
case .Flush:
|
||||
ferr := _sync(&f.file)
|
||||
err = error_to_io_error(ferr)
|
||||
err = _sync(&f.file)
|
||||
return
|
||||
|
||||
case .Close, .Destroy:
|
||||
ferr := _close(f)
|
||||
err = error_to_io_error(ferr)
|
||||
err = _close(f)
|
||||
return
|
||||
|
||||
case .Query:
|
||||
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
|
||||
|
||||
case .Fstat:
|
||||
err = file_stream_fstat_utility(f, p, allocator)
|
||||
return
|
||||
case:
|
||||
return 0, .Unsupported
|
||||
}
|
||||
|
||||
@@ -1,12 +1,50 @@
|
||||
package os2
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
|
||||
// A subset of the io.Stream_Mode with added File specific modes
|
||||
File_Stream_Mode :: enum {
|
||||
Close,
|
||||
Flush,
|
||||
Read,
|
||||
Read_At,
|
||||
Write,
|
||||
Write_At,
|
||||
Seek,
|
||||
Size,
|
||||
Destroy,
|
||||
Query, // query what modes are available on `io.Stream`
|
||||
|
||||
Fstat, // File specific (not available on io.Stream)
|
||||
}
|
||||
#assert(intrinsics.type_is_superset_of(File_Stream_Mode, io.Stream_Mode))
|
||||
|
||||
// Superset interface of io.Stream_Proc with the added `runtime.Allocator` parameter needed for the Fstat mode
|
||||
File_Stream_Proc :: #type proc(
|
||||
stream_data: rawptr,
|
||||
mode: File_Stream_Mode,
|
||||
p: []byte,
|
||||
offset: i64,
|
||||
whence: io.Seek_From,
|
||||
allocator: runtime.Allocator,
|
||||
) -> (n: i64, err: Error)
|
||||
|
||||
File_Stream :: struct {
|
||||
procedure: File_Stream_Proc,
|
||||
data: rawptr,
|
||||
}
|
||||
|
||||
|
||||
// Converts a file `f` into an `io.Stream`
|
||||
to_stream :: proc(f: ^File) -> (s: io.Stream) {
|
||||
if f != nil {
|
||||
assert(f.stream.procedure != nil)
|
||||
s = f.stream
|
||||
s = {
|
||||
file_io_stream_proc,
|
||||
f,
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -24,3 +62,28 @@ to_writer :: to_stream
|
||||
even if it has no logical difference.
|
||||
*/
|
||||
to_reader :: to_stream
|
||||
|
||||
|
||||
@(private="package")
|
||||
file_io_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
f := (^File)(stream_data)
|
||||
|
||||
file_stream_mode := transmute(File_Stream_Mode)mode
|
||||
|
||||
ferr: Error
|
||||
n, ferr = f.stream.procedure(f, file_stream_mode, p, offset, whence, runtime.nil_allocator())
|
||||
err = error_to_io_error(ferr)
|
||||
return
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
file_stream_fstat_utility :: proc(f: ^File_Impl, p: []byte, allocator: runtime.Allocator) -> (err: Error) {
|
||||
fi: File_Info
|
||||
if len(p) >= size_of(fi) {
|
||||
fi, err = _fstat(&f.file, allocator)
|
||||
runtime.mem_copy_non_overlapping(raw_data(p), &fi, size_of(fi))
|
||||
} else {
|
||||
err = .Short_Buffer
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -65,7 +65,6 @@ init_std_files :: proc "contextless" () {
|
||||
data = impl,
|
||||
procedure = _file_stream_proc,
|
||||
}
|
||||
impl.file.fstat = _fstat
|
||||
|
||||
return &impl.file
|
||||
}
|
||||
@@ -185,7 +184,6 @@ _new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -
|
||||
data = impl,
|
||||
procedure = _file_stream_proc,
|
||||
}
|
||||
impl.file.fstat = _fstat
|
||||
|
||||
return &impl.file, nil
|
||||
}
|
||||
@@ -827,44 +825,38 @@ _exists :: proc(path: string) -> bool {
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
_file_stream_proc :: proc(stream_data: rawptr, mode: File_Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From, allocator: runtime.Allocator) -> (n: i64, err: Error) {
|
||||
f := (^File_Impl)(stream_data)
|
||||
ferr: Error
|
||||
switch mode {
|
||||
case .Read:
|
||||
n, ferr = _read(f, p)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _read(f, p)
|
||||
return
|
||||
case .Read_At:
|
||||
n, ferr = _read_at(f, p, offset)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _read_at(f, p, offset)
|
||||
return
|
||||
case .Write:
|
||||
n, ferr = _write(f, p)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _write(f, p)
|
||||
return
|
||||
case .Write_At:
|
||||
n, ferr = _write_at(f, p, offset)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _write_at(f, p, offset)
|
||||
return
|
||||
case .Seek:
|
||||
n, ferr = _seek(f, offset, whence)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _seek(f, offset, whence)
|
||||
return
|
||||
case .Size:
|
||||
n, ferr = _file_size(f)
|
||||
err = error_to_io_error(ferr)
|
||||
n, err = _file_size(f)
|
||||
return
|
||||
case .Flush:
|
||||
ferr = _flush(f)
|
||||
err = error_to_io_error(ferr)
|
||||
err = _flush(f)
|
||||
return
|
||||
case .Close, .Destroy:
|
||||
ferr = _close(f)
|
||||
err = error_to_io_error(ferr)
|
||||
err = _close(f)
|
||||
return
|
||||
case .Query:
|
||||
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
|
||||
case .Fstat:
|
||||
err = file_stream_fstat_utility(f, p, allocator)
|
||||
return
|
||||
}
|
||||
return 0, .Unsupported
|
||||
}
|
||||
|
||||
@@ -46,8 +46,11 @@ file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) {
|
||||
fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
if f == nil {
|
||||
return {}, nil
|
||||
} else if f.fstat != nil {
|
||||
return f->fstat(allocator)
|
||||
} else if f.stream.procedure != nil {
|
||||
fi: File_Info
|
||||
data := ([^]byte)(&fi)[:size_of(fi)]
|
||||
_, err := f.stream.procedure(f, .Fstat, data, 0, nil, allocator)
|
||||
return fi, err
|
||||
}
|
||||
return {}, .Invalid_Callback
|
||||
}
|
||||
|
||||
@@ -2485,6 +2485,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
case BuiltinProc_min:
|
||||
case BuiltinProc_max:
|
||||
case BuiltinProc_type_is_subtype_of:
|
||||
case BuiltinProc_type_is_superset_of:
|
||||
case BuiltinProc_objc_send:
|
||||
case BuiltinProc_objc_find_selector:
|
||||
case BuiltinProc_objc_find_class:
|
||||
@@ -7397,6 +7398,129 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
operand->type = t_untyped_bool;
|
||||
} break;
|
||||
|
||||
case BuiltinProc_type_is_superset_of:
|
||||
{
|
||||
Operand op_super = {};
|
||||
Operand op_sub = {};
|
||||
|
||||
check_expr_or_type(c, &op_super, ce->args[0]);
|
||||
if (op_super.mode != Addressing_Type) {
|
||||
gbString e = expr_to_string(op_super.expr);
|
||||
error(op_super.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
|
||||
gb_string_free(e);
|
||||
return false;
|
||||
}
|
||||
check_expr_or_type(c, &op_sub, ce->args[1]);
|
||||
if (op_sub.mode != Addressing_Type) {
|
||||
gbString e = expr_to_string(op_sub.expr);
|
||||
error(op_sub.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
|
||||
gb_string_free(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->type = t_untyped_bool;
|
||||
|
||||
Type *super = op_super.type;
|
||||
Type *sub = op_sub.type;
|
||||
if (are_types_identical(super, sub)) {
|
||||
operand->value = exact_value_bool(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
super = base_type(super);
|
||||
sub = base_type(sub);
|
||||
if (are_types_identical(super, sub)) {
|
||||
operand->value = exact_value_bool(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (super->kind != sub->kind) {
|
||||
gbString a = type_to_string(op_super.type);
|
||||
gbString b = type_to_string(op_sub.type);
|
||||
error(op_super.expr, "'%.*s' expects types of the same kind, got %s vs %s", LIT(builtin_name), a, b);
|
||||
gb_string_free(b);
|
||||
gb_string_free(a);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (super->kind == Type_Enum) {
|
||||
if (sub->Enum.fields.count > super->Enum.fields.count) {
|
||||
operand->value = exact_value_bool(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Type *base_super = base_enum_type(super);
|
||||
Type *base_sub = base_enum_type(sub);
|
||||
if (base_super == base_sub && base_super == nullptr) {
|
||||
// okay
|
||||
} else if (!are_types_identical(base_type(base_super), base_type(base_sub))) {
|
||||
operand->value = exact_value_bool(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Entity *f_sub : sub->Enum.fields) {
|
||||
bool found = false;
|
||||
|
||||
if (f_sub->kind != Entity_Constant) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Entity *f_super : super->Enum.fields) {
|
||||
if (f_super->kind != Entity_Constant) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (f_sub->token.string == f_super->token.string) {
|
||||
if (compare_exact_values(Token_CmpEq, f_sub->Constant.value, f_super->Constant.value)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
operand->value = exact_value_bool(false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
operand->value = exact_value_bool(true);
|
||||
return true;
|
||||
|
||||
} else if (super->kind == Type_Union) {
|
||||
if (sub->Union.variants.count > super->Union.variants.count) {
|
||||
operand->value = exact_value_bool(false);
|
||||
return true;
|
||||
}
|
||||
if (sub->Union.kind != super->Union.kind) {
|
||||
operand->value = exact_value_bool(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
for_array(i, sub->Union.variants) {
|
||||
Type *t_sub = sub->Union.variants[i];
|
||||
Type *t_super = super->Union.variants[i];
|
||||
if (!are_types_identical(t_sub, t_super)) {
|
||||
operand->value = exact_value_bool(false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
operand->value = exact_value_bool(true);
|
||||
return true;
|
||||
|
||||
}
|
||||
gbString a = type_to_string(op_super.type);
|
||||
gbString b = type_to_string(op_sub.type);
|
||||
error(op_super.expr, "'%.*s' expects types of the same kind and either an enum or union, got %s vs %s", LIT(builtin_name), a, b);
|
||||
gb_string_free(b);
|
||||
gb_string_free(a);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
case BuiltinProc_type_field_index_of:
|
||||
{
|
||||
Operand op = {};
|
||||
|
||||
@@ -336,6 +336,7 @@ BuiltinProc__type_simple_boolean_end,
|
||||
BuiltinProc_type_polymorphic_record_parameter_value,
|
||||
|
||||
BuiltinProc_type_is_subtype_of,
|
||||
BuiltinProc_type_is_superset_of,
|
||||
|
||||
BuiltinProc_type_field_index_of,
|
||||
|
||||
@@ -708,6 +709,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("type_is_subtype_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_is_superset_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics },
|
||||
|
||||
{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user