From ee3464461399e0561ae667ed483acb5719366124 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 30 Oct 2023 14:07:37 +0100 Subject: [PATCH] Interface based streams. Fix for initializing with a force unwrap inside. Allow $define to take a list. Allow $define to return error on argument type mismatch in call. Fixed broken bit operations on boolean vectors. --- lib/std/core/allocators/temp_allocator.c3 | 10 +- lib/std/core/builtin.c3 | 40 +- lib/std/core/builtin_comparison.c3 | 4 +- lib/std/core/dstring.c3 | 22 +- lib/std/encoding/csv.c3 | 12 +- lib/std/encoding/json.c3 | 4 +- lib/std/io/bits.c3 | 18 +- lib/std/io/file.c3 | 29 +- lib/std/io/io.c3 | 145 ++++++- lib/std/io/stream.c3 | 396 ++++-------------- lib/std/io/stream/buffer.c3 | 53 ++- lib/std/io/stream/bytebuffer.c3 | 32 +- lib/std/io/stream/bytereader.c3 | 36 +- lib/std/io/stream/bytewriter.c3 | 25 +- lib/std/io/stream/dstringwriter.c3 | 38 -- lib/std/io/stream/limitreader.c3 | 29 +- lib/std/io/stream/scanner.c3 | 28 +- lib/std/net/socket.c3 | 25 +- resources/examples/contextfree/boolerr.c3 | 2 +- resources/examples/load_world.c3 | 2 +- resources/examples/plus_minus.c3 | 2 +- resources/examples/process.c3 | 2 +- src/compiler/compiler_internal.h | 2 +- src/compiler/copying.c | 4 +- src/compiler/expr.c | 1 + src/compiler/llvm_codegen_expr.c | 2 +- src/compiler/parse_expr.c | 14 +- src/compiler/sema_builtins.c | 12 +- src/compiler/sema_decls.c | 8 +- src/compiler/sema_expr.c | 367 ++++++++-------- src/compiler/sema_initializers.c | 10 +- src/compiler/sema_stmts.c | 20 +- src/version.h | 2 +- test/test_suite/errors/rethrow_macro.c3 | 2 +- .../generic/generic_lambda_complex.c3t | 17 +- .../macros/unifying_implicit_void.c3t | 89 ++-- .../switch/switch_in_defer_macro.c3t | 96 +++-- test/unit/stdlib/io/bufferstream.c3 | 4 +- test/unit/stdlib/io/bytestream.c3 | 31 +- test/unit/stdlib/io/dstringstream.c3 | 4 +- test/unit/stdlib/io/file_read_write_any.c3 | 4 +- test/unit/stdlib/io/varint.c3 | 10 +- 42 files changed, 776 insertions(+), 877 deletions(-) delete mode 100644 lib/std/io/stream/dstringwriter.c3 diff --git a/lib/std/core/allocators/temp_allocator.c3 b/lib/std/core/allocators/temp_allocator.c3 index 37f3463ea..759980b30 100644 --- a/lib/std/core/allocators/temp_allocator.c3 +++ b/lib/std/core/allocators/temp_allocator.c3 @@ -202,21 +202,21 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz return &page.data[0]; } -fn void TempAllocator.print_pages(&self, File f) +fn void! TempAllocator.print_pages(&self, File f) { TempAllocatorPage *last_page = self.last_page; if (!last_page) { - f.printf("No pages.\n"); + io::fprintf(&f, "No pages.\n")!; return; } - f.printf("---Pages----\n"); + io::fprintf(&f, "---Pages----\n")!; uint index = 0; while (last_page) { bool is_not_aligned = !(last_page.size & (1u64 << 63)); - f.printf("%d. Alloc: %d %d at %p%s\n", ++index, - last_page.size & ~(1u64 << 63), last_page.mark, &last_page.data[0], is_not_aligned ? "" : " [aligned]"); + io::fprintf(&f, "%d. Alloc: %d %d at %p%s\n", ++index, + last_page.size & ~(1u64 << 63), last_page.mark, &last_page.data[0], is_not_aligned ? "" : " [aligned]")!; last_page = last_page.prev_page; } } diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index c0a517aa4..053ffc1e1 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -94,33 +94,33 @@ fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::DARWI BacktraceList! backtrace = darwin::backtrace_load(mem::temp()); if (catch backtrace) return false; if (backtrace.len() <= backtraces_to_ignore) return false; - (void)io::stderr().print("\nERROR: '"); - (void)io::stderr().print(message); - io::printn("'"); + io::eprint("\nERROR: '"); + io::eprint(message); + io::eprintn("'"); foreach (i, &trace : backtrace) { if (i < backtraces_to_ignore) continue; if (trace.is_unknown()) { - (void)io::stderr().printn(" in ???"); + io::eprintn(" in ???"); continue; } if (trace.has_file()) { - (void)io::stderr().printfn(" in %s (%s:%d) [%s]", trace.function, trace.file, trace.line, trace.object_file); + io::eprintfn(" in %s (%s:%d) [%s]", trace.function, trace.file, trace.line, trace.object_file); continue; } - (void)io::stderr().printfn(" in %s (source unavailable) [%s]", trace.function, trace.object_file); + io::eprintfn(" in %s (source unavailable) [%s]", trace.function, trace.object_file); } return true; }; } fn void default_panic(String message, String file, String function, uint line) @if(env::DARWIN) { - $if $defined(io::stderr) && $defined(Stream.printf): + $if $defined(io::stderr): if (!print_backtrace(message, 2)) { - (void)io::stderr().printfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line); + io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line); return; } $endif @@ -130,23 +130,23 @@ fn void default_panic(String message, String file, String function, uint line) @ fn void default_panic(String message, String file, String function, uint line) @if(!env::DARWIN) { CallstackElement* stack = $$stacktrace(); - $if $defined(io::stderr) && $defined(Stream.printf): + $if $defined(io::stderr): if (stack) stack = stack.prev; if (stack) { - (void)io::stderr().print("\nERROR: '"); - (void)io::stderr().print(message); - (void)io::stderr().printn("'"); + io::eprint("\nERROR: '"); + io::eprint(message); + io::eprintn("'"); } else { - (void)io::stderr().print("\nERROR: '"); - (void)io::stderr().print(message); - (void)io::stderr().printfn("', in %s (%s:%d)", function, file, line); + io::eprint("\nERROR: '"); + io::eprint(message); + io::eprintfn("', in %s (%s:%d)", function, file, line); } while (stack) { - (void)io::stderr().printfn(" in %s %s (%s:%d)", stack.location.name, stack.function, stack.file, stack.line); + io::eprintfn(" in %s %s (%s:%d)", stack.location.name, stack.function, stack.file, stack.line); if (stack == stack.prev) break; stack = stack.prev; } @@ -398,10 +398,10 @@ fn void sig_bus_error(CInt i) $if !env::DARWIN: sig_panic("Illegal memory access."); $else - $if $defined(io::stderr) && $defined(Stream.printf): + $if $defined(io::stderr): if (!print_backtrace("Illegal memory access.", 1)) { - (void)io::stderr().printn("\nERROR: 'Illegal memory access'."); + io::eprintn("\nERROR: 'Illegal memory access'."); } $endif $endif @@ -413,10 +413,10 @@ fn void sig_segmentation_fault(CInt i) $if !env::DARWIN: sig_panic("Out of bounds memory access."); $else - $if $defined(io::stderr) && $defined(Stream.printf): + $if $defined(io::stderr): if (!print_backtrace("Out of bounds memory access.", 1)) { - (void)io::stderr().printn("\nERROR: Memory error without backtrace, possible stack overflow."); + io::eprintn("\nERROR: Memory error without backtrace, possible stack overflow."); } $endif $endif diff --git a/lib/std/core/builtin_comparison.c3 b/lib/std/core/builtin_comparison.c3 index e60a151c0..30fbfe2ca 100644 --- a/lib/std/core/builtin_comparison.c3 +++ b/lib/std/core/builtin_comparison.c3 @@ -83,9 +83,9 @@ macro greater_eq(a, b) @builtin macro bool equals(a, b) @builtin { $switch - $case $defined(a.equals): + $case $defined(a.equals, a.equals(b)): return a.equals(b); - $case $defined(a.compare_to): + $case $defined(a.compare_to, a.compare_to(b)): return a.compare_to(b) == 0; $case $defined(a.less): return !a.less(b) && !b.less(a); diff --git a/lib/std/core/dstring.c3 b/lib/std/core/dstring.c3 index 385b58965..49bca723d 100644 --- a/lib/std/core/dstring.c3 +++ b/lib/std/core/dstring.c3 @@ -1,6 +1,7 @@ module std::core::dstring; +import std::io; -distinct DString = void*; +distinct DString (OutStream) = void*; const usz MIN_CAPACITY @private = 16; @@ -79,9 +80,9 @@ fn usz DString.capacity(self) return self.data().capacity; } -fn usz DString.len(self) +fn usz DString.len(&self) @dynamic { - if (!self) return 0; + if (!*self) return 0; return self.data().len; } @@ -255,6 +256,17 @@ fn void DString.clear(self) self.data().len = 0; } +fn usz! DString.write(&self, char[] buffer) @dynamic +{ + self.append_chars((String)buffer); + return buffer.len; +} + +fn void! DString.write_byte(&self, char c) @dynamic +{ + self.append_char(c); +} + fn void DString.append_char(&self, char c) { if (!*self) @@ -387,9 +399,9 @@ fn void DString.reserve(&self, usz addition) *self = (DString)realloc(data, StringData.sizeof + new_capacity, .using = data.allocator); } -fn usz! DString.read_from_stream(&self, Stream* reader) +fn usz! DString.read_from_stream(&self, InStream* reader) { - if (reader.supports_available()) + if (&reader.available) { usz total_read = 0; while (usz available = reader.available()!) diff --git a/lib/std/encoding/csv.c3 b/lib/std/encoding/csv.c3 index 382527f3b..aa3eba359 100644 --- a/lib/std/encoding/csv.c3 +++ b/lib/std/encoding/csv.c3 @@ -3,11 +3,11 @@ import std::io; struct CsvReader { - Stream* stream; + InStream* stream; String separator; } -fn void CsvReader.init(&self, Stream* stream, String separator = ",") +fn void CsvReader.init(&self, InStream* stream, String separator = ",") { self.stream = stream; self.separator = separator; @@ -15,9 +15,9 @@ fn void CsvReader.init(&self, Stream* stream, String separator = ",") fn String[]! CsvReader.read_row(self, Allocator* using = mem::heap()) { - @pool() + @pool(using) { - return self.stream.treadline().split(self.separator, .using = using); + return io::treadline(self.stream).split(self.separator, .using = using); }; } @@ -30,13 +30,13 @@ fn void! CsvReader.skip_row(self) @maydiscard { @pool() { - self.stream.readline(mem::temp())!; + (void)io::treadline(self.stream); }; } macro CsvReader.@each_row(self, int rows = int.max; @body(String[] row)) { - Stream* stream = self.stream; + InputStream* stream = self.stream; String sep = self.separator; while (rows--) { diff --git a/lib/std/encoding/json.c3 b/lib/std/encoding/json.c3 index 5ea266487..915c544ff 100644 --- a/lib/std/encoding/json.c3 +++ b/lib/std/encoding/json.c3 @@ -15,7 +15,7 @@ fault JsonParsingError INVALID_NUMBER, } -fn Object*! parse(Stream* s, Allocator* using = mem::heap()) +fn Object*! parse(InStream* s, Allocator* using = mem::heap()) { JsonContext context = { .last_string = dstring::new_with_capacity(64, using), .stream = s, .allocator = using }; defer context.last_string.free(); @@ -44,7 +44,7 @@ enum JsonTokenType @local struct JsonContext @local { uint line; - Stream* stream; + InStream* stream; Allocator* allocator; JsonTokenType token; DString last_string; diff --git a/lib/std/io/bits.c3 b/lib/std/io/bits.c3 index 702c1b1b4..04449195b 100644 --- a/lib/std/io/bits.c3 +++ b/lib/std/io/bits.c3 @@ -2,15 +2,12 @@ module std::io; struct BitReader { - Stream reader; + InStream* reader; uint bits; uint len; } -/** - * @require byte_reader.supports_read_byte() - **/ -fn void BitReader.init(&self, Stream byte_reader) +fn void BitReader.init(&self, InStream* byte_reader) { *self = { .reader = byte_reader }; } @@ -43,15 +40,12 @@ fn char! BitReader.read_bits(&self, uint nbits) struct BitWriter { - Stream writer; + OutStream* writer; uint bits; uint len; } -/** - * @require byte_writer.supports_write_byte() - **/ -fn void BitWriter.init(&self, Stream byte_writer) +fn void BitWriter.init(&self, OutStream* byte_writer) { *self = { .writer = byte_writer }; } @@ -63,7 +57,7 @@ fn void! BitWriter.flush(&self) uint n = (self.len + 7) / 8; char[4] buffer; bitorder::write(bits, &buffer, UIntBE); - self.writer.write_all(buffer[:n])!; + io::write_all(self.writer, buffer[:n])!; self.len = 0; } @@ -83,7 +77,7 @@ fn void! BitWriter.write_bits(&self, uint bits, uint nbits) lbits |= (ulong)(bits >> left) << (64 - (n - left)); char[8] buffer; bitorder::write(lbits, &buffer, ULongBE); - self.writer.write_all(buffer[:to_write])!; + io::write_all(self.writer, buffer[:to_write])!; } self.bits <<= left; self.bits |= bits & ((1 << left) - 1); diff --git a/lib/std/io/file.c3 b/lib/std/io/file.c3 index 77ca1172c..ad00a8cff 100644 --- a/lib/std/io/file.c3 +++ b/lib/std/io/file.c3 @@ -1,9 +1,8 @@ module std::io; import libc; -struct File +struct File (InStream, OutStream) { - inline Stream stream; CFile file; } @@ -22,7 +21,7 @@ fn File! open_path(Path path, String mode) fn File from_handle(CFile file) { - return { .stream.fns = &FILESTREAM_INTERFACE, .file = file }; + return { .file = file }; } fn bool is_file(String path) @@ -49,7 +48,7 @@ fn void! File.reopen(&self, String filename, String mode) /** * @require self.file != null **/ -fn usz! File.seek(&self, isz offset, Seek seek_mode = Seek.SET) +fn usz! File.seek(&self, isz offset, Seek seek_mode = Seek.SET) @dynamic { os::native_fseek(self.file, offset, seek_mode)!; return os::native_ftell(self.file); @@ -75,7 +74,7 @@ fn void! File.memopen(File* file, char[] data, String mode) /** * @require self.file != null */ -fn void! File.write_byte(&self, char c) +fn void! File.write_byte(&self, char c) @dynamic { if (!libc::fputc(c, self.file)) return IoError.EOF?; } @@ -83,7 +82,7 @@ fn void! File.write_byte(&self, char c) /** * @param [&inout] self */ -fn void! File.close(&self) @inline +fn void! File.close(&self) @inline @dynamic { if (self.file && libc::fclose(self.file)) { @@ -117,7 +116,7 @@ fn bool File.eof(&self) @inline /** * @param [in] buffer */ -fn usz! File.read(&self, char[] buffer) +fn usz! File.read(&self, char[] buffer) @dynamic { return os::native_fread(self.file, buffer); } @@ -126,13 +125,13 @@ fn usz! File.read(&self, char[] buffer) * @param [out] buffer * @require self.file `File must be initialized` */ -fn usz! File.write(&self, char[] buffer) +fn usz! File.write(&self, char[] buffer) @dynamic { return os::native_fwrite(self.file, buffer); } -fn char! File.read_byte(&self) +fn char! File.read_byte(&self) @dynamic { int c = libc::fgetc(self.file); if (c == -1) return IoError.EOF?; @@ -142,17 +141,7 @@ fn char! File.read_byte(&self) /** * @require self.file `File must be initialized` */ -fn void! File.flush(&self) +fn void! File.flush(&self) @dynamic { libc::fflush(self.file); } - -const StreamInterface FILESTREAM_INTERFACE = { - .close_fn = (CloseStreamFn)&File.close, - .seek_fn = (SeekStreamFn)&File.seek, - .read_fn = (ReadStreamFn)&File.read, - .read_byte_fn = (ReadByteStreamFn)&File.read_byte, - .write_fn = (WriteStreamFn)&File.write, - .write_byte_fn = (WriteByteStreamFn)&File.write_byte, - .flush_fn = (FlushStreamFn)&File.flush -}; \ No newline at end of file diff --git a/lib/std/io/io.c3 b/lib/std/io/io.c3 index 6753af57d..83753aa95 100644 --- a/lib/std/io/io.c3 +++ b/lib/std/io/io.c3 @@ -45,14 +45,127 @@ fault IoError } +/** + * @param stream + * @require @is_instream(stream) + **/ +macro String! readline(stream = io::stdin(), Allocator* using = mem::heap()) +{ + bool $is_stream = $typeof(stream).typeid == InStream*.typeid; + $if $is_stream: + $typeof(&stream.read_byte) func = &stream.read_byte; + char val = func((void*)stream)!; + $else + char val = stream.read_byte()!; + $endif + + if (val == '\n') return ""; + @pool(using) + { + DString str = dstring::tnew_with_capacity(256); + if (val != '\r') str.append(val); + while (1) + { + $if $is_stream: + char! c = func((void*)stream); + $else + char! c = stream.read_byte(); + $endif + if (catch err = c) + { + if (err == IoError.EOF) break; + return err?; + } + if (c == '\r') continue; + if (c == '\n') break; + str.append_char(c); + } + return str.copy_str(using); + }; +} + +macro String! treadline(stream = io::stdin()) => readline(stream, mem::temp()) @inline; + +/** + * @require @is_outstream(out) "The output must implement OutStream" + */ +macro usz! fprint(out, x) +{ + var $Type = $typeof(x); + $switch ($Type) + $case String: + return out.write(x); + $case ZString: + return out.write(x.str_view()); + $case DString: + return out.write(x.str_view()); + $default: + $if $assignable(x, String): + return out.write((String)x); + $else + return printf("%s", x, .out = out); + $endif + $endswitch +} + +fn usz! fprintf(OutStream* out, String format, args...) +{ + Formatter formatter; + formatter.init(&out_putstream_fn, &out); + return formatter.vprintf(format, args); +} + +fn usz! fprintfn(OutStream* out, String format, args...) @maydiscard +{ + Formatter formatter; + formatter.init(&out_putstream_fn, &out); + usz len = formatter.vprintf(format, args)!; + out.write_byte('\n')!; + if (&out.flush) out.flush()!; + return len + 1; +} + +/** + * @require @is_outstream(out) "The output must implement OutStream" + */ +macro usz! fprintn(out, x = "") +{ + usz len = fprint(out, x)!; + out.write_byte('\n')!; + $switch + $case $typeof(out).typeid == OutStream*.typeid: + if (&out.flush) out.flush()!; + $case $defined(out.flush): + out.flush()!; + $endswitch + return len + 1; +} + macro void print(x) { - (void)print_gen(stdout(), x); + (void)fprint(io::stdout(), x); } macro void printn(x = "") { - (void)printn_gen(stdout(), x); + (void)fprintn(io::stdout(), x); +} + +macro void eprint(x) +{ + (void)fprint(io::stderr(), x); +} + +macro void eprintn(x) +{ + (void)fprintn(io::stderr(), x); +} + + +fn void! out_putstream_fn(void* data, char c) @private +{ + OutStream** stream = data; + return (*stream).write_byte(c); } fn void! out_putchar_fn(void* data @unused, char c) @private @@ -60,7 +173,6 @@ fn void! out_putchar_fn(void* data @unused, char c) @private libc::putchar(c); } - fn usz! printf(String format, args...) @maydiscard { Formatter formatter; @@ -74,9 +186,30 @@ fn usz! printfn(String format, args...) @maydiscard formatter.init(&out_putchar_fn); usz len = formatter.vprintf(format, args)!; putchar('\n'); + io::stdout().flush()!; return len + 1; } +fn usz! eprintf(String format, args...) @maydiscard +{ + Formatter formatter; + OutStream* stream = stderr(); + formatter.init(&out_putstream_fn, &stream); + return formatter.vprintf(format, args); +} + + +fn usz! eprintfn(String format, args...) @maydiscard +{ + Formatter formatter; + OutStream* stream = stderr(); + formatter.init(&out_putstream_fn, &stream); + usz len = formatter.vprintf(format, args)! + 1; + stderr().write_byte('\n')!; + stderr().flush()!; + return len; +} + fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard { Formatter formatter; @@ -112,21 +245,21 @@ fn void putchar(char c) @inline fn File* stdout() { static File file; - if (!file.fns) file = file::from_handle(libc::stdout()); + if (!file.file) file = file::from_handle(libc::stdout()); return &file; } fn File* stderr() { static File file; - if (!file.fns) file = file::from_handle(libc::stderr()); + if (!file.file) file = file::from_handle(libc::stderr()); return &file; } fn File* stdin() { static File file; - if (!file.fns) file = file::from_handle(libc::stdin()); + if (!file.file) file = file::from_handle(libc::stdin()); return &file; } diff --git a/lib/std/io/stream.c3 b/lib/std/io/stream.c3 index 2dae7c115..670a0d4a8 100644 --- a/lib/std/io/stream.c3 +++ b/lib/std/io/stream.c3 @@ -1,377 +1,166 @@ module std::io; -def CloseStreamFn = fn void!(Stream*); -def FlushStreamFn = fn void!(Stream*); -def SeekStreamFn = fn usz!(Stream*, isz offset, Seek seek); -def LenStreamFn = fn usz(Stream*); -def AvailableStreamFn = fn usz!(Stream*); -def ReadStreamFn = fn usz!(Stream*, char[] bytes); -def ReadFromStreamFn = fn usz!(Stream*, Stream*); -def ReadByteStreamFn = fn char!(Stream*); -def PushbackByteStreamFn = fn void!(Stream*); -def WriteStreamFn = fn usz!(Stream*, char[] bytes); -def WriteToStreamFn = fn usz!(Stream*, Stream* out); -def WriteByteStreamFn = fn void!(Stream*, char c); -def DestroyStreamFn = fn void!(Stream*); - -struct StreamInterface -{ - CloseStreamFn close_fn; - FlushStreamFn flush_fn; - SeekStreamFn seek_fn; - LenStreamFn len_fn; - AvailableStreamFn available_fn; - ReadStreamFn read_fn; - ReadFromStreamFn read_stream_fn; - ReadByteStreamFn read_byte_fn; - PushbackByteStreamFn pushback_byte_fn; - WriteStreamFn write_fn; - WriteToStreamFn write_stream_fn; - WriteByteStreamFn write_byte_fn; - DestroyStreamFn destroy_fn; -} - -struct Stream -{ - StreamInterface* fns; -} - -struct StreamWrapper -{ - inline Stream stream; - void* data; -} - -fn bool Stream.supports_flush(&s) @inline => (bool)s.fns.flush_fn; -fn bool Stream.supports_seek(&s) @inline => (bool)s.fns.seek_fn; -fn bool Stream.supports_available(&s) @inline => s.fns.available_fn || s.fns.seek_fn; -fn bool Stream.supports_len(&s) @inline => s.fns.len_fn || s.fns.seek_fn; -fn bool Stream.supports_read(&s) @inline => s.fns.read_fn || s.fns.read_byte_fn; -fn bool Stream.supports_read_from(&s) @inline => (bool)s.fns.read_stream_fn; -fn bool Stream.supports_write_to(&s) @inline => (bool)s.fns.write_stream_fn; -fn bool Stream.supports_pushback_byte(&s) @inline => s.fns.pushback_byte_fn || s.fns.seek_fn; -fn bool Stream.supports_write(&s) @inline => s.fns.write_fn || s.fns.write_byte_fn; -fn bool Stream.supports_read_byte(&s) @inline => (bool)s.fns.read_byte_fn; -fn bool Stream.supports_write_byte(&s) @inline => (bool)s.fns.write_byte_fn; - -fn void! Stream.destroy(&self) @inline @maydiscard +interface InStream { - if (self.fns.destroy_fn) return self.fns.destroy_fn(self); - return self.close(); + fn void! close() @optional; + fn usz! seek(isz offset, Seek seek) @optional; + fn usz len() @optional; + fn usz! available() @optional; + fn usz! read(char[] buffer); + fn char! read_byte(); + fn usz! write_to(OutStream* out) @optional; + fn void! pushback_byte() @optional; } -fn void! Stream.close(&self) @inline @maydiscard -{ - if (CloseStreamFn func = self.fns.close_fn) return func(self); -} -fn usz! Stream.seek(&self, isz offset, Seek seek) @inline +interface OutStream { - if (SeekStreamFn func = self.fns.seek_fn) return func(self, offset, seek); - return IoError.NOT_SEEKABLE?; + fn void! destroy() @optional; + fn void! close() @optional; + fn void! flush() @optional; + fn usz! write(char[] bytes); + fn void! write_byte(char c); + fn usz! read_to(InStream* in) @optional; } -fn usz! Stream.available(&self) @inline +fn usz! available(InStream* s) { - if (AvailableStreamFn func = self.fns.available_fn) return func(self); - if (SeekStreamFn func = self.fns.seek_fn) + if (&s.available) return s.available(); + if (&s.seek) { - usz curr = func(self, 0, Seek.CURSOR)!; - usz len = func(self, 0, Seek.END)!; - func(self, curr, Seek.SET)!; + usz curr = s.seek(0, Seek.CURSOR)!; + usz len = s.seek(0, Seek.END)!; + s.seek(curr, Seek.SET)!; return len - curr; } - return IoError.NOT_SEEKABLE?; -} - -fn usz! Stream.read_any(&self, any* ref) -{ - return self.read_all(ref.ptr[:ref.type.sizeof]); -} - -/** - * @param ref "the object to write." - * @require ref.ptr != null - * @ensure return == ref.type.sizeof - */ -fn usz! Stream.write_any(&self, any* ref) -{ - return self.write_all(ref.ptr[:ref.type.sizeof]); -} - -fn usz! Stream.read(&self, char[] buffer) -{ - if (ReadStreamFn func = self.fns.read_fn) return func(self, buffer); - if (ReadByteStreamFn func = self.fns.read_byte_fn) - { - usz len = 0; - foreach (&cptr : buffer) - { - char! c = func(self); - if (catch err = c) - { - case IoError.EOF: return len; - default: return err?; - } - *cptr = c; - len++; - } - } - return IoError.UNSUPPORTED_OPERATION?; -} - -macro usz! Stream.print(&self, x) @maydiscard -{ - return print_gen(self, x); + return 0; } -macro usz! print_gen(self, x) +macro bool @is_instream(#expr) { - var $Type = $typeof(x); - $switch ($Type) - $case String: - return self.write(x); - $case ZString: - return self.write(x.str_view()); - $case DString: - return self.write(x.str_view()); - $default: - $if $defined((String)x): - return self.write((String)x); - $else - return printf("%s", x); - $endif - $endswitch -} - -macro usz! printn_gen(self, x) @maydiscard -{ - usz len = print_gen(self, x)!; - self.write_byte('\n')!; - self.flush()!; - return len + 1; + return $assignable(#expr, InStream*); } -macro usz! Stream.printn(&self, x) @maydiscard +macro bool @is_outstream(#expr) { - return printn_gen(self, x); -} - -fn usz! Stream.printf(&self, String format, args...) @maydiscard -{ - Formatter formatter; - if (WriteByteStreamFn func_byte = self.fns.write_byte_fn) - { - formatter.init((OutputFn)func_byte, self); - } - else if (WriteStreamFn func = self.fns.write_fn) - { - formatter.init((OutputFn)&Stream.write_byte, self); - } - else - { - return IoError.UNSUPPORTED_OPERATION?; - } - return formatter.vprintf(format, args)!; + return $assignable(#expr, OutStream*); } -fn usz! Stream.printfn(&self, String format, args...) @maydiscard @inline +/** + * @param [&out] ref + * @require @is_instream(stream) + **/ +macro usz! read_any(stream, any* ref) { - Formatter formatter; - if (WriteByteStreamFn func_byte = self.fns.write_byte_fn) - { - formatter.init((OutputFn)func_byte, self); - } - else if (WriteStreamFn func = self.fns.write_fn) - { - formatter.init((OutputFn)&Stream.write_byte, self); - } - else - { - return IoError.UNSUPPORTED_OPERATION?; - } - usz len = formatter.vprintf(format, args)!; - self.write_byte('\n')!; - self.flush()!; - return len + 1; + return read_all(stream, ((char*)ref)[:ref.type.sizeof]); } -fn char! Stream.read_byte(&self) @inline +/** + * @param [&in] ref "the object to write." + * @require @is_outstream(stream) + * @ensure return == ref.type.sizeof + */ +macro usz! write_any(stream, any* ref) { - if (ReadByteStreamFn func = self.fns.read_byte_fn) return func(self); - if (ReadStreamFn func = self.fns.read_fn) - { - char[1] buffer; - usz read = func(self, &buffer)!; - if (read != 1) return IoError.EOF?; - return buffer[0]; - } - return IoError.UNSUPPORTED_OPERATION?; + return write_all(stream, ((char*)ref)[:ref.type.sizeof]); } -fn usz! Stream.read_all(&self, char[] buffer) @inline +/** + * @require @is_instream(stream) + */ +macro usz! read_all(stream, char[] buffer) { - usz n = self.read(buffer)!; + if (buffer.len == 0) return 0; + usz n = stream.read(buffer)!; if (n != buffer.len) return IoError.UNEXPECTED_EOF?; return n; } -fn usz! Stream.write_all(&self, char[] buffer) @inline +/** + * @require @is_outstream(stream) + */ +macro usz! write_all(stream, char[] buffer) { - usz n = self.write(buffer)!; + if (buffer.len == 0) return 0; + usz n = stream.write(buffer)!; if (n != buffer.len) return IoError.INCOMPLETE_WRITE?; return n; } - -fn String! Stream.treadline(&self) => self.readline(mem::temp()) @inline; - -fn String! Stream.readline(&self, Allocator* using = mem::heap()) +macro usz! @read_using_read_byte(&s, char[] buffer) { - if (ReadByteStreamFn func = self.fns.read_byte_fn) + usz len = 0; + foreach (&cptr : buffer) { - char val = func(self)!; - if (val == '\n') return ""; - @pool(using) - { - DString str = dstring::tnew_with_capacity(256); - if (val != '\r') str.append(val); - while (1) - { - char! c = func(self); - if (catch err = c) - { - if (err == IoError.EOF) break; - return err?; - } - if (c == '\r') continue; - if (c == '\n') break; - str.append_char(c); - } - return str.copy_str(using); - }; - } - if (ReadStreamFn func = self.fns.read_fn) - { - char[1] buff; - if (func(self, &buff)! == 0) return IoError.EOF?; - char val = buff[0]; - if (val == '\n') return ""; - @pool(using) - { - DString str = dstring::tnew_with_capacity(256); - if (val != '\r') str.append(val); - while (1) - { - usz! read = func(self, &buff); - if (catch err = read) - { - if (err == IoError.EOF) break; - return err?; - } - if (!read) break; - char c = buff[0]; - if (c == '\r') continue; - if (c == '\n') break; - str.append_char(c); - } - return str.copy_str(using); - }; + char! c = s.read_byte(); + if (catch err = c) + { + case IoError.EOF: return len; + default: return err?; + } + *cptr = c; + len++; } - return IoError.UNSUPPORTED_OPERATION?; - - + return len; } -fn usz! Stream.write(&self, char[] bytes) @inline +macro void! @write_byte_using_write(&s, char c) { - if (WriteStreamFn func = self.fns.write_fn) return func(self, bytes); - if (WriteByteStreamFn func = self.fns.write_byte_fn) - { - foreach (c : bytes) func(self, c)!; - return bytes.len; - } - return IoError.UNSUPPORTED_OPERATION?; + char[1] buff = { c }; + (*s).write(&buff)!; } -fn void! Stream.write_byte(&self, char b) @inline -{ - if (WriteByteStreamFn func = self.fns.write_byte_fn) return func(self, b); - return IoError.UNSUPPORTED_OPERATION?; -} -fn usz! Stream.write_to(&self, Stream* to) @inline +macro char! @read_byte_using_read(&s) { - if (WriteToStreamFn func = self.fns.write_stream_fn) return func(self, to); - return IoError.UNSUPPORTED_OPERATION?; + char[1] buffer; + usz read = (*s).read(&buffer)!; + if (read != 1) return IoError.EOF?; + return buffer[0]; } -fn usz! Stream.read_from(&self, Stream* from) @inline -{ - if (ReadFromStreamFn func = self.fns.read_stream_fn) return func(self, from); - return IoError.UNSUPPORTED_OPERATION?; -} +def ReadByteFn = fn char!(); -fn void! Stream.flush(&self) @inline @maydiscard -{ - if (FlushStreamFn func = self.fns.flush_fn) return func(self); - return IoError.UNSUPPORTED_OPERATION?; -} -fn usz! Stream.len(&self) @inline +macro usz! @write_using_write_byte(&s, char[] bytes) { - if (LenStreamFn func = self.fns.len_fn) return func(self); - if (SeekStreamFn func = self.fns.seek_fn) - { - usz curr = func(self, 0, Seek.CURSOR)!; - usz len = func(self, 0, Seek.END)!; - func(self, curr, Seek.SET)!; - return len; - } - return IoError.NOT_SEEKABLE?; + foreach (c : bytes) s.write_byte(self, c)!; + return bytes.len; } -fn void! Stream.pushback_byte(&self) @inline +macro void! @pushback_using_seek(&s) { - if (PushbackByteStreamFn func = self.fns.pushback_byte_fn) return func(self); - if (SeekStreamFn func = self.fns.seek_fn) - { - func(self, -1, CURSOR)!; - return; - } - return IoError.UNSUPPORTED_OPERATION?; + s.seek(-1, CURSOR)!; } -fn void! Stream.write_string(&self, String str) @inline => (void)(self.write((char[])str)!); - -fn usz! Stream.copy_to(&self, Stream* dst, char[] buffer = {}) +fn usz! copy_to(InStream* in, OutStream* dst, char[] buffer = {}) { - if (buffer.len) return copy_through_buffer(self, dst, buffer); - if (WriteToStreamFn func = self.fns.write_stream_fn) return func(self, dst); - if (ReadFromStreamFn func = self.fns.read_stream_fn) return func(dst, self); + if (buffer.len) return copy_through_buffer(in, dst, buffer); + if (&in.write_to) return in.write_to(dst); + if (&dst.read_to) return dst.read_to(in); $switch (env::MEMORY_ENV) $case NORMAL: @pool() { - return copy_through_buffer(self, dst, tmalloc(char, 4096)); + return copy_through_buffer(in, dst, tmalloc(char, 4096)); }; $case SMALL: @pool() { - return copy_through_buffer(self, dst, tmalloc(char, 1024)); + return copy_through_buffer(in, dst, tmalloc(char, 1024)); }; $case TINY: $case NONE: - return copy_through_buffer(self, dst, &&(char[256]{})); + return copy_through_buffer(in, dst, &&(char[256]{})); $endswitch } -macro usz! copy_through_buffer(Stream *self, Stream* dst, char[] buffer) @local +macro usz! copy_through_buffer(InStream *in, OutStream* dst, char[] buffer) @local { usz total_copied; while (true) { - usz! len = self.read(buffer); + usz! len = in.read(buffer); if (catch err = len) { case IoError.EOF: return total_copied; @@ -387,9 +176,10 @@ macro usz! copy_through_buffer(Stream *self, Stream* dst, char[] buffer) @local const char[*] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 }; /** + * @require @is_instream(stream) * @require $typeof(x_ptr).kindof == POINTER && $typeof(x_ptr).inner.kindof.is_int() **/ -macro usz! Stream.read_varint(&self, x_ptr) +macro usz! read_varint(stream, x_ptr) { var $Type = $typefrom($typeof(x_ptr).inner); const MAX = MAX_VARS[$Type.sizeof]; @@ -398,7 +188,7 @@ macro usz! Stream.read_varint(&self, x_ptr) usz n; for (usz i = 0; i < MAX; i++) { - char! c = self.read_byte(); + char! c = stream.read_byte(); if (catch err = c) { case IoError.EOF: @@ -422,11 +212,11 @@ macro usz! Stream.read_varint(&self, x_ptr) } return MathError.OVERFLOW?; } - /** + * @require @is_outstream(stream) * @require $typeof(x).kindof.is_int() **/ -macro usz! Stream.write_varint(&self, x) +macro usz! write_varint(stream, x) { var $Type = $typeof(x); const MAX = MAX_VARS[$Type.sizeof]; @@ -439,5 +229,5 @@ macro usz! Stream.write_varint(&self, x) i++; } buffer[i] = (char)x; - return self.write_all(buffer[:i + 1]); + return write_all(stream, buffer[:i + 1]); } \ No newline at end of file diff --git a/lib/std/io/stream/buffer.c3 b/lib/std/io/stream/buffer.c3 index e56d13536..2f26d2984 100644 --- a/lib/std/io/stream/buffer.c3 +++ b/lib/std/io/stream/buffer.c3 @@ -1,9 +1,8 @@ module std::io; -struct ReadBuffer +struct ReadBuffer (InStream) { - inline Stream stream; - Stream* wrapped_stream; + InStream* wrapped_stream; char[] bytes; usz read_idx; usz write_idx; @@ -15,24 +14,22 @@ struct ReadBuffer * @require bytes.len > 0 * @require self.bytes.len == 0 "Init may not run on already initialized data" **/ -fn ReadBuffer* ReadBuffer.init(&self, Stream* wrapped_stream, char[] bytes) +fn ReadBuffer* ReadBuffer.init(&self, InStream* wrapped_stream, char[] bytes) { - *self = { .stream.fns = &READBUFFER_INTERFACE, .wrapped_stream = wrapped_stream, .bytes = bytes }; + *self = { .wrapped_stream = wrapped_stream, .bytes = bytes }; return self; } - - -const StreamInterface READBUFFER_INTERFACE = { - .read_fn = (ReadStreamFn)&ReadBuffer.read, - .read_byte_fn = (ReadByteStreamFn)&ReadBuffer.read_byte, -}; - fn String ReadBuffer.str_view(&self) @inline { return (String)self.bytes[self.read_idx:self.write_idx - self.read_idx]; } -fn usz! ReadBuffer.read(&self, char[] bytes) +fn void! ReadBuffer.close(&self) @dynamic +{ + if (&self.wrapped_stream.close) self.wrapped_stream.close()!; +} + +fn usz! ReadBuffer.read(&self, char[] bytes) @dynamic { if (self.read_idx == self.write_idx) { @@ -49,7 +46,7 @@ fn usz! ReadBuffer.read(&self, char[] bytes) return n; } -fn char! ReadBuffer.read_byte(&self) +fn char! ReadBuffer.read_byte(&self) @dynamic { if (self.read_idx == self.write_idx) self.refill()!; if (self.read_idx == self.write_idx) return IoError.EOF?; @@ -64,10 +61,9 @@ fn void! ReadBuffer.refill(&self) @local @inline self.write_idx = self.wrapped_stream.read(self.bytes)!; } -struct WriteBuffer +struct WriteBuffer (OutStream) { - inline Stream stream; - Stream* wrapped_stream; + OutStream* wrapped_stream; char[] bytes; usz index; } @@ -78,30 +74,29 @@ struct WriteBuffer * @require bytes.len > 0 "Non-empty buffer required" * @require self.bytes.len == 0 "Init may not run on already initialized data" **/ -fn WriteBuffer* WriteBuffer.init(&self, Stream* wrapped_stream, char[] bytes) +fn WriteBuffer* WriteBuffer.init(&self, OutStream* wrapped_stream, char[] bytes) { - *self = { .stream.fns = &WRITEBUFFER_INTERFACE, .wrapped_stream = wrapped_stream, .bytes = bytes }; + *self = { .wrapped_stream = wrapped_stream, .bytes = bytes }; return self; } -const StreamInterface WRITEBUFFER_INTERFACE = { - .flush_fn = (FlushStreamFn)&WriteBuffer.flush, - .write_fn = (WriteStreamFn)&WriteBuffer.write, - .write_byte_fn = (WriteByteStreamFn)&WriteBuffer.write_byte, -}; - fn String WriteBuffer.str_view(&self) @inline { return (String)self.bytes[:self.index]; } -fn void! WriteBuffer.flush(&self) +fn void! WriteBuffer.close(&self) @dynamic +{ + if (&self.wrapped_stream.close) return self.wrapped_stream.close(); +} + +fn void! WriteBuffer.flush(&self) @dynamic { self.write_pending()!; - if (self.wrapped_stream.supports_flush()) self.wrapped_stream.flush()!; + if (&self.wrapped_stream.flush) self.wrapped_stream.flush()!; } -fn usz! WriteBuffer.write(&self, char[] bytes) +fn usz! WriteBuffer.write(&self, char[] bytes) @dynamic { usz n = self.bytes.len - self.index; if (bytes.len < n) @@ -123,7 +118,7 @@ fn usz! WriteBuffer.write(&self, char[] bytes) return bytes.len; } -fn void! WriteBuffer.write_byte(&self, char c) +fn void! WriteBuffer.write_byte(&self, char c) @dynamic { usz n = self.bytes.len - self.index; if (n == 0) self.write_pending()!; diff --git a/lib/std/io/stream/bytebuffer.c3 b/lib/std/io/stream/bytebuffer.c3 index d6b510cc8..d8a47412a 100644 --- a/lib/std/io/stream/bytebuffer.c3 +++ b/lib/std/io/stream/bytebuffer.c3 @@ -1,9 +1,8 @@ module std::io; import std::math; -struct ByteBuffer +struct ByteBuffer (InStream, OutStream) { - inline Stream stream; Allocator* allocator; usz max_read; char[] bytes; @@ -19,7 +18,7 @@ struct ByteBuffer **/ fn ByteBuffer*! ByteBuffer.init(&self, usz max_read, usz initial_capacity = 16, Allocator* using = mem::heap()) { - *self = { .stream.fns = &BYTEBUFFER_INTERFACE, .allocator = using, .max_read = max_read }; + *self = { .allocator = using, .max_read = max_read }; initial_capacity = max(initial_capacity, 16); self.grow(initial_capacity)!; return self; @@ -36,7 +35,7 @@ fn ByteBuffer*! ByteBuffer.tinit(&self, usz max_read, usz initial_capacity = 16) **/ fn ByteBuffer*! ByteBuffer.init_with_buffer(&self, char[] buf) { - *self = { .stream.fns = &BYTEBUFFER_INTERFACE, .max_read = buf.len, .bytes = buf }; + *self = { .max_read = buf.len, .bytes = buf }; return self; } @@ -46,16 +45,7 @@ fn void ByteBuffer.free(&self) *self = {}; } -const StreamInterface BYTEBUFFER_INTERFACE = { - .write_fn = (WriteStreamFn)&ByteBuffer.write, - .write_byte_fn = (WriteByteStreamFn)&ByteBuffer.write_byte, - .read_fn = (ReadStreamFn)&ByteBuffer.read, - .read_byte_fn = (ReadByteStreamFn)&ByteBuffer.read_byte, - .pushback_byte_fn = (PushbackByteStreamFn)&ByteBuffer.pushback_byte, - .available_fn = (AvailableStreamFn)&ByteBuffer.available -}; - -fn usz! ByteBuffer.write(&self, char[] bytes) +fn usz! ByteBuffer.write(&self, char[] bytes) @dynamic { usz cap = self.bytes.len - self.write_idx; if (cap < bytes.len) self.grow(bytes.len)!; @@ -64,7 +54,7 @@ fn usz! ByteBuffer.write(&self, char[] bytes) return bytes.len; } -fn void! ByteBuffer.write_byte(&self, char c) +fn void! ByteBuffer.write_byte(&self, char c) @dynamic { usz cap = self.bytes.len - self.write_idx; if (cap == 0) self.grow(1)!; @@ -72,7 +62,7 @@ fn void! ByteBuffer.write_byte(&self, char c) self.write_idx++; } -fn usz! ByteBuffer.read(&self, char[] bytes) +fn usz! ByteBuffer.read(&self, char[] bytes) @dynamic { usz readable = self.write_idx - self.read_idx; if (readable == 0) @@ -88,7 +78,7 @@ fn usz! ByteBuffer.read(&self, char[] bytes) return n; } -fn char! ByteBuffer.read_byte(&self) +fn char! ByteBuffer.read_byte(&self) @dynamic { usz readable = self.write_idx - self.read_idx; if (readable == 0) @@ -106,7 +96,7 @@ fn char! ByteBuffer.read_byte(&self) /* * Only the last byte of a successful read can be pushed back. */ -fn void! ByteBuffer.pushback_byte(&self) +fn void! ByteBuffer.pushback_byte(&self) @dynamic { if (!self.has_last) return IoError.EOF?; assert(self.read_idx > 0); @@ -114,13 +104,14 @@ fn void! ByteBuffer.pushback_byte(&self) self.has_last = false; } -fn void! ByteBuffer.seek(&self, isz offset, Seek seek) +fn usz! ByteBuffer.seek(&self, isz offset, Seek seek) @dynamic { switch (seek) { case SET: if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?; self.read_idx = offset; + return offset; case CURSOR: if ((offset < 0 && self.read_idx < -offset) || (offset > 0 && self.read_idx + offset > self.write_idx)) return IoError.INVALID_POSITION?; @@ -129,9 +120,10 @@ fn void! ByteBuffer.seek(&self, isz offset, Seek seek) if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?; self.read_idx = self.write_idx - offset; } + return self.read_idx; } -fn usz! ByteBuffer.available(&self) @inline +fn usz! ByteBuffer.available(&self) @inline @dynamic { return self.write_idx - self.read_idx; } diff --git a/lib/std/io/stream/bytereader.c3 b/lib/std/io/stream/bytereader.c3 index c31979167..05c93a7b3 100644 --- a/lib/std/io/stream/bytereader.c3 +++ b/lib/std/io/stream/bytereader.c3 @@ -1,19 +1,23 @@ module std::io; -struct ByteReader +struct ByteReader (InStream) { - inline Stream stream; char[] bytes; usz index; } +fn usz ByteReader.len(&self) @dynamic +{ + return self.bytes.len; +} + fn ByteReader* ByteReader.init(&self, char[] bytes) { - *self = { .stream = { &BYTEREADER_INTERFACE }, .bytes = bytes }; + *self = { .bytes = bytes }; return self; } -fn usz! ByteReader.read(&self, char[] bytes) +fn usz! ByteReader.read(&self, char[] bytes) @dynamic { if (self.index >= self.bytes.len) return IoError.EOF?; usz len = min(self.bytes.len - self.index, bytes.len); @@ -23,19 +27,19 @@ fn usz! ByteReader.read(&self, char[] bytes) return len; } -fn char! ByteReader.read_byte(&self) +fn char! ByteReader.read_byte(&self) @dynamic { if (self.index >= self.bytes.len) return IoError.EOF?; return self.bytes[self.index++]; } -fn void! ByteReader.pushback_byte(&self) +fn void! ByteReader.pushback_byte(&self) @dynamic { if (!self.index) return IoError.INVALID_PUSHBACK?; self.index--; } -fn usz! ByteReader.seek(&self, isz offset, Seek seek) +fn usz! ByteReader.seek(&self, isz offset, Seek seek) @dynamic { isz new_index; switch (seek) @@ -49,7 +53,7 @@ fn usz! ByteReader.seek(&self, isz offset, Seek seek) return new_index; } -fn usz! ByteReader.write_stream(&self, Stream* writer) +fn usz! ByteReader.write_to(&self, OutStream* writer) @dynamic { if (self.index >= self.bytes.len) return 0; usz written = writer.write(self.bytes[self.index..])!; @@ -58,19 +62,7 @@ fn usz! ByteReader.write_stream(&self, Stream* writer) return written; } -fn usz ByteReader.available(&self) @inline +fn usz! ByteReader.available(&self) @inline @dynamic { return max(0, self.bytes.len - self.index); -} - -const StreamInterface BYTEREADER_INTERFACE = { - .len_fn = fn (Stream* self) => ((ByteReader*)self).bytes.len, - .read_fn = (ReadStreamFn)&ByteReader.read, - .read_byte_fn = (ReadByteStreamFn)&ByteReader.read_byte, - .pushback_byte_fn = (PushbackByteStreamFn)&ByteReader.pushback_byte, - .seek_fn = (SeekStreamFn)&ByteReader.seek, - .write_stream_fn = (WriteToStreamFn)&ByteReader.write_stream, - .available_fn = fn (Stream* self) => ((ByteReader*)self).available(), -}; - - +} \ No newline at end of file diff --git a/lib/std/io/stream/bytewriter.c3 b/lib/std/io/stream/bytewriter.c3 index 4fc94d83e..a4c488e17 100644 --- a/lib/std/io/stream/bytewriter.c3 +++ b/lib/std/io/stream/bytewriter.c3 @@ -1,8 +1,7 @@ module std::io; -struct ByteWriter +struct ByteWriter (OutStream) { - inline Stream stream; char[] bytes; usz index; Allocator* allocator; @@ -16,13 +15,13 @@ struct ByteWriter **/ fn ByteWriter* ByteWriter.init(&self, Allocator* using = mem::heap()) { - *self = { .stream.fns = &BYTEWRITER_INTERFACE, .bytes = {}, .allocator = using }; + *self = { .bytes = {}, .allocator = using }; return self; } fn ByteWriter* ByteWriter.init_buffer(&self, char[] data) { - *self = { .stream.fns = &BYTEWRITER_INTERFACE, .bytes = data, .allocator = null }; + *self = { .bytes = data, .allocator = null }; return self; } @@ -35,7 +34,7 @@ fn ByteWriter* ByteWriter.tinit(&self) return self.init(mem::temp()); } -fn void ByteWriter.destroy(&self) +fn void! ByteWriter.destroy(&self) @dynamic { if (!self.allocator) return; if (void* ptr = self.bytes.ptr) free(ptr, .using = self.allocator); @@ -57,7 +56,7 @@ fn void! ByteWriter.ensure_capacity(&self, usz len) @inline self.bytes = new_ptr[:new_capacity]; } -fn usz! ByteWriter.write(&self, char[] bytes) +fn usz! ByteWriter.write(&self, char[] bytes) @dynamic { self.ensure_capacity(self.index + bytes.len)!; mem::copy(&self.bytes[self.index], bytes.ptr, bytes.len); @@ -65,7 +64,7 @@ fn usz! ByteWriter.write(&self, char[] bytes) return bytes.len; } -fn void! ByteWriter.write_byte(&self, char c) +fn void! ByteWriter.write_byte(&self, char c) @dynamic { self.ensure_capacity(self.index + 1)!; self.bytes[self.index++] = c; @@ -75,10 +74,10 @@ fn void! ByteWriter.write_byte(&self, char c) * @param [&inout] self * @param reader **/ -fn usz! ByteWriter.read_from(&self, Stream* reader) +fn usz! ByteWriter.read_from(&self, InStream* reader) @dynamic { usz start_index = self.index; - if (reader.supports_available()) + if (&reader.available) { while (usz available = reader.available()!) { @@ -110,11 +109,3 @@ fn usz! ByteWriter.read_from(&self, Stream* reader) // Otherwise go another round } } - -const StreamInterface BYTEWRITER_INTERFACE = { - .destroy_fn = fn (s) => ((ByteWriter*)s).destroy(), - .len_fn = fn (s) => ((ByteWriter*)s).bytes.len, - .write_fn = (WriteStreamFn)&ByteWriter.write, - .write_byte_fn = (WriteByteStreamFn)&ByteWriter.write_byte, - .read_stream_fn = (ReadFromStreamFn)&ByteWriter.read_from, -}; diff --git a/lib/std/io/stream/dstringwriter.c3 b/lib/std/io/stream/dstringwriter.c3 deleted file mode 100644 index 025c20c89..000000000 --- a/lib/std/io/stream/dstringwriter.c3 +++ /dev/null @@ -1,38 +0,0 @@ -module std::io; - -struct DStringStream -{ - inline Stream stream; - DString* wrapped_string; -} - -fn DStringStream* DStringStream.init(&self, DString* wrapped_string) -{ - *self = { .stream.fns = &DSTRINGSTREAM_INTERFACE, .wrapped_string = wrapped_string }; - return self; -} - -fn usz DStringStream.len(&self) => self.wrapped_string.len(); - -fn usz! DStringStream.write(&self, char[] bytes) -{ - self.wrapped_string.append_chars((String)bytes); - return bytes.len; -} - -fn void! DStringStream.write_byte(&self, char c) -{ - self.wrapped_string.append_char(c); -} - -fn usz! DStringStream.read_from_stream(&self, Stream* reader) -{ - return self.wrapped_string.read_from_stream(reader); -} - -const StreamInterface DSTRINGSTREAM_INTERFACE = { - .len_fn = (LenStreamFn)&DStringStream.len, - .write_fn = (WriteStreamFn)&DStringStream.write, - .write_byte_fn = (WriteByteStreamFn)&DStringStream.write_byte, - .read_stream_fn = (ReadFromStreamFn)&DStringStream.read_from_stream, -}; \ No newline at end of file diff --git a/lib/std/io/stream/limitreader.c3 b/lib/std/io/stream/limitreader.c3 index 1f9a6a895..c248a2f2d 100644 --- a/lib/std/io/stream/limitreader.c3 +++ b/lib/std/io/stream/limitreader.c3 @@ -1,9 +1,8 @@ module std::io; -struct LimitReader +struct LimitReader (InStream) { - inline Stream stream; - Stream* wrapped_stream; + InStream* wrapped_stream; usz limit; } @@ -11,13 +10,19 @@ struct LimitReader * @param [&inout] wrapped_stream "The stream to read from" * @param limit "The max limit to read" **/ -fn LimitReader* LimitReader.init(&self, Stream* wrapped_stream, usz limit) +fn LimitReader* LimitReader.init(&self, InStream* wrapped_stream, usz limit) { - *self = { .stream.fns = &LIMITREADER_INTERFACE, .wrapped_stream = wrapped_stream, .limit = limit }; + *self = { .wrapped_stream = wrapped_stream, .limit = limit }; return self; } -fn usz! LimitReader.read(&self, char[] bytes) +fn void! LimitReader.close(&self) @dynamic +{ + if (&self.wrapped_stream.close) return self.wrapped_stream.close(); +} + + +fn usz! LimitReader.read(&self, char[] bytes) @dynamic { if (self.limit == 0) return IoError.EOF?; usz m = min(bytes.len, self.limit); @@ -26,20 +31,14 @@ fn usz! LimitReader.read(&self, char[] bytes) return n; } -fn char! LimitReader.read_byte(&self) +fn char! LimitReader.read_byte(&self) @dynamic { if (self.limit == 0) return IoError.EOF?; defer try self.limit--; return self.wrapped_stream.read_byte(); } -fn usz LimitReader.available(&self) @inline +fn usz! LimitReader.available(&self) @inline @dynamic { return self.limit; -} - -const StreamInterface LIMITREADER_INTERFACE = { - .read_fn = (ReadStreamFn)&LimitReader.read, - .read_byte_fn = (ReadByteStreamFn)&LimitReader.read_byte, - .available_fn = fn(s) => ((LimitReader*)s).available(), -}; \ No newline at end of file +} \ No newline at end of file diff --git a/lib/std/io/stream/scanner.c3 b/lib/std/io/stream/scanner.c3 index dd524d4ca..4965091b0 100644 --- a/lib/std/io/stream/scanner.c3 +++ b/lib/std/io/stream/scanner.c3 @@ -1,9 +1,8 @@ module std::io; -struct Scanner +struct Scanner (InStream) { - inline Stream stream; - Stream* wrapped_stream; + InStream* wrapped_stream; char[] buf; usz pattern_idx; usz read_idx; @@ -16,23 +15,17 @@ struct Scanner * * @param [&in] stream "The stream to read data from." * @require buffer.len > 0 "Non-empty buffer required." - * @require stream.supports_read() "The stream must support read." - * **/ -fn void Scanner.init(&self, Stream* stream, char[] buffer) +fn void Scanner.init(&self, InStream* stream, char[] buffer) { - *self = { .stream.fns = &SCANNER_INTERFACE, .wrapped_stream = stream, .buf = buffer }; + *self = { .wrapped_stream = stream, .buf = buffer }; } -const StreamInterface SCANNER_INTERFACE = { - .read_fn = (ReadStreamFn)&Scanner.read, - .read_byte_fn = (ReadByteStreamFn)&Scanner.read_byte, -}; /** * Return and clear any remaining unscanned data. **/ -fn char[] Scanner.flush(&self) +fn char[] Scanner.flush(&self) @dynamic { assert(self.read_idx >= self.pattern_idx); usz n = self.read_idx - self.pattern_idx; @@ -42,6 +35,11 @@ fn char[] Scanner.flush(&self) return buf; } +fn void! Scanner.close(&self) @dynamic +{ + if (&self.wrapped_stream.close) return self.wrapped_stream.close(); +} + /** * Scan the stream for the next split character and return data up to the match. * @require pattern.len > 0 "Non-empty pattern required." @@ -102,7 +100,7 @@ macro usz! Scanner.refill(&self, buf) @private return n; } -fn usz! Scanner.read(&self, char[] bytes) +fn usz! Scanner.read(&self, char[] bytes) @dynamic { usz n; if (self.pattern_idx < self.read_idx) @@ -116,11 +114,11 @@ fn usz! Scanner.read(&self, char[] bytes) return n; } -fn char! Scanner.read_byte(&self) +fn char! Scanner.read_byte(&self) @dynamic { if (self.pattern_idx < self.read_idx) { return self.buf[self.pattern_idx++]; } return self.wrapped_stream.read_byte(); -} \ No newline at end of file +} diff --git a/lib/std/net/socket.c3 b/lib/std/net/socket.c3 index 64ed9fbcc..35f7ddd02 100644 --- a/lib/std/net/socket.c3 +++ b/lib/std/net/socket.c3 @@ -2,9 +2,8 @@ module std::net @if(os::SUPPORTS_INET); import std::io; import libc; -struct Socket +struct Socket (InStream, OutStream) { - inline Stream stream; NativeSocket sock; Socklen_t ai_addrlen; // TODO proper way to get the size of sockaddr_storage @@ -77,7 +76,7 @@ fn ulong! poll(Poll[] polls, Duration timeout) macro Socket new_socket(fd, ai) { - Socket sock = { .stream.fns = &SOCKETSTREAM_INTERFACE, .sock = fd, .ai_addrlen = ai.ai_addrlen }; + Socket sock = { .sock = fd, .ai_addrlen = ai.ai_addrlen }; assert(sock.ai_addr_storage.len >= ai.ai_addrlen, "storage %d < addrlen %d", sock.ai_addr_storage.len, ai.ai_addrlen); mem::copy(&sock.ai_addr_storage, (void*)ai.ai_addr, ai.ai_addrlen); return sock; @@ -93,12 +92,6 @@ enum SocketOption : char (CInt value) DONTROUTE (os::SO_DONTROUTE), } -const StreamInterface SOCKETSTREAM_INTERFACE = { - .read_fn = (ReadStreamFn)&Socket.read, - .write_fn = (WriteStreamFn)&Socket.write, - .close_fn = (CloseStreamFn)&Socket.close, -}; - fn bool! Socket.get_broadcast(&self) => self.get_option(BROADCAST); fn bool! Socket.get_keepalive(&self) => self.get_option(KEEPALIVE); fn bool! Socket.get_reuseaddr(&self) => self.get_option(REUSEADDR); @@ -126,21 +119,29 @@ fn bool! Socket.get_option(&self, SocketOption option) return (bool)flag; } -fn usz! Socket.read(&self, char[] bytes) +fn usz! Socket.read(&self, char[] bytes) @dynamic { isz n = libc::read((Fd)self.sock, bytes.ptr, bytes.len); if (n < 0) return NetError.READ_FAILED?; return (usz)n; } -fn usz! Socket.write(&self, char[] bytes) +fn char! Socket.read_byte(&self) @dynamic => io::@read_byte_using_read(self); + +fn usz! Socket.write(&self, char[] bytes) @dynamic { isz n = libc::write((Fd)self.sock, bytes.ptr, bytes.len); if (n < 0) return NetError.WRITE_FAILED?; return (usz)n; } -fn void! Socket.close(&self) @inline +fn void! Socket.write_byte(&self, char byte) @dynamic => io::@write_byte_using_write(self, byte); + +fn void! Socket.destroy(&self) @dynamic +{ + self.close()!; +} +fn void! Socket.close(&self) @inline @dynamic { self.sock.close()!; } diff --git a/resources/examples/contextfree/boolerr.c3 b/resources/examples/contextfree/boolerr.c3 index 728c80a48..119be3fae 100644 --- a/resources/examples/contextfree/boolerr.c3 +++ b/resources/examples/contextfree/boolerr.c3 @@ -22,7 +22,7 @@ struct StringData @private fn void Summary.print(Summary *s, File* out) { String title = s.title ? s.title.str_view() : "missing"; - out.printf("Summary({ .title = %s, .ok = %s})", title, s.ok); + (void)io::fprintf(out, "Summary({ .title = %s, .ok = %s})", title, s.ok); } fn bool contains(String haystack, String needle) diff --git a/resources/examples/load_world.c3 b/resources/examples/load_world.c3 index 281b853f2..09bd5f063 100644 --- a/resources/examples/load_world.c3 +++ b/resources/examples/load_world.c3 @@ -6,6 +6,6 @@ fn void! main() defer f.close()!!; while (!f.eof()) { - @pool() { io::printn(f.treadline()!); }; + @pool() { io::printn(io::treadline(&f)!); }; } } \ No newline at end of file diff --git a/resources/examples/plus_minus.c3 b/resources/examples/plus_minus.c3 index 7ead402c1..665b6cf7e 100644 --- a/resources/examples/plus_minus.c3 +++ b/resources/examples/plus_minus.c3 @@ -13,7 +13,7 @@ fault TokenResult fn void! main(String[] args) { // Grab a string from stdin - String s = io::stdin().readline()!; + String s = io::readline()!; // Delete it at scope end [defer] defer s.free(); diff --git a/resources/examples/process.c3 b/resources/examples/process.c3 index 2465653df..3306c3773 100644 --- a/resources/examples/process.c3 +++ b/resources/examples/process.c3 @@ -7,7 +7,7 @@ fn void! main() String command = env::WIN32 ? "dir" : "ls"; SubProcess x = process::create({ command }, { .search_user_path = true })!!; x.join()!; - Stream* stream = &&x.stdout(); + InStream* stream = &&x.stdout(); while (try char b = stream.read_byte()) { io::printf("%c", b); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index b3aac5151..1d7a583af 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2282,7 +2282,7 @@ void sema_erase_var(SemaContext *context, Decl *decl); void sema_erase_unwrapped(SemaContext *context, Decl *decl); bool sema_analyse_cond_expr(SemaContext *context, Expr *expr); -bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allow_optional); +bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allow_optional, bool *no_match_ref); MemberIndex sema_get_initializer_const_array_size(SemaContext *context, Expr *initializer, bool *may_be_array, bool *is_const_size); bool sema_analyse_expr(SemaContext *context, Expr *expr); bool sema_expr_check_discard(Expr *expr); diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 10f858201..ed2b7c941 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -433,10 +433,12 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) case EXPR_GROUP: case EXPR_STRINGIFY: case EXPR_CT_EVAL: - case EXPR_CT_DEFINED: case EXPR_CT_IS_CONST: MACRO_COPY_EXPR(expr->inner_expr); return expr; + case EXPR_CT_DEFINED: + MACRO_COPY_EXPR_LIST(expr->expression_list); + return expr; case EXPR_TYPEID_INFO: MACRO_COPY_EXPRID(expr->typeid_info_expr.parent); return expr; diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 343f3d38c..8d10c7aed 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -283,6 +283,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) assert(!exprid_is_constant_eval(expr->ternary_expr.cond, eval_kind)); return false; case EXPR_FORCE_UNWRAP: + return false; case EXPR_TYPEID: return eval_kind != CONSTANT_EVAL_CONSTANT_VALUE; case EXPR_UNARY: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 313b82161..86f237bff 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -5810,7 +5810,7 @@ static void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr assert(arg_count); Expr *any_val = args[0]; assert(any_val->expr_kind == EXPR_CAST); - args[0] = exprptr(any_val->cast_expr.expr)->unary_expr.expr; + args[0] = exprptr(any_val->cast_expr.expr); } if (!expr->call_expr.is_func_ref) diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index d44b13fed..8a8d31742 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1044,9 +1044,19 @@ static Expr *parse_ct_defined(ParseContext *c, Expr *left) assert(!left && "Unexpected left hand side"); Expr *defined = expr_new(EXPR_CT_DEFINED, c->span); advance(c); + Expr **list = NULL; CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); - ASSIGN_EXPR_OR_RET(defined->inner_expr, parse_expr(c), poisoned_expr); - CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); + while (!try_consume(c, TOKEN_RPAREN)) + { + ASSIGN_EXPR_OR_RET(Expr *expr, parse_expr(c), poisoned_expr); + vec_add(list, expr); + if (!try_consume(c, TOKEN_COMMA)) + { + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); + break; + } + } + defined->expression_list = list; return defined; } diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index a0583625f..bb80f66e6 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -79,7 +79,7 @@ static bool sema_check_builtin_args_const(Expr **args, size_t arg_len) static bool sema_check_alignment_expression(SemaContext *context, Expr *align) { - if (!sema_analyse_expr_rhs(context, type_usz, align, false)) return false; + if (!sema_analyse_expr_rhs(context, type_usz, align, false, NULL)) return false; if (!expr_is_const_int(align) || !int_fits(align->const_expr.ixx, TYPE_U64) || (!is_power_of_two(align->const_expr.ixx.i.low) && align->const_expr.ixx.i.low)) @@ -188,7 +188,7 @@ static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, b for (unsigned i = first_mask_value; i < arg_count; i++) { Expr *mask_val = args[i]; - if (!sema_analyse_expr_rhs(context, type_int, mask_val, false)) return false; + if (!sema_analyse_expr_rhs(context, type_int, mask_val, false, NULL)) return false; if (!expr_is_const_int(mask_val)) { RETURN_SEMA_ERROR(mask_val, "The swizzle positions must be compile time constants."); @@ -227,7 +227,7 @@ static bool sema_expr_analyse_compare_exchange(SemaContext *context, Expr *expr) for (int i = 1; i < 3; i++) { Expr *arg = args[i]; - if (!sema_analyse_expr_rhs(context, pointee == type_void ? NULL : pointee, arg, true)) return false; + if (!sema_analyse_expr_rhs(context, pointee == type_void ? NULL : pointee, arg, true, NULL)) return false; if (pointee == type_void) pointee = arg->type->canonical; if (!type_is_atomic(type_flatten(arg->type))) { @@ -238,7 +238,7 @@ static bool sema_expr_analyse_compare_exchange(SemaContext *context, Expr *expr) } for (int i = 3; i < 5; i++) { - if (!sema_analyse_expr_rhs(context, type_bool, args[i], false)) return false; + if (!sema_analyse_expr_rhs(context, type_bool, args[i], false, NULL)) return false; if (!expr_is_const(args[i])) { SEMA_ERROR(args[i], "Expected a constant boolean value."); @@ -247,7 +247,7 @@ static bool sema_expr_analyse_compare_exchange(SemaContext *context, Expr *expr) } for (int i = 5; i < 7; i++) { - if (!sema_analyse_expr_rhs(context, type_char, args[i], false)) return false; + if (!sema_analyse_expr_rhs(context, type_char, args[i], false, NULL)) return false; if (!is_valid_atomicity(args[i])) return false; } unsigned success = args[5]->const_expr.ixx.i.low; @@ -276,7 +276,7 @@ static bool sema_expr_analyse_syscall(SemaContext *context, Expr *expr) for (unsigned i = 0; i < arg_count; i++) { Expr *arg = args[i]; - if (!sema_analyse_expr_rhs(context, type_uptr, arg, true)) return false; + if (!sema_analyse_expr_rhs(context, type_uptr, arg, true, NULL)) return false; optional = optional || type_is_optional(arg->type); } switch (platform_target.arch) diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index d46449575..d82d9fa97 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1094,7 +1094,7 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, Expr *expr = param->var.init_expr; if (expr_is_const(expr)) { - if (!sema_analyse_expr_rhs(context, param->type, expr, true)) return decl_poison(param); + if (!sema_analyse_expr_rhs(context, param->type, expr, true, NULL)) return decl_poison(param); } } if (!sema_check_param_uniqueness_and_type(params, param, i, param_count)) return decl_poison(param); @@ -1266,7 +1266,7 @@ static inline bool sema_analyse_enum_param(SemaContext *context, Decl *param, bo { Expr *expr = param->var.init_expr; - if (!sema_analyse_expr_rhs(context, param->type, expr, true)) return false; + if (!sema_analyse_expr_rhs(context, param->type, expr, true, NULL)) return false; if (IS_OPTIONAL(expr)) { SEMA_ERROR(expr, "Default arguments may not be optionals."); @@ -1414,7 +1414,7 @@ static inline bool sema_analyse_enum(SemaContext *context, Decl *decl, bool *era { Expr *arg = args[j]; - if (!sema_analyse_expr_rhs(context, associated_values[j]->type, arg, false)) return false; + if (!sema_analyse_expr_rhs(context, associated_values[j]->type, arg, false, NULL)) return false; if (!expr_is_constant_eval(arg, CONSTANT_EVAL_GLOBAL_INIT)) { SEMA_ERROR(arg, "Expected a constant expression as parameter."); @@ -3111,7 +3111,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) decl->var.init_expr = init = expr_new(EXPR_POISONED, decl->span); expr_rewrite_to_const_zero(init, decl->type); } - if (!sema_analyse_expr_rhs(context, decl->type, init, false)) goto FAIL; + if (!sema_analyse_expr_rhs(context, decl->type, init, false, NULL)) goto FAIL; if (!expr_is_constant_eval(init, CONSTANT_EVAL_CONSTANT_VALUE)) { SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 78902acb6..33a2a8758 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -119,7 +119,8 @@ static inline bool sema_binary_analyse_arithmetic_subexpr(SemaContext *context, static inline bool sema_binary_analyse_ct_identifier_lvalue(SemaContext *context, Expr *expr); static bool sema_binary_check_unclear_op_precedence(Expr *left_side, Expr * main_expr, Expr *right_side); static bool sema_binary_analyse_ct_common_assign(SemaContext *context, Expr *expr, Expr *left); -static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, Expr *right, Type *left_type, Type *right_type, Expr *parent, const char *error_message); +static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, Expr *right, Type *left_type, Type *right_type, + Expr *parent, const char *error_message, bool allow_bool_vec); static bool sema_binary_is_unsigned_always_false_comparison(SemaContext *context, Expr *expr, Expr *left, Expr *right); static bool sema_binary_is_expr_lvalue(Expr *top_expr, Expr *expr); static void sema_binary_unify_voidptr(SemaContext *context, Expr *left, Expr *right, Type **left_type_ref, Type **right_type_ref); @@ -1095,7 +1096,7 @@ static inline bool sema_binary_analyse_arithmetic_subexpr(SemaContext *context, if (left_type == type_bool && right_type == type_bool) return true; } // 2. Perform promotion to a common type. - return sema_binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, error); + return sema_binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, error, bool_and_bitstruct_is_allowed); } @@ -1305,7 +1306,7 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, bool success; SCOPE_START new_context->original_inline_line = context->original_inline_line ? context->original_inline_line : init_expr->span.row; - success = sema_analyse_expr_rhs(new_context, param->type, arg, true); + success = sema_analyse_expr_rhs(new_context, param->type, arg, true, no_match_ref); SCOPE_END; sema_context_destroy(&default_context); if (!success) return false; @@ -1396,8 +1397,8 @@ INLINE bool sema_arg_is_pass_through_ref(Expr *expr) return decl->var.kind == VARDECL_PARAM_REF; } -static inline bool -sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee, bool *optional, bool *no_match_ref) +static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee, + bool *optional, bool *no_match_ref) { // 1. Check body arguments (for macro calls, or possibly broken ) if (!sema_call_check_invalid_body_arguments(context, call, &callee)) return false; @@ -1500,7 +1501,7 @@ sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee foreach(Expr*, varargs) { // 11e. A simple variadic value: - if (!sema_analyse_expr_rhs(context, variadic_type, val, true)) return false; + if (!sema_analyse_expr_rhs(context, variadic_type, val, true, no_match_ref)) return false; *optional |= IS_OPTIONAL(val); } } @@ -1578,7 +1579,7 @@ sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee break; case VARDECL_PARAM: // foo - if (!sema_analyse_expr_rhs(context, type, arg, true)) return false; + if (!sema_analyse_expr_rhs(context, type, arg, true, no_match_ref)) return false; if (type_no_optional(arg->type) == type_void) RETURN_SEMA_ERROR(arg, "A 'void' value cannot be passed as a parameter."); if (IS_OPTIONAL(arg)) *optional = true; if (type_is_invalid_storage_type(arg->type)) @@ -1601,7 +1602,7 @@ sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee case VARDECL_PARAM_CT: // $foo assert(callee.macro); - if (!sema_analyse_expr_rhs(context, type, arg, true)) return false; + if (!sema_analyse_expr_rhs(context, type, arg, true, no_match_ref)) return false; if (!expr_is_constant_eval(arg, CONSTANT_EVAL_CONSTANT_VALUE)) { SEMA_ERROR(arg, "A compile time parameter must always be a constant, did you mistake it for a normal paramter?"); @@ -2150,7 +2151,7 @@ static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *c for (unsigned i = 0; i < expressions; i++) { Expr *expr = args[i]; - if (!sema_analyse_expr_rhs(macro_context, params[i]->type, expr, false)) return false; + if (!sema_analyse_expr_rhs(macro_context, params[i]->type, expr, false, NULL)) return false; } AstId macro_defer = macro_context->active_scope.defer_last; @@ -2276,7 +2277,10 @@ static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr, bool struct_var = func_expr->access_expr.parent; if (decl->func_decl.signature.params[0]->type->type_kind == TYPE_POINTER) { - expr_insert_addr(struct_var); + if (!decl->func_decl.attr_interface_method) + { + expr_insert_addr(struct_var); + } } break; default: @@ -4602,7 +4606,7 @@ bool sema_expr_analyse_assign_right_side(SemaContext *context, Expr *expr, Type // 1. Evaluate right side to required type. bool to_optional = left_type && type_is_optional(left_type); - if (!sema_analyse_expr_rhs(context, left_type, right, is_unwrapped || to_optional)) return false; + if (!sema_analyse_expr_rhs(context, left_type, right, is_unwrapped || to_optional, NULL)) return false; if (IS_OPTIONAL(right) && !to_optional) { if (is_unwrapped) @@ -4966,10 +4970,11 @@ static bool sema_expr_analyse_add_sub_assign(SemaContext *context, Expr *expr, E } -static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, Expr *right, Type *left_type, Type *right_type, Expr *parent, const char *error_message) +static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, Expr *right, Type *left_type, Type *right_type, + Expr *parent, const char *error_message, bool allow_bool_vec) { Type *max = cast_numeric_arithmetic_promotion(type_find_max_type(left_type, right_type)); - if (!max || !type_underlying_is_numeric(max)) + if (!max || (!type_underlying_is_numeric(max) && !(allow_bool_vec && type_flat_is_bool_vector(max)))) { if (!error_message) { @@ -5206,12 +5211,12 @@ static bool sema_expr_analyse_sub(SemaContext *context, Expr *expr, Expr *left, // 7. Attempt arithmetic promotion, to promote both to a common type. if (!sema_binary_arithmetic_promotion(context, - left, - right, - left_type, - right_type, - expr, - "The subtraction %s - %s is not possible.")) + left, + right, + left_type, + right_type, + expr, + "The subtraction %s - %s is not possible.", false)) { return false; } @@ -5385,12 +5390,12 @@ static bool sema_expr_analyse_add(SemaContext *context, Expr *expr, Expr *left, assert(!cast_to_iptr); // 4. Do a binary arithmetic promotion if (!sema_binary_arithmetic_promotion(context, - left, - right, - left_type, - right_type, - expr, - "Cannot do the addition %s + %s.")) + left, + right, + left_type, + right_type, + expr, + "Cannot do the addition %s + %s.", false)) { return false; } @@ -7640,169 +7645,175 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr { if (expr->resolve_status == RESOLVE_DONE) return expr_ok(expr); - Expr *inner = expr->inner_expr; + Expr **list = expr->expression_list; bool success = true; bool failed = false; - Expr *main = inner; -RETRY:; - switch (main->expr_kind) + unsigned list_len = vec_size(list); + if (!list_len) RETURN_SEMA_ERROR(expr, "Expected at least one expression to test."); + for (unsigned i = 0; i < list_len; i++) { - case EXPR_ACCESS: - if (!sema_expr_analyse_access(context, main, &failed)) - { - if (!failed) return false; - success = false; - } - break; - case EXPR_IDENTIFIER: + Expr *main = list[i]; + RETRY: + switch (main->expr_kind) { - Decl *decl = sema_find_path_symbol(context, main->identifier_expr.ident, main->identifier_expr.path); - if (!decl_ok(decl)) return false; - success = decl != NULL; - break; - } - case EXPR_COMPILER_CONST: - success = sema_expr_analyse_compiler_const(context, main, false); - break; - case EXPR_BUILTIN: - success = sema_expr_analyse_builtin(context, main, false); - break; - case EXPR_UNARY: - main->resolve_status = RESOLVE_RUNNING; - if (!sema_expr_analyse_unary(context, main, &failed)) + case EXPR_ACCESS: + if (!sema_expr_analyse_access(context, main, &failed)) + { + if (!failed) return false; + success = false; + } + break; + case EXPR_IDENTIFIER: { - if (!failed) return false; - success = false; + Decl *decl = sema_find_path_symbol(context, main->identifier_expr.ident, main->identifier_expr.path); + if (!decl_ok(decl)) return false; + success = decl != NULL; + break; } - break; - case EXPR_TYPEINFO: - { - Type *type = sema_expr_check_type_exists(context, main->type_expr); - if (!type_ok(type)) return false; - success = type != NULL; - break; - } - case EXPR_CT_EVAL: - success = sema_ct_eval_expr(context, "$eval", main->inner_expr, false); - break; - case EXPR_HASH_IDENT: - { - Decl *decl = sema_resolve_symbol(context, main->hash_ident_expr.identifier, NULL, main->span); - if (!decl_ok(decl)) return false; - if (!decl) RETURN_SEMA_ERROR(inner, "No parameter '%s' found.", main->hash_ident_expr.identifier); - main = copy_expr_single(decl->var.init_expr); - goto RETRY; - } - case EXPR_SUBSCRIPT: - { - if (!sema_expr_analyse_subscript(context, main, SUBSCRIPT_EVAL_VALID)) + case EXPR_COMPILER_CONST: + success = sema_expr_analyse_compiler_const(context, main, false); + break; + case EXPR_BUILTIN: + success = sema_expr_analyse_builtin(context, main, false); + break; + case EXPR_UNARY: + main->resolve_status = RESOLVE_RUNNING; + if (!sema_expr_analyse_unary(context, main, &failed)) + { + if (!failed) return false; + success = false; + } + break; + case EXPR_TYPEINFO: { - return false; + Type *type = sema_expr_check_type_exists(context, main->type_expr); + if (!type_ok(type)) return false; + success = type != NULL; + break; } - if (!expr_ok(main)) + case EXPR_CT_EVAL: + success = sema_ct_eval_expr(context, "$eval", main->inner_expr, false); + break; + case EXPR_HASH_IDENT: { - success = false; + Decl *decl = sema_resolve_symbol(context, main->hash_ident_expr.identifier, NULL, main->span); + if (!decl_ok(decl)) return false; + if (!decl) RETURN_SEMA_ERROR(list[i], "No parameter '%s' found.", main->hash_ident_expr.identifier); + main = copy_expr_single(decl->var.init_expr); + goto RETRY; } - break; - } - case EXPR_CAST: - if (!sema_expr_analyse_cast(context, main, &failed)) + case EXPR_SUBSCRIPT: { - if (!failed) return false; - success = false; + if (!sema_expr_analyse_subscript(context, main, SUBSCRIPT_EVAL_VALID)) + { + return false; + } + if (!expr_ok(main)) + { + success = false; + } + break; } - break; - case EXPR_CT_IDENT: - { - Decl *decl = sema_resolve_symbol(context, main->ct_ident_expr.identifier, NULL, main->span); - if (!decl_ok(decl)) return false; - success = decl != NULL; - break; - } - case EXPR_CALL: - { - bool no_match; - if (!sema_expr_analyse_call(context, main, &no_match)) + case EXPR_CAST: + if (!sema_expr_analyse_cast(context, main, &failed)) + { + if (!failed) return false; + success = false; + } + break; + case EXPR_CT_IDENT: { - if (!no_match) return false; - success = false; + Decl *decl = sema_resolve_symbol(context, main->ct_ident_expr.identifier, NULL, main->span); + if (!decl_ok(decl)) return false; + success = decl != NULL; + break; } - break; - } - case EXPR_FORCE_UNWRAP: - if (!sema_analyse_expr(context, main->inner_expr)) return false; - success = IS_OPTIONAL(main->inner_expr); - break; - case EXPR_RETHROW: - if (!sema_analyse_expr(context, main->rethrow_expr.inner)) return false; - success = IS_OPTIONAL(main->rethrow_expr.inner); - break; - case EXPR_OPTIONAL: - if (!sema_expr_analyse_optional(context, main, &failed)) + case EXPR_CALL: { - if (!failed) return false; - success = false; + bool no_match; + if (!sema_expr_analyse_call(context, main, &no_match)) + { + if (!no_match) return false; + success = false; + } + break; } - break; - case EXPR_POISONED: - success = false; - break; - case EXPR_COND: - case EXPR_TEST_HOOK: - case EXPR_CATCH_UNWRAP: - case EXPR_DESIGNATOR: - case EXPR_BENCHMARK_HOOK: - case EXPR_TRY_UNWRAP: - case EXPR_TRY_UNWRAP_CHAIN: - case EXPR_ANYSWITCH: - case EXPR_OPERATOR_CHARS: - case EXPR_MACRO_BODY_EXPANSION: - case EXPR_BUILTIN_ACCESS: - case EXPR_DECL: - UNREACHABLE - case EXPR_BINARY: - case EXPR_BITACCESS: - case EXPR_BITASSIGN: - case EXPR_COMPOUND_LITERAL: - case EXPR_EMBED: - case EXPR_GENERIC_IDENT: - case EXPR_POINTER_OFFSET: - case EXPR_RETVAL: - case EXPR_SLICE: - case EXPR_SLICE_ASSIGN: - case EXPR_SLICE_COPY: - case EXPR_SWIZZLE: - case EXPR_SUBSCRIPT_ADDR: - case EXPR_SUBSCRIPT_ASSIGN: - case EXPR_VASPLAT: - case EXPR_MACRO_BODY: - REMINDER("Check if these should be analysed"); - FALLTHROUGH; - // Above needs to be analysed - case EXPR_GROUP: - case EXPR_INITIALIZER_LIST: - case EXPR_DESIGNATED_INITIALIZER_LIST: - case EXPR_ASM: - case EXPR_CONST: - case EXPR_NOP: - case EXPR_MACRO_BLOCK: - case EXPR_LAMBDA: - case EXPR_EXPR_BLOCK: - case EXPR_CT_IS_CONST: - case EXPR_CT_DEFINED: - case EXPR_CT_AND_OR: - case EXPR_STRINGIFY: - case EXPR_TERNARY: - case EXPR_CT_CASTABLE: - case EXPR_CT_ARG: - case EXPR_CT_CALL: - case EXPR_EXPRESSION_LIST: - case EXPR_POST_UNARY: - case EXPR_TYPEID: - case EXPR_TYPEID_INFO: - if (!sema_analyse_expr(context, main)) return false; - break; + case EXPR_FORCE_UNWRAP: + if (!sema_analyse_expr(context, main->inner_expr)) return false; + success = IS_OPTIONAL(main->inner_expr); + break; + case EXPR_RETHROW: + if (!sema_analyse_expr(context, main->rethrow_expr.inner)) return false; + success = IS_OPTIONAL(main->rethrow_expr.inner); + break; + case EXPR_OPTIONAL: + if (!sema_expr_analyse_optional(context, main, &failed)) + { + if (!failed) return false; + success = false; + } + break; + case EXPR_POISONED: + success = false; + break; + case EXPR_COND: + case EXPR_TEST_HOOK: + case EXPR_CATCH_UNWRAP: + case EXPR_DESIGNATOR: + case EXPR_BENCHMARK_HOOK: + case EXPR_TRY_UNWRAP: + case EXPR_TRY_UNWRAP_CHAIN: + case EXPR_ANYSWITCH: + case EXPR_OPERATOR_CHARS: + case EXPR_MACRO_BODY_EXPANSION: + case EXPR_BUILTIN_ACCESS: + case EXPR_DECL: + UNREACHABLE + case EXPR_BINARY: + case EXPR_BITACCESS: + case EXPR_BITASSIGN: + case EXPR_COMPOUND_LITERAL: + case EXPR_EMBED: + case EXPR_GENERIC_IDENT: + case EXPR_POINTER_OFFSET: + case EXPR_RETVAL: + case EXPR_SLICE: + case EXPR_SLICE_ASSIGN: + case EXPR_SLICE_COPY: + case EXPR_SWIZZLE: + case EXPR_SUBSCRIPT_ADDR: + case EXPR_SUBSCRIPT_ASSIGN: + case EXPR_VASPLAT: + case EXPR_MACRO_BODY: + REMINDER("Check if these should be analysed"); + FALLTHROUGH; + // Above needs to be analysed + case EXPR_GROUP: + case EXPR_INITIALIZER_LIST: + case EXPR_DESIGNATED_INITIALIZER_LIST: + case EXPR_ASM: + case EXPR_CONST: + case EXPR_NOP: + case EXPR_MACRO_BLOCK: + case EXPR_LAMBDA: + case EXPR_EXPR_BLOCK: + case EXPR_CT_IS_CONST: + case EXPR_CT_DEFINED: + case EXPR_CT_AND_OR: + case EXPR_STRINGIFY: + case EXPR_TERNARY: + case EXPR_CT_CASTABLE: + case EXPR_CT_ARG: + case EXPR_CT_CALL: + case EXPR_EXPRESSION_LIST: + case EXPR_POST_UNARY: + case EXPR_TYPEID: + case EXPR_TYPEID_INFO: + if (!sema_analyse_expr(context, main)) return false; + break; + } + if (!success) break; } expr_rewrite_const_bool(expr, type_bool, success); return true; @@ -8269,7 +8280,7 @@ bool sema_analyse_cond_expr(SemaContext *context, Expr *expr) } -bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allow_optional) +bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allow_optional, bool *no_match_ref) { if (to && type_is_optional(to)) { @@ -8292,6 +8303,7 @@ bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allo Type *flat = type_flatten(to); if (flat != type_anyfault && flat->type_kind != TYPE_FAULTTYPE && expr_is_const(expr)) { + if (no_match_ref) goto NO_MATCH_REF; sema_error_at_after(expr->span, "You need to add a trailing '?' here to make this an optional."); return false; } @@ -8306,6 +8318,7 @@ bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allo if (len < 1) goto NO_SLICE; if (len != to_canonical->array.len) { + if (no_match_ref) goto NO_MATCH_REF; RETURN_SEMA_ERROR(expr, "Slice length mismatch, expected %u but got %u.", to_canonical->array.len, len); } // Given x[3..7] -> (int[5]*)x[3..7] @@ -8316,13 +8329,21 @@ bool sema_analyse_expr_rhs(SemaContext *context, Type *to, Expr *expr, bool allo return true; } NO_SLICE:; - if (to && !cast_implicit(context, expr, to)) return false; + if (to) + { + bool cast_works = no_match_ref ? cast_implicit_silent(context, expr, to) : cast_implicit(context, expr, to); + if (!cast_works) return false; + } if (!allow_optional && IS_OPTIONAL(expr)) { + if (no_match_ref) goto NO_MATCH_REF; RETURN_SEMA_ERROR(expr, "It is not possible to cast from %s to %s.", type_quoted_error_string(expr->type), type_quoted_error_string(type_no_optional(expr->type))); } return true; +NO_MATCH_REF: + *no_match_ref = true; + return false; } static MemberIndex len_from_const_initializer(ConstInitializer *init) diff --git a/src/compiler/sema_initializers.c b/src/compiler/sema_initializers.c index 7669bc68e..255b97d86 100644 --- a/src/compiler/sema_initializers.c +++ b/src/compiler/sema_initializers.c @@ -158,7 +158,7 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte } Expr *element = elements[i]; // 6. We know the required type, so resolve the expression. - if (!sema_analyse_expr_rhs(context, members[i]->type, element, true)) return false; + if (!sema_analyse_expr_rhs(context, members[i]->type, element, true, NULL)) return false; if (member->decl_kind == DECL_VAR && member->var.kind == VARDECL_BITMEMBER) { if (!sema_bit_assignment_check(element, members[i])) return false; @@ -284,7 +284,7 @@ static inline bool sema_expr_analyse_array_plain_initializer(SemaContext *contex sub->subscript_expr.expr = exprid(expr_variable(decl)); sub->subscript_expr.range.start = exprid(expr_new_const_int(element->span, type_usz, 0)); vec_add(expr_list->expression_list, sub); - if (!sema_analyse_expr_rhs(context, inner_type, expr_list, true)) return false; + if (!sema_analyse_expr_rhs(context, inner_type, expr_list, true, NULL)) return false; elements[i] = expr_list; for (unsigned j = 1; j < len; j++) { @@ -292,7 +292,7 @@ static inline bool sema_expr_analyse_array_plain_initializer(SemaContext *contex sub->subscript_expr.expr = exprid(expr_variable(decl)); sub->subscript_expr.range.start = exprid(expr_new_const_int(element->span, type_usz, 1)); vec_insert_at(elements, i + j, sub); - if (!sema_analyse_expr_rhs(context, inner_type, sub, true)) return false; + if (!sema_analyse_expr_rhs(context, inner_type, sub, true, NULL)) return false; } initializer->initializer_list = elements; count += len - 1; @@ -305,7 +305,7 @@ static inline bool sema_expr_analyse_array_plain_initializer(SemaContext *contex } else { - if (!sema_analyse_expr_rhs(context, inner_type, element, true)) return false; + if (!sema_analyse_expr_rhs(context, inner_type, element, true, NULL)) return false; if (inner_is_inferred) { if (inferred_element) @@ -413,7 +413,7 @@ static bool sema_expr_analyse_designated_initializer(SemaContext *context, Type Type *result = sema_expr_analyse_designator(context, original, expr, &max_index, &member); if (!result) return false; Expr *value = expr->designator_expr.value; - if (!sema_analyse_expr_rhs(context, result, value, true)) return false; + if (!sema_analyse_expr_rhs(context, result, value, true, NULL)) return false; if (member && member->decl_kind == DECL_VAR && member->var.kind == VARDECL_BITMEMBER) { if (!sema_bit_assignment_check(value, member)) return false; diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index dd527db0e..e52458176 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -431,7 +431,7 @@ static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *state { if (block_type) { - if (!sema_analyse_expr_rhs(context, block_type, ret_expr, true)) return false; + if (!sema_analyse_expr_rhs(context, block_type, ret_expr, true, NULL)) return false; } else { @@ -533,7 +533,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement if (return_expr) { - if (!sema_analyse_expr_rhs(context, expected_rtype, return_expr, type_is_optional(expected_rtype))) return false; + if (!sema_analyse_expr_rhs(context, expected_rtype, return_expr, type_is_optional(expected_rtype), NULL)) return false; if (!sema_check_not_stack_variable_escape(context, return_expr)) return false; if (!sema_return_optional_check_is_valid_in_scope(context, return_expr)) return false; } @@ -924,13 +924,13 @@ static inline bool sema_analyse_last_cond(SemaContext *context, Expr *expr, Cond Expr *right = exprptr(expr->binary_expr.right); bool is_deref = right->expr_kind == EXPR_UNARY && right->unary_expr.operator == UNARYOP_DEREF; if (is_deref) right = right->unary_expr.expr; - if (!sema_analyse_expr_rhs(context, NULL, right, false)) return false; + if (!sema_analyse_expr_rhs(context, NULL, right, false, NULL)) return false; Type *type = right->type->canonical; if (type == type_get_ptr(type_anyptr) && is_deref) { is_deref = false; right = exprptr(expr->binary_expr.right); - if (!sema_analyse_expr_rhs(context, NULL, right, false)) return false; + if (!sema_analyse_expr_rhs(context, NULL, right, false, NULL)) return false; } if (type != type_anyptr) goto NORMAL_EXPR; // Found an expansion here @@ -1970,7 +1970,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) Type *expected_type = parent->ast_kind == AST_SWITCH_STMT ? cond->type : type_anyfault; - if (!sema_analyse_expr_rhs(context, expected_type, value, false)) return false; + if (!sema_analyse_expr_rhs(context, expected_type, value, false, NULL)) return false; statement->nextcase_stmt.defer_id = context_get_defers(context, context->active_scope.defer_last, parent->switch_stmt.defer, true); @@ -2091,7 +2091,7 @@ static inline bool sema_analyse_compound_statement_no_scope(SemaContext *context static inline bool sema_check_type_case(SemaContext *context, Type *switch_type, Ast *case_stmt, Ast **cases, unsigned index) { Expr *expr = exprptr(case_stmt->case_stmt.expr); - if (!sema_analyse_expr_rhs(context, type_typeid, expr, false)) return false; + if (!sema_analyse_expr_rhs(context, type_typeid, expr, false, NULL)) return false; if (expr_is_const(expr)) { @@ -2119,8 +2119,8 @@ static inline bool sema_check_value_case(SemaContext *context, Type *switch_type Expr *to_expr = exprptrzero(case_stmt->case_stmt.to_expr); // 1. Try to do implicit conversion to the correct type. - if (!sema_analyse_expr_rhs(context, switch_type, expr, false)) return false; - if (to_expr && !sema_analyse_expr_rhs(context, switch_type, to_expr, false)) return false; + if (!sema_analyse_expr_rhs(context, switch_type, expr, false, NULL)) return false; + if (to_expr && !sema_analyse_expr_rhs(context, switch_type, to_expr, false, NULL)) return false; bool is_range = to_expr != NULL; bool first_is_const = expr_is_const(expr); @@ -2406,8 +2406,8 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem } else { - if (!sema_analyse_expr_rhs(context, type, expr, false)) goto FAILED; - if (to_expr && !sema_analyse_expr_rhs(context, type, to_expr, false)) goto FAILED; + if (!sema_analyse_expr_rhs(context, type, expr, false, NULL)) goto FAILED; + if (to_expr && !sema_analyse_expr_rhs(context, type, to_expr, false, NULL)) goto FAILED; } if (!expr_is_const(expr)) { diff --git a/src/version.h b/src/version.h index 408d016f3..2d3b6f362 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.691" +#define COMPILER_VERSION "0.4.692" diff --git a/test/test_suite/errors/rethrow_macro.c3 b/test/test_suite/errors/rethrow_macro.c3 index dd399b838..00225e552 100644 --- a/test/test_suite/errors/rethrow_macro.c3 +++ b/test/test_suite/errors/rethrow_macro.c3 @@ -4,7 +4,7 @@ import std::io; macro char[] read(src, Allocator* allocator, n) { char* data = allocator.alloc_checked(n)!; // #error: Rethrow is only allowed in macros - src.read_all(data[:n])!; + io::read_all(src, data[:n])!; } fn void main() diff --git a/test/test_suite/generic/generic_lambda_complex.c3t b/test/test_suite/generic/generic_lambda_complex.c3t index abab5ca2e..576477f13 100644 --- a/test/test_suite/generic/generic_lambda_complex.c3t +++ b/test/test_suite/generic/generic_lambda_complex.c3t @@ -38,7 +38,7 @@ struct TextTag TextTagKind kind; union { String* data; - Stream template; + TextTemplate* template; } } @@ -102,16 +102,7 @@ fn void! TextTemplate.free(&self) *self = {}; } -fn StreamWrapper TextTemplate.as_stream(&self) -{ - return { .stream.fns = &texttemplate_interface, .data = self }; -} - -StreamInterface texttemplate_interface @private = { - .write_stream_fn = fn (s, out) => ((TextTemplate*)(((StreamWrapper*)s).data)).write_to(out), -}; - -fn usz! TextTemplate.write_to(&self, Stream* writer) +fn usz! TextTemplate.write_to(&self, OutStream* writer) { usz n; usz pos; @@ -125,7 +116,7 @@ fn usz! TextTemplate.write_to(&self, Stream* writer) return n; } -fn usz! TextTag.write(&self, Stream* writer) +fn usz! TextTag.write(&self, OutStream* writer) { switch (self.kind) { @@ -247,7 +238,7 @@ panic_block: ; preds = %assign_optional4 %14 = insertvalue %"any*[]" undef, ptr %varargslots, 0 %"$$temp" = insertvalue %"any*[]" %14, i64 1, 1 store %"any*[]" %"$$temp", ptr %indirectarg6, align 8 - call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 25, ptr @.func, i64 4, i32 173, ptr byval(%"any*[]") align 8 %indirectarg6) + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 25 unreachable noerr_block7: ; preds = %after_check5 diff --git a/test/test_suite/macros/unifying_implicit_void.c3t b/test/test_suite/macros/unifying_implicit_void.c3t index b3bfbc646..39df98834 100644 --- a/test/test_suite/macros/unifying_implicit_void.c3t +++ b/test/test_suite/macros/unifying_implicit_void.c3t @@ -5,10 +5,11 @@ import std::io; fn void! main() { ByteReader r; - r.foo()!; + InStream* s = &r; + s.foo()!; } -macro Stream.foo(&self) +macro InStream.foo(&self) { char! c = self.read_byte(); if (catch err = c) @@ -25,62 +26,86 @@ macro Stream.foo(&self) define i64 @oups.main() #0 { entry: %r = alloca %ByteReader, align 8 + %s = alloca %"any*", align 8 %error_var = alloca i64, align 8 + %self = alloca %"any*", align 8 %c = alloca i8, align 1 %c.f = alloca i64, align 8 + %.inlinecache = alloca ptr, align 8 + %.cachedtype = alloca ptr, align 8 %retparam = alloca i8, align 1 %err = alloca i64, align 8 - call void @llvm.memset.p0.i64(ptr align 8 %r, i8 0, i64 32, i1 false) - %0 = getelementptr inbounds %ByteReader, ptr %r, i32 0, i32 0 - %1 = call i64 @std.io.Stream.read_byte(ptr %retparam, ptr %0) #3 - %not_err = icmp eq i64 %1, 0 - %2 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) - br i1 %2, label %after_check, label %assign_optional - -assign_optional: ; preds = %entry - store i64 %1, ptr %c.f, align 8 + store ptr null, ptr %.cachedtype, align 8 + call void @llvm.memset.p0.i64(ptr align 8 %r, i8 0, i64 24, i1 false) + %0 = insertvalue %"any*" undef, ptr %r, 0 + %1 = insertvalue %"any*" %0, i64 ptrtoint (ptr @"$ct.std.io.ByteReader" to i64), 1 + store %"any*" %1, ptr %s, align 8 + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %self, ptr align 8 %s, i32 16, i1 false) + %2 = getelementptr inbounds %"any*", ptr %self, i32 0, i32 1 + %3 = load i64, ptr %2, align 8 + %4 = getelementptr inbounds %"any*", ptr %self, i32 0, i32 0 + %5 = inttoptr i64 %3 to ptr + %type = load ptr, ptr %.cachedtype, align 8 + %6 = icmp eq ptr %5, %type + br i1 %6, label %cache_hit, label %cache_miss +cache_miss: ; preds = %entry + %7 = getelementptr inbounds %.introspect, ptr %5, i32 0, i32 2 + %8 = load ptr, ptr %7, align 8 + %9 = call ptr @.dyn_search(ptr %8, ptr @"$sel.read_byte") + store ptr %9, ptr %.inlinecache, align 8 + store ptr %5, ptr %.cachedtype, align 8 + br label %10 +cache_hit: ; preds = %entry + %cache_hit_fn = load ptr, ptr %.inlinecache, align 8 + br label %10 +10: ; preds = %cache_hit, %cache_miss + %fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %9, %cache_miss ] + %11 = icmp eq ptr %fn_phi, null + br i1 %11, label %missing_function, label %match +missing_function: ; preds = %10 + %12 = load ptr, ptr @std.core.builtin.panic, align 8 + call void %12(ptr @.panic_msg, i64 46, ptr @.file, i64 25, ptr @.func, i64 4, i32 13) + unreachable +match: ; preds = %10 + %13 = load ptr, ptr %4, align 8 + %14 = call i64 %fn_phi(ptr %retparam, ptr %13) + %not_err = icmp eq i64 %14, 0 + %15 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %15, label %after_check, label %assign_optional +assign_optional: ; preds = %match + store i64 %14, ptr %c.f, align 8 br label %after_assign - -after_check: ; preds = %entry - %3 = load i8, ptr %retparam, align 1 - store i8 %3, ptr %c, align 1 +after_check: ; preds = %match + %16 = load i8, ptr %retparam, align 1 + store i8 %16, ptr %c, align 1 store i64 0, ptr %c.f, align 8 br label %after_assign - after_assign: ; preds = %after_check, %assign_optional br label %testblock - testblock: ; preds = %after_assign %optval = load i64, ptr %c.f, align 8 %not_err1 = icmp eq i64 %optval, 0 - %4 = call i1 @llvm.expect.i1(i1 %not_err1, i1 true) - br i1 %4, label %after_check3, label %assign_optional2 - + %17 = call i1 @llvm.expect.i1(i1 %not_err1, i1 true) + br i1 %17, label %after_check3, label %assign_optional2 assign_optional2: ; preds = %testblock store i64 %optval, ptr %err, align 8 br label %end_block - after_check3: ; preds = %testblock store i64 0, ptr %err, align 8 br label %end_block - end_block: ; preds = %after_check3, %assign_optional2 - %5 = load i64, ptr %err, align 8 - %neq = icmp ne i64 %5, 0 + %18 = load i64, ptr %err, align 8 + %neq = icmp ne i64 %18, 0 br i1 %neq, label %if.then, label %if.exit - if.then: ; preds = %end_block - %6 = load i64, ptr %err, align 8 - store i64 %6, ptr %error_var, align 8 + %19 = load i64, ptr %err, align 8 + store i64 %19, ptr %error_var, align 8 br label %guard_block - if.exit: ; preds = %end_block br label %noerr_block - guard_block: ; preds = %if.then - %7 = load i64, ptr %error_var, align 8 - ret i64 %7 - + %20 = load i64, ptr %error_var, align 8 + ret i64 %20 noerr_block: ; preds = %if.exit ret i64 0 } \ No newline at end of file diff --git a/test/test_suite/switch/switch_in_defer_macro.c3t b/test/test_suite/switch/switch_in_defer_macro.c3t index 017bfd844..622727ac8 100644 --- a/test/test_suite/switch/switch_in_defer_macro.c3t +++ b/test/test_suite/switch/switch_in_defer_macro.c3t @@ -33,7 +33,7 @@ def Ident = fn bool (usz index, char c); struct Lexer { Allocator* allocator; - Stream* reader; + InStream* reader; char[] buf; TokenTrie tokens; Ident ident; @@ -50,7 +50,7 @@ struct Lexer } } -fn void! Lexer.init(&self, Stream* reader, Ident ident, Allocator* using = mem::heap()) +fn void! Lexer.init(&self, InStream* reader, Ident ident, Allocator* using = mem::heap()) { TokenTrie trie; ushort max_token; @@ -356,7 +356,7 @@ fn void! Lexer.parse_comment(&self, String end) @private char[] buf = self.buf[:end.len]; while (true) { - if (catch err = self.reader.read_all(buf)) + if (catch err = io::read_all(self.reader, buf)) { case IoError.UNEXPECTED_EOF: case IoError.EOF: @@ -381,9 +381,9 @@ macro Lexer.unread(self, n) @private case n == 1: self.reader.pushback_byte()!!; case n > 1: - if (SeekStreamFn func = self.reader.fns.seek_fn) + if (&self.reader.seek) { - func(self.reader, -n, CURSOR)!!; + self.reader.seek(-n, CURSOR)!!; break; } for (; n > 0; n--) self.reader.pushback_byte()!!; @@ -685,9 +685,8 @@ fn void test() %"any*" = type { ptr, i64 } %"UintTest[]" = type { ptr, i64 } %UintTest = type { %"char[]", i64 } -%ByteReader = type { %Stream, %"char[]", i64 } -%Stream = type { ptr } -%Lexer = type { %"any*", ptr, %"char[]", %Trie, ptr, i8, i8, i32, i32, i32, %.anon } +%ByteReader = type { %"char[]", i64 } +%Lexer = type { %"any*", %"any*", %"char[]", %Trie, ptr, i8, i8, i32, i32, i32, %.anon } %Trie = type { %List } %List = type { i64, i64, %"any*", ptr } %.anon = type { %"char[]" } @@ -709,6 +708,7 @@ fn void test() @.str.4 = private unnamed_addr constant [2 x i8] c"\0A\00", align 1 @.str.5 = private unnamed_addr constant [3 x i8] c"*/\00", align 1 @"lexer_test.Comment$end" = linkonce constant [2 x %"char[]"] [%"char[]" { ptr @.str.4, i64 1 }, %"char[]" { ptr @.str.5, i64 2 }], align 8 +@"$ct.std.io.ByteReader" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 24, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 @std.core.mem.thread_allocator = external thread_local global %"any*", align 8 ; Function Attrs: nounwind @@ -755,8 +755,9 @@ entry: %br = alloca %ByteReader, align 8 %lex = alloca %Lexer, align 8 %error_var = alloca i64, align 8 + %taddr = alloca %"any*", align 8 %kind = alloca i8, align 1 - %error_var4 = alloca i64, align 8 + %error_var6 = alloca i64, align 8 %retparam = alloca i8, align 1 store %"UintTest[]" zeroinitializer, ptr %tcases, align 8 %0 = getelementptr inbounds %"UintTest[]", ptr %tcases, i32 0, i32 1 @@ -765,7 +766,7 @@ entry: store i64 0, ptr %.anon1, align 8 br label %loop.cond -loop.cond: ; preds = %noerr_block9, %entry +loop.cond: ; preds = %noerr_block11, %entry %2 = load i64, ptr %.anon1, align 8 %3 = load i64, ptr %.anon, align 8 %lt = icmp ult i64 %2, %3 @@ -777,57 +778,64 @@ loop.body: ; preds = %loop.cond %6 = load i64, ptr %.anon1, align 8 %ptroffset = getelementptr inbounds %UintTest, ptr %5, i64 %6 call void @llvm.memcpy.p0.p0.i32(ptr align 8 %tc, ptr align 8 %ptroffset, i32 24, i1 false) - call void @llvm.memset.p0.i64(ptr align 8 %br, i8 0, i64 32, i1 false) - call void @llvm.memset.p0.i64(ptr align 8 %lex, i8 0, i64 120, i1 false) + call void @llvm.memset.p0.i64(ptr align 8 %br, i8 0, i64 24, i1 false) + call void @llvm.memset.p0.i64(ptr align 8 %lex, i8 0, i64 128, i1 false) %7 = getelementptr inbounds %UintTest, ptr %tc, i32 0, i32 0 %8 = getelementptr inbounds %"char[]", ptr %7, i32 0, i32 0 %lo = load ptr, ptr %8, align 8 %9 = getelementptr inbounds %"char[]", ptr %7, i32 0, i32 1 %hi = load i64, ptr %9, align 8 %10 = call ptr @std.io.ByteReader.init(ptr %br, ptr %lo, i64 %hi) - %lo2 = load i64, ptr @std.core.mem.thread_allocator, align 8 - %hi3 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8 - %11 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.init"(ptr %lex, ptr %10, ptr @lexer_test.is_ident_char, i64 %lo2, ptr %hi3) - %not_err = icmp eq i64 %11, 0 - %12 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) - br i1 %12, label %after_check, label %assign_optional + %11 = insertvalue %"any*" undef, ptr %10, 0 + %12 = insertvalue %"any*" %11, i64 ptrtoint (ptr @"$ct.std.io.ByteReader" to i64), 1 + store %"any*" %12, ptr %taddr, align 8 + %13 = getelementptr inbounds { i64, ptr }, ptr %taddr, i32 0, i32 0 + %lo2 = load i64, ptr %13, align 8 + %14 = getelementptr inbounds { i64, ptr }, ptr %taddr, i32 0, i32 1 + %hi3 = load ptr, ptr %14, align 8 + %lo4 = load i64, ptr @std.core.mem.thread_allocator, align 8 + %hi5 = load ptr, ptr getelementptr inbounds ({ i64, ptr }, ptr @std.core.mem.thread_allocator, i32 0, i32 1), align 8 + %15 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.init"(ptr %lex, i64 %lo2, ptr %hi3, ptr @lexer_test.is_ident_char, i64 %lo4, ptr %hi5) + %not_err = icmp eq i64 %15, 0 + %16 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %16, label %after_check, label %assign_optional assign_optional: ; preds = %loop.body - store i64 %11, ptr %error_var, align 8 + store i64 %15, ptr %error_var, align 8 br label %guard_block after_check: ; preds = %loop.body br label %noerr_block guard_block: ; preds = %assign_optional - %13 = load i64, ptr %error_var, align 8 - ret i64 %13 + %17 = load i64, ptr %error_var, align 8 + ret i64 %17 noerr_block: ; preds = %after_check - %14 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.next"(ptr %retparam, ptr %lex) - %not_err5 = icmp eq i64 %14, 0 - %15 = call i1 @llvm.expect.i1(i1 %not_err5, i1 true) - br i1 %15, label %after_check7, label %assign_optional6 - -assign_optional6: ; preds = %noerr_block - store i64 %14, ptr %error_var4, align 8 - br label %guard_block8 - -after_check7: ; preds = %noerr_block - br label %noerr_block9 - -guard_block8: ; preds = %assign_optional6 - %16 = load i64, ptr %error_var4, align 8 - ret i64 %16 - -noerr_block9: ; preds = %after_check7 - %17 = load i8, ptr %retparam, align 1 - store i8 %17, ptr %kind, align 1 - %18 = load i8, ptr %kind, align 1 - %eq = icmp eq i8 %18, 1 + %18 = call i64 @"lexer$lexer_test.Token$lexer_test.Comment$.Lexer.next"(ptr %retparam, ptr %lex) + %not_err7 = icmp eq i64 %18, 0 + %19 = call i1 @llvm.expect.i1(i1 %not_err7, i1 true) + br i1 %19, label %after_check9, label %assign_optional8 + +assign_optional8: ; preds = %noerr_block + store i64 %18, ptr %error_var6, align 8 + br label %guard_block10 + +after_check9: ; preds = %noerr_block + br label %noerr_block11 + +guard_block10: ; preds = %assign_optional8 + %20 = load i64, ptr %error_var6, align 8 + ret i64 %20 + +noerr_block11: ; preds = %after_check9 + %21 = load i8, ptr %retparam, align 1 + store i8 %21, ptr %kind, align 1 + %22 = load i8, ptr %kind, align 1 + %eq = icmp eq i8 %22, 1 call void @llvm.assume(i1 %eq) - %19 = load i64, ptr %.anon1, align 8 - %add = add i64 %19, 1 + %23 = load i64, ptr %.anon1, align 8 + %add = add i64 %23, 1 store i64 %add, ptr %.anon1, align 8 br label %loop.cond diff --git a/test/unit/stdlib/io/bufferstream.c3 b/test/unit/stdlib/io/bufferstream.c3 index 43112c179..603179c4d 100644 --- a/test/unit/stdlib/io/bufferstream.c3 +++ b/test/unit/stdlib/io/bufferstream.c3 @@ -29,7 +29,7 @@ fn void! readbuffer() ByteWriter bw; bw.tinit(); - usz n = reader_buf.copy_to(&bw)!; + usz n = io::copy_to(&reader_buf, &bw)!; assert(n == DATA.len, "got %d; want %d", n, DATA.len); String got = bw.str_view(); @@ -61,7 +61,7 @@ fn void! writebuffer() WriteBuffer write_buf; write_buf.init(&out, buf[..]); - usz n = br.copy_to(&write_buf)!; + usz n = io::copy_to(&br, &write_buf)!; assert(n == DATA.len, "got %d; want %d", n, DATA.len); String got = out.str_view(); diff --git a/test/unit/stdlib/io/bytestream.c3 b/test/unit/stdlib/io/bytestream.c3 index afd6df36e..a1ae864cb 100644 --- a/test/unit/stdlib/io/bytestream.c3 +++ b/test/unit/stdlib/io/bytestream.c3 @@ -4,8 +4,8 @@ fn void! bytestream() { ByteReader r; r.init("abc"); - Stream* s = &r; - assert(s.len()! == 3); + InStream* s = &r; + assert(s.len() == 3); char[5] buffer; assert('a' == s.read_byte()!); s.pushback_byte()!; @@ -13,11 +13,11 @@ fn void! bytestream() assert((String)buffer[:len] == "abc"); ByteWriter w; w.init(); - Stream* ws = &w; + OutStream* ws = &w; ws.write("helloworld")!; assert(w.str_view() == "helloworld"); s.seek(0, SET)!; - ws.read_from(s)!; + io::copy_to(s, ws)!; s.seek(1, SET)!; s.write_to(ws)!; assert(w.str_view() == "helloworldabcbc"); @@ -28,7 +28,7 @@ fn void! bytewriter_buffer() ByteWriter writer; char[8] z; writer.init_buffer(&z); - Stream* s = &writer; + OutStream* s = &writer; s.write("hello")!!; s.write_byte(0)!!; String o = ((ZString)&z).str_view(); @@ -40,7 +40,7 @@ fn void! bytewriter_read_from() { char[] data = "Lorem ipsum dolor sit amet biam."; TestReader r = { .bytes = data }; - Stream* s = &&r.as_stream(); + InStream* s = &r; ByteWriter bw; bw.tinit(); @@ -52,27 +52,20 @@ fn void! bytewriter_read_from() module std::io; // TestReader only has the read method to trigger the path // in ByteWriter.read_from that does not rely on the available method. -struct TestReader +struct TestReader (InStream) { char[] bytes; usz index; } -fn StreamWrapper TestReader.as_stream(TestReader *r) +fn usz! TestReader.read(&self, char[] bytes) @dynamic { - return { .stream = { &testReader_interface }, .data = r }; -} - -fn usz! TestReader.read(TestReader *r, char[] bytes) -{ - usz left = r.bytes.len - r.index; + usz left = self.bytes.len - self.index; if (left == 0) return 0; usz n = min(left, bytes.len); - mem::copy(bytes.ptr, &r.bytes[r.index], n); - r.index += n; + mem::copy(bytes.ptr, &self.bytes[self.index], n); + self.index += n; return n; } -StreamInterface testReader_interface = { - .read_fn = fn(s, char[] bytes) => ((TestReader*)((StreamWrapper*)s).data).read(bytes), -}; \ No newline at end of file +fn char! TestReader.read_byte(&self) @dynamic => io::@read_byte_using_read(self); diff --git a/test/unit/stdlib/io/dstringstream.c3 b/test/unit/stdlib/io/dstringstream.c3 index 73c689767..c2a1e0913 100644 --- a/test/unit/stdlib/io/dstringstream.c3 +++ b/test/unit/stdlib/io/dstringstream.c3 @@ -4,13 +4,13 @@ fn void! test_writing() { DString foo; foo.init(); - Stream* s = DStringStream{}.init(&foo); + OutStream* s = &foo; s.write("hello")!!; s.write_byte('-')!!; s.write("what?-------------------------------------------------------")!!; ByteReader r; String test_str = "2134"; - s.read_from(r.init(test_str))!; + io::copy_to(r.init(test_str), s)!; String o = foo.str_view(); assert(o == "hello-what?-------------------------------------------------------2134"); } diff --git a/test/unit/stdlib/io/file_read_write_any.c3 b/test/unit/stdlib/io/file_read_write_any.c3 index a13425c78..ff020159b 100644 --- a/test/unit/stdlib/io/file_read_write_any.c3 +++ b/test/unit/stdlib/io/file_read_write_any.c3 @@ -57,13 +57,13 @@ fn void! read_write_any() File file = file::open("__file_read_write_any_test_file", "wb")!!; - file.write_any(&data)!!; + io::write_any(&file, &data)!!; file.flush()!; file.close()!!; file = file::open("__file_read_write_any_test_file", "rb")!!; Data rdata; - file.read_any(&rdata)!!; + io::read_any(&file, &rdata)!!; file.close()!!; diff --git a/test/unit/stdlib/io/varint.c3 b/test/unit/stdlib/io/varint.c3 index 4cb61f035..143114559 100644 --- a/test/unit/stdlib/io/varint.c3 +++ b/test/unit/stdlib/io/varint.c3 @@ -9,14 +9,14 @@ fn void! write_read() uint x; uint y; - n = buf.write_varint(123)!; + n = io::write_varint(&buf, 123)!; assert(n == 1, "got %d; want 1", n); - buf.read_varint(&y)!; + io::read_varint(&buf, &y)!; assert(y == 123, "got %d; want 123", y); - n = buf.write_varint(123456789)!; + n = io::write_varint(&buf, 123456789)!; assert(n == 4, "got %d; want 4", n); - buf.read_varint(&y)!; + io::read_varint(&buf, &y)!; assert(y == 123456789, "got %d; want 123456789", y); } @@ -46,7 +46,7 @@ fn void! samples() { ByteWriter bw; bw.tinit(); - usz n = bw.write_varint(tc.in)!; + usz n = io::write_varint(&bw, tc.in)!; assert(n == tc.bytes.len, "got %d; want %d", n, tc.bytes.len); char[] bytes = bw.bytes[:bw.index]; assert(bytes == tc.bytes, "got %d; want %d", bytes, tc.bytes);