diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index 73d0617fc..c8ad66ad3 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -104,13 +104,29 @@ fn void default_panic(String message, String file, String function, uint line) @ $$trap(); } + +macro void abort(String string = "Unrecoverable error reached", ...) @builtin @noreturn +{ + panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat); + $$trap(); +} + +bool in_panic @local = false; + fn void default_panic(String message, String file, String function, uint line) @if(!env::NATIVE_STACKTRACE) { + if (in_panic) + { + io::eprintn("Panic inside of panic."); + return; + } + in_panic = true; $if $defined(io::stderr): io::eprint("\nERROR: '"); io::eprint(message); io::eprintfn("', in %s (%s:%d)", function, file, line); $endif + in_panic = false; $$trap(); } @@ -120,11 +136,19 @@ PanicFn panic = &default_panic; fn void panicf(String fmt, String file, String function, uint line, args...) { + if (in_panic) + { + io::eprint("Panic inside of panic: "); + io::eprintn(fmt); + return; + } + in_panic = true; @stack_mem(512; Allocator allocator) { DString s; s.new_init(.allocator = allocator); s.appendf(fmt, ...args); + in_panic = false; panic(s.str_view(), file, function, line); }; } diff --git a/lib/std/io/formatter.c3 b/lib/std/io/formatter.c3 index 857c34bf2..4b455e4bb 100644 --- a/lib/std/io/formatter.c3 +++ b/lib/std/io/formatter.c3 @@ -15,24 +15,14 @@ fault PrintFault { BUFFER_EXCEEDED, INTERNAL_BUFFER_EXCEEDED, - INVALID_FORMAT_STRING, - MISSING_ARG, - INVALID_ARGUMENT_TYPE, -} - -fault FormattingFault -{ - UNTERMINATED_FORMAT, - MISSING_ARG, - INVALID_WIDTH_ARG, - INVALID_FORMAT_TYPE, + INVALID_FORMAT, + NOT_ENOUGH_ARGUMENTS, + INVALID_ARGUMENT, } def OutputFn = fn void!(void* buffer, char c); def FloatType = double; - - fn usz! Formatter.printf(&self, String format, args...) { return self.vprintf(format, args) @inline; @@ -48,6 +38,7 @@ struct Formatter uint width; uint prec; usz idx; + anyfault first_fault; } } @@ -69,7 +60,12 @@ fn void Formatter.init(&self, OutputFn out_fn, void* data = null) fn usz! Formatter.out(&self, char c) @private { - self.out_fn(self.data, c)!; + if (catch err = self.out_fn(self.data, c)) + { + if (self.first_fault) return self.first_fault?; + self.first_fault = err; + return err?; + } return 1; } @@ -136,7 +132,7 @@ fn usz! Formatter.out_str(&self, any arg) @private } self.flags = {}; self.width = 0; - return self.ntoa_any(arg, 10); + return self.ntoa_any(arg, 10) ?? self.out_substr(""); case FLOAT: PrintFlags flags = self.flags; uint width = self.width; @@ -147,7 +143,7 @@ fn usz! Formatter.out_str(&self, any arg) @private } self.flags = {}; self.width = 0; - return self.ftoa(float_from_any(arg)!!); + return self.ftoa(float_from_any(arg)) ?? self.out_substr("ERR"); case BOOL: return self.out_substr(*(bool*)arg.ptr ? "true" : "false"); default: @@ -292,10 +288,31 @@ fn void! out_null_fn(void* data @unused, char c @unused) @private { } +macro usz! @report_fault(Formatter* f, $fault) +{ + (void)f.out_substr($fault); + return PrintFault.INVALID_FORMAT?; +} +macro usz! @wrap_bad(Formatter* f, #action) +{ + usz! len = #action; + if (catch err = len) + { + case PrintFault.BUFFER_EXCEEDED: + case PrintFault.INTERNAL_BUFFER_EXCEEDED: + return f.first_err(err)?; + default: + err = f.first_err(PrintFault.INVALID_ARGUMENT); + f.out_substr("")!; + return err?; + } + return len; +} fn usz! Formatter.vprintf(&self, String format, any[] anys) { + self.first_fault = {}; if (!self.out_fn) { // use null output function @@ -315,7 +332,7 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys) continue; } i++; - if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING?; + if (i >= format_len) return @report_fault(self, "%ERR"); c = format[i]; if (c == '%') { @@ -335,11 +352,12 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys) case '#': self.flags.hash = true; default: break FLAG_EVAL; } - if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?; + if (++i >= format_len) return @report_fault(self, "%ERR"); c = format[i]; } // evaluate width field - int w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!; + int! w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i); + if (catch w) return @report_fault(self, "%ERR"); c = format[i]; if (w < 0) { @@ -352,15 +370,21 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys) if (c == '.') { self.flags.precision = true; - if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?; - int prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!; + if (++i >= format_len) return @report_fault(self, ""); + int! prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i); + if (catch prec) return @report_fault(self, ""); self.prec = prec < 0 ? 0 : prec; c = format[i]; } // evaluate specifier uint base = 0; - if (variant_index >= anys.len) return PrintFault.MISSING_ARG?; + if (variant_index >= anys.len) + { + self.first_err(PrintFault.NOT_ENOUGH_ARGUMENTS); + total_len += self.out_substr("")!; + continue; + } any current = anys[variant_index++]; switch (c) { @@ -386,25 +410,25 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys) self.flags.uppercase = true; nextcase; case 'a': - total_len += self.atoa(float_from_any(current)!!)!; + total_len += @wrap_bad(self, self.atoa(float_from_any(current)))!; continue; case 'F' : self.flags.uppercase = true; nextcase; case 'f': - total_len += self.ftoa(float_from_any(current)!!)!; + total_len += @wrap_bad(self, self.ftoa(float_from_any(current)))!; continue; case 'E': self.flags.uppercase = true; nextcase; case 'e': - total_len += self.etoa(float_from_any(current)!!)!; + total_len += @wrap_bad(self, self.etoa(float_from_any(current)))!; continue; case 'G': self.flags.uppercase = true; nextcase; case 'g': - total_len += self.gtoa(float_from_any(current)!!)!; + total_len += @wrap_bad(self, self.gtoa(float_from_any(current)))!; continue; case 'c': total_len += self.out_char(current)!; @@ -429,7 +453,9 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys) self.flags.hash = true; base = 16; default: - return PrintFault.INVALID_FORMAT_STRING?; + self.first_err(PrintFault.INVALID_FORMAT); + total_len += self.out_substr("")!; + continue; } if (base != 10) { @@ -440,14 +466,13 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys) if (self.flags.precision) self.flags.zeropad = false; bool is_neg; - uint128 v = int_from_any(current, &is_neg)!!; - - total_len += self.ntoa(v, is_neg, base)!; + total_len += @wrap_bad(self, self.ntoa(int_from_any(current, &is_neg), is_neg, base))!; } // termination // out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); // return written chars without terminating \0 + if (self.first_fault) return self.first_fault?; return total_len; } diff --git a/lib/std/io/formatter_private.c3 b/lib/std/io/formatter_private.c3 index 793afe62a..ae3b8b3de 100644 --- a/lib/std/io/formatter_private.c3 +++ b/lib/std/io/formatter_private.c3 @@ -4,6 +4,18 @@ import std::math; const char[16] XDIGITS_H = "0123456789ABCDEF"; const char[16] XDIGITS_L = "0123456789abcdef"; +fault FormattingFault +{ + BAD_FORMAT +} + +macro Formatter.first_err(&self, anyfault f) +{ + if (self.first_fault) return self.first_fault; + self.first_fault = f; + return f; +} + fn usz! Formatter.adjust(&self, usz len) @local { if (!self.flags.left) return 0; @@ -21,6 +33,7 @@ fn uint128! int_from_any(any arg, bool *is_neg) @private case TypeKind.ENUM: return int_from_any(arg.as_inner(), is_neg); default: + break; } *is_neg = false; switch (arg) @@ -59,7 +72,7 @@ fn uint128! int_from_any(any arg, bool *is_neg) @private double d = *arg; return (uint128)((*is_neg = d < 0) ? -d : d); default: - return PrintFault.INVALID_ARGUMENT_TYPE?; + return FormattingFault.BAD_FORMAT?; } } @@ -101,7 +114,7 @@ fn FloatType! float_from_any(any arg) @private case double: return (FloatType)*arg; default: - return PrintFault.INVALID_ARGUMENT_TYPE?; + return FormattingFault.BAD_FORMAT?; } } @@ -588,8 +601,7 @@ fn usz! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba fn usz! Formatter.ntoa_any(&self, any arg, uint base) @private { bool is_neg; - uint128 val = int_from_any(arg, &is_neg)!!; - return self.ntoa(val, is_neg, base) @inline; + return self.ntoa(int_from_any(arg, &is_neg)!!, is_neg, base) @inline; } fn usz! Formatter.out_char(&self, any arg) @private @@ -640,17 +652,6 @@ fn usz! Formatter.out_reverse(&self, char[] buf) @private return n; } -fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline @private -{ - usz val = ++(*index_ptr); - if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT?; -} - -fn any! next_any(any* args_ptr, usz args_len, usz* arg_index_ptr) @inline @private -{ - if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG?; - return args_ptr[(*arg_index_ptr)++]; -} fn int! printf_parse_format_field( any* args_ptr, usz args_len, usz* args_index_ptr, @@ -659,9 +660,11 @@ fn int! printf_parse_format_field( char c = format_ptr[*index_ptr]; if (c.is_digit()) return simple_atoi(format_ptr, format_len, index_ptr); if (c != '*') return 0; - printf_advance_format(format_len, index_ptr)!; - any val = next_any(args_ptr, args_len, args_index_ptr)!; - if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG?; + usz len = ++(*index_ptr); + if (len >= format_len) return FormattingFault.BAD_FORMAT?; + if (*args_index_ptr >= args_len) return FormattingFault.BAD_FORMAT?; + any val = args_ptr[(*args_index_ptr)++]; + if (!val.type.kindof.is_int()) return FormattingFault.BAD_FORMAT?; uint! intval = types::any_to_int(val, int); - return intval ?? FormattingFault.INVALID_WIDTH_ARG?; + return intval ?? FormattingFault.BAD_FORMAT?; } diff --git a/lib/std/io/io.c3 b/lib/std/io/io.c3 index 014af8c2c..17a920d8c 100644 --- a/lib/std/io/io.c3 +++ b/lib/std/io/io.c3 @@ -256,7 +256,7 @@ fn usz! printfn(String format, args...) @maydiscard { Formatter formatter; formatter.init(&out_putchar_fn); - usz len = formatter.vprintf(format, args)!; + usz! len = formatter.vprintf(format, args); putchar('\n'); io::stdout().flush()!; return len + 1; @@ -290,10 +290,10 @@ 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; + usz! len = formatter.vprintf(format, args); stderr().write_byte('\n')!; stderr().flush()!; - return len; + return len + 1; } /** diff --git a/releasenotes.md b/releasenotes.md index 8c56b7360..18d3ef850 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -97,6 +97,7 @@ - Methods can now properly be aliased using `def` #1393. - Memory leak in Object when not using temp allocators. - Tracking allocator would double the allocations in the report. +- `printf` will now show errors in the output when there are errors. ### Stdlib changes