From 4783946476af03621850b9e69c786186beb567b8 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 3 Oct 2022 12:12:51 +0200 Subject: [PATCH] Version bump. Updated printf using "Formatter". Fix to initializers. --- README.md | 2 +- lib/std/io_formatter_private.c3 | 572 ++++++++++++ lib/std/io_printf.c3 | 883 ++++-------------- src/compiler/llvm_codegen_function.c | 1 + src/compiler/sema_expr.c | 2 + src/compiler/sema_passes.c | 9 +- src/version.h | 2 +- .../initialize/initialize_finalize.c3t | 8 +- test/test_suite/stdlib/map.c3t | 72 +- .../initialize/initialize_finalize.c3t | 8 +- test/test_suite2/stdlib/map.c3t | 30 +- 11 files changed, 850 insertions(+), 739 deletions(-) create mode 100644 lib/std/io_formatter_private.c3 diff --git a/README.md b/README.md index 889464ef5..33b8906fe 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ fn void test() ### Current status -The current version of the compiler is alpha release 0.2. +The current version of the compiler is alpha release 0.3. It's possible to try out the current C3 compiler in the browser: https://ide.judge0.com/ – this is courtesy of the developer of Judge0. diff --git a/lib/std/io_formatter_private.c3 b/lib/std/io_formatter_private.c3 new file mode 100644 index 000000000..2ff0c6a74 --- /dev/null +++ b/lib/std/io_formatter_private.c3 @@ -0,0 +1,572 @@ +module std::io; + +private fn void! Formatter.left_adjust(Formatter* this, usize len) +{ + if (!this.flags.left) return; + for (usize l = len; l < this.width; l++) this.out(' ')?; +} + +private fn void! Formatter.right_adjust(Formatter* this, usize len) +{ + if (this.flags.left) return; + for (usize l = len; l < this.width; l++) this.out(' ')?; +} + + +private fn NtoaType int_from_variant(variant arg, bool *is_neg) +{ + *is_neg = false; + $if (NtoaType.typeid == uint128.typeid): + switch (arg) + { + case int128: + int128 val = *arg; + return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : val; + case uint128: + return *arg; + } + $endif; + + if (arg.type.kind == TypeKind.POINTER) + { + return (NtoaType)(uptr)*(void**)arg.ptr; + } + switch (arg) + { + case bool: + return (NtoaType)*arg; + case ichar: + int val = *arg; + return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val; + case short: + int val = *arg; + return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val; + case int: + int val = *arg; + return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val; + case long: + long val = *arg; + return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val; + case char: + return *arg; + case ushort: + return *arg; + case uint: + return *arg; + case ulong: + return *arg; + case float: + float f = *arg; + return (NtoaType)((*is_neg = f < 0) ? -f : f); + case double: + double d = *arg; + return (NtoaType)((*is_neg = d < 0) ? -d : d); + default: + return 0; + } +} + +private fn FloatType float_from_variant(variant arg) +{ + $if (env::I128_SUPPORT): + switch (arg) + { + case int128: + return *arg; + case uint128: + return *arg; + } + $endif; + $if (env::F128_SUPPORT): + if (arg.type == float128.typeid) return *((float128*)arg.ptr); + $endif; + $if (env::F16_SUPPORT): + if (arg.type == float16.typeid) return *((float16*)arg.ptr); + $endif; + + if (arg.type.kind == TypeKind.POINTER) + { + return (FloatType)(uptr)(void*)arg.ptr; + } + switch (arg) + { + case bool: + return (FloatType)*arg; + case ichar: + return *arg; + case short: + return *arg; + case int: + return *arg; + case long: + return *arg; + case char: + return *arg; + case ushort: + return *arg; + case uint: + return *arg; + case ulong: + return *arg; + case float: + return (FloatType)*arg; + case double: + return (FloatType)*arg; + default: + return 0; + } +} + + +/** + * Read a simple integer value, typically for formatting. + * + * @param [inout] len_ptr "the length remaining." + * @param [in] buf "the buf to read from." + * @param maxlen "the maximum len that can be read." + * @return "The result of the atoi." + **/ +private fn uint simple_atoi(char* buf, usize maxlen, usize* len_ptr) @inline +{ + uint i = 0; + usize len = *len_ptr; + while (len < maxlen) + { + char c = buf[len]; + if (c < '0' || c > '9') break; + i = i * 10 + c - '0'; + len++; + } + *len_ptr = len; + return i; +} + + +private fn void! Formatter.out_substr(Formatter *this, char[] str) +{ + usize l = conv::utf8_codepoints(str); + uint prec = this.prec; + if (this.flags.precision && l < prec) l = prec; + this.right_adjust(' ')?; + usize index = 0; + usize chars = str.len; + char* ptr = str.ptr; + while (index < chars) + { + char c = ptr[index]; + // Break if we have precision set and we ran out... + if (c & 0xC0 != 0x80 && this.flags.precision && !prec--) break; + this.out(c)?; + index++; + } + return this.left_adjust(l); +} + + +union ConvUnion +{ + ulong u; + double f; +} + +private fn void! Formatter.etoa(Formatter* this, FloatType value) +{ + // check for NaN and special values + if (value != value || value < FloatType.min || value > FloatType.max) + { + return this.ftoa(value); + } + + // determine the sign + bool negative = value < 0; + if (negative) value = -value; + + // default precision + if (!this.flags.precision) + { + this.prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + ConvUnion conv; + + conv.f = (double)value; + int exp2 = (int)(conv.u >> 52 & 0x7FF) - 1023; // effectively log2 + conv.u = (conv.u & (1u64 << 52 - 1)) | (1023u64 << 52); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.f - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + double z2 = z * z; + conv.u = (ulong)(exp2 + 1023) << 52; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.f *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.f) + { + expval--; + conv.f /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + uint minwidth = ((expval < 100) && (expval > -100)) ? 4 : 5; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (this.flags.adapt_exp) + { + // do we want to fall-back to "%f" mode? + if (value >= 1e-4 && value < 1e6) + { + this.prec = this.prec > expval ? this.prec - expval - 1 : 0; + this.flags.precision = true; // make sure ftoa respects precision + // no characters in exponent + minwidth = 0; + expval = 0; + } + else + { + // we use one sigfig for the whole part + if (this.prec > 0 && this.flags.precision) this.prec--; + } + } + + // Adjust width + uint fwidth = this.width > minwidth ? this.width - minwidth : 0; + + // if we're padding on the right, DON'T pad the floating part + if (this.flags.left && minwidth) fwidth = 0; + + // rescale the float value + if (expval) value /= conv.f; + + // output the floating part + usize start_idx = this.idx; + PrintFlags old = this.flags; + this.flags.adapt_exp = false; + this.width = fwidth; + this.ftoa(negative ? -value : value)?; + this.flags = old; + + // output the exponent part + if (minwidth) + { + // output the exponential symbol + this.out(this.flags.uppercase ? 'E' : 'e')?; + // output the exponent value + this.flags = { .zeropad = true, .plus = true }; + this.width = minwidth - 1; + this.prec = 0; + this.ntoa((NtoaType)(expval < 0 ? -expval : expval), expval < 0, 10)?; + this.flags = old; + // might need to right-pad spaces + this.left_adjust(this.idx - start_idx)?; + } +} + +// internal ftoa for fixed decimal floating point +private fn void! Formatter.ftoa(Formatter* this, FloatType value) +{ + char[PRINTF_FTOA_BUFFER_SIZE] buf = void; + usize len = 0; + const FloatType[] POW10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + FloatType diff = 0.0; + + // powers of 10 + + // test for special values + if (value != value) + { + return this.out_reverse("nan"); + } + if (value < -FloatType.max) + { + return this.out_reverse("fni-"); + } + if (value > FloatType.max) + { + if (this.flags.plus) + { + return this.out_reverse("fni+"); + } + return this.out_reverse("fni"); + } + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if (value > PRINTF_MAX_FLOAT || value < -PRINTF_MAX_FLOAT) + { + return this.etoa(value); + } + + // test for negative + bool negative = value < 0; + if (negative) value = 0 - value; + + // set default precision, if not set explicitly + if (!this.flags.precision) this.prec = PRINTF_DEFAULT_FLOAT_PRECISION; + + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while (this.prec > 9) + { + if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '0'; + this.prec--; + } + + // Safe due to 1e9 limit. + int whole = (int)value; + FloatType tmp = (value - whole) * POW10[this.prec]; + ulong frac = (ulong)tmp; + diff = tmp - frac; + + switch (true) + { + case diff > 0.5: + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= POW10[this.prec]) + { + frac = 0; + ++whole; + } + case diff < 0.5: + break; + case !frac && (frac & 1): + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + if (!this.prec) + { + diff = value - (FloatType)whole; + if ((!(diff < 0.5) || diff > 0.5) && (whole & 1)) + { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } + else + { + uint count = this.prec; + // now do fractional part, as an unsigned number + do + { + if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + --count; + buf[len++] = (char)(48 + (frac % 10)); + } + while (frac /= 10); + // add extra 0s + while (count-- > 0) + { + if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '0'; + } + if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + // add decimal + buf[len++] = '.'; + } + + // do whole part, number is reversed + do + { + if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = (char)(48 + (whole % 10)); + } + while (whole /= 10); + + // pad leading zeros + if (!this.flags.left && this.flags.zeropad) + { + if (this.width && (negative || this.flags.plus || this.flags.space)) this.width--; + while (len < this.width) + { + if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '0'; + } + } + + char next = {| + if (negative) return '-'; + if (this.flags.plus) return '+'; + if (this.flags.space) return ' '; + return 0; + |}; + if (next) + { + if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = next; + } + return this.out_reverse(buf[:len]); +} + +private fn void! Formatter.ntoa(Formatter* this, NtoaType value, bool negative, uint base) +{ + char[PRINTF_NTOA_BUFFER_SIZE] buf = void; + usize len = 0; + + // no hash for 0 values + if (!value) this.flags.hash = false; + + // write if precision != 0 or value is != 0 + if (!this.flags.precision || value) + { + char past_10 = (this.flags.uppercase ? 'A' : 'a') - 10; + do + { + if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + char digit = (char)(value % base); + buf[len++] = digit + (digit < 10 ? '0' : past_10); + value /= base; + } + while (value); + } + return this.ntoa_format(buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base); +} + +private fn void! Formatter.ntoa_format(Formatter* this, char[] buf, usize len, bool negative, uint base) +{ + // pad leading zeros + if (!this.flags.left) + { + if (this.width && this.flags.zeropad && (negative || this.flags.plus || this.flags.space)) this.width--; + while (len < this.prec) + { + if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '0'; + } + while (this.flags.zeropad && len < this.width) + { + if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '0'; + } + } + + // handle hash + if (this.flags.hash && base != 10) + { + if (!this.flags.precision && len && len == this.prec && len == this.width) + { + len--; + if (len) len--; + } + if (base != 10) + { + if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + switch (base) + { + case 16: + buf[len++] = this.flags.uppercase ? 'X' : 'x'; + case 8: + buf[len++] = this.flags.uppercase ? 'O' : 'o'; + case 2: + buf[len++] = this.flags.uppercase ? 'B' : 'b'; + default: + unreachable(); + } + buf[len++] = '0'; + } + } + + switch (true) + { + case negative: + if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '-'; + case this.flags.plus: + if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '+'; + case this.flags.space: + if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = ' '; + } + if (!len) return; + return this.out_reverse(buf[:len]); +} + +$if (env::I128_SUPPORT): +define NtoaType = uint128; +$else: +define NtoaType = ulong; +$endif; + +private fn void! Formatter.ntoa_variant(Formatter* this, variant arg, uint base) +{ + bool is_neg; + NtoaType val = int_from_variant(arg, &is_neg); + return this.ntoa(val, is_neg, base) @inline; +} + +private fn void! Formatter.out_char(Formatter* this, variant arg) +{ + uint l = 1; + // pre padding + this.right_adjust(l)?; + // char output + Char32 c = types::variant_to_int(arg, uint) ?? 0xFFFD; + switch (true) + { + case c < 0x7f: + this.out((char)c)?; + case c < 0x7ff: + this.out((char)(0xC0 | c >> 6))?; + this.out((char)(0x80 | (c & 0x3F)))?; + case c < 0xffff: + this.out((char)(0xE0 | c >> 12))?; + this.out((char)(0x80 | (c >> 6 & 0x3F)))?; + this.out((char)(0x80 | (c & 0x3F)))?; + default: + this.out((char)(0xF0 | c >> 18))?; + this.out((char)(0x80 | (c >> 12 & 0x3F)))?; + this.out((char)(0x80 | (c >> 6 & 0x3F)))?; + this.out((char)(0x80 | (c & 0x3F)))?; + } + return this.left_adjust(l); +} + + +private fn void! Formatter.out_reverse(Formatter* this, char[] buf) +{ + usize buffer_start_idx = this.idx; + usize len = buf.len; + // pad spaces up to given width + if (!this.flags.left && !this.flags.zeropad) + { + for (usize i = len; i < this.width; i++) + { + this.out(' ')?; + } + } + // reverse string + while (len) this.out(buf[--len])?; + + // append pad spaces up to given width + return this.left_adjust(this.idx - buffer_start_idx); +} + +private fn void! printf_advance_format(usize format_len, usize *index_ptr) @inline +{ + usize val = ++(*index_ptr); + if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT!; +} + +private fn variant! next_variant(variant* args_ptr, usize args_len, usize* arg_index_ptr) @inline +{ + if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG!; + return args_ptr[(*arg_index_ptr)++]; +} + +private fn int! printf_parse_format_field(variant* args_ptr, usize args_len, usize* args_index_ptr, char* format_ptr, usize format_len, usize* index_ptr) @inline +{ + char c = format_ptr[*index_ptr]; + if (c >= '0' && c <= '9') return simple_atoi(format_ptr, format_len, index_ptr); + if (c != '*') return 0; + printf_advance_format(format_len, index_ptr)?; + variant val = next_variant(args_ptr, args_len, args_index_ptr)?; + if (!val.type.kind.is_int()) return FormattingFault.INVALID_WIDTH_ARG!; + uint! intval = types::variant_to_int(val, int); + if (catch intval) return FormattingFault.INVALID_WIDTH_ARG!; + return intval; +} diff --git a/lib/std/io_printf.c3 b/lib/std/io_printf.c3 index 0d0059299..b8e59cffe 100644 --- a/lib/std/io_printf.c3 +++ b/lib/std/io_printf.c3 @@ -15,6 +15,30 @@ fault PrintFault MISSING_ARG, } + +define OutputFn = fn void!(char c, void* buffer); +define ToStringFunction = fn char[](void* value, Allocator *allocator); +define ToFormatFunction = fn void!(void* value, Formatter* formatter); +define FloatType = double; + +private define StringFunctionMap = std::map::HashMap; +private define FormatterFunctionMap = std::map::HashMap; +private FormatterFunctionMap toformat_functions; +private StringFunctionMap tostring_functions; + +struct Formatter +{ + void *data; + OutputFn out_fn; + struct + { + PrintFlags flags; + uint width; + uint prec; + usize idx; + } +} + bitstruct PrintFlags : uint { bool zeropad : 0; @@ -27,104 +51,128 @@ bitstruct PrintFlags : uint bool adapt_exp : 7; } -struct PrintParam +fn void Formatter.init(Formatter* this, OutputFn out_fn, void* data = null) { - OutputFn outfn; - void* buffer; - PrintFlags flags; - uint width; - uint prec; - usize idx; + *this = { .data = data, .out_fn = out_fn}; } -define OutputFn = fn void!(char c, void* buffer, usize buffer_idx); -define PrintFunction = fn char[](void* value, Allocator *allocator); - -private define PrintFunctionMap = std::map::HashMap; -private PrintFunctionMap print_functions; - -/** - * @require $checks($Type {}.to_string()) "Expected a type with 'to_string' defined" - * @require $checks($Type a, Allocator b, a.to_string(&b)) "Expected 'to_string' to take an allocator as argument." - **/ -macro void printf_register_to_string($Type) +/* + * @require $checks($Type {}.to_string()) || $checks($Type {}.to_format(&&Formatter{})) "Expected a type with 'to_string' or 'to_format' defined" + * @require !$checks($Type {}.to_string()) || $checks($Type a, Allocator b, a.to_string(&b)) "Expected 'to_string' to take an allocator as argument." + * @require !$checks($Type {}.to_format()) || $checks($Type a, Formatter b, a.to_format(&b)) "Expected 'to_format' to take a Formatter as argument." + */ +macro void formatter_register_type($Type) { - printf_register($Type.typeid, (PrintFunction)&$Type.to_string); + $if ($checks($Type {}.to_format(&&Formatter {}))): + if (!toformat_functions.table.len) + { + toformat_functions.init(512); + } + toformat_functions.set($Type.typeid, (ToFormatFunction)&$Type.to_format); + $else: + if (!tostring_functions.table.len) + { + tostring_functions.init(512); + } + tostring_functions.set($Type.typeid, (ToStringFunction)&$Type.to_string); + $endif; } + static initialize @priority(101) { - if (!print_functions.table.len) + if (!toformat_functions.table.len) { - print_functions.init(512); + toformat_functions.init(512); + } + if (!tostring_functions.table.len) + { + tostring_functions.init(512); } } -fn void printf_register(typeid type, PrintFunction function) +private fn void! Formatter.out(Formatter* this, char c) { - if (!print_functions.table.len) - { - print_functions.init(512); - } - print_functions.set(type, function); + this.out_fn(c, this.data)?; } -fn void! PrintParam.out(PrintParam* param, char c) +macro void! Formatter.print_with_function(Formatter* this, variant arg, char[] default_name) { - param.outfn(c, param.buffer, param.idx++)?; -} - -private fn void! out_str(PrintParam* param, variant arg) -{ - @pool() + if (try to_format = toformat_functions.get(arg.type)) { - if (try print_func = print_functions.get(arg.type)) + PrintFlags old = this.flags; + uint old_width = this.width; + uint old_prec = this.prec; + defer { - return out_substr(param, print_func(arg.ptr, mem::temp_allocator())); - } - }; + this.flags = old; + this.width = old_width; + this.prec = old_prec; + } + return to_format(arg.ptr, this); + } + if (try to_string = tostring_functions.get(arg.type)) + { + PrintFlags old = this.flags; + uint old_width = this.width; + uint old_prec = this.prec; + defer + { + this.flags = old; + this.width = old_width; + this.prec = old_prec; + } + @pool() + { + return this.out_substr(to_string(arg.ptr, mem::temp_allocator())); + }; + } + return this.out_substr(default_name); +} +private fn void! Formatter.out_str(Formatter* this, variant arg) +{ switch (arg.type.kind) { case TYPEID: - return out_substr(param, ""); + return this.out_substr(""); case VOID: - return out_substr(param, "void"); + return this.out_substr("void"); case ANYERR: case FAULT: - return out_substr(param, (*(anyerr*)arg.ptr).nameof); + return this.out_substr((*(anyerr*)arg.ptr).nameof); case VARIANT: - return out_substr(param, ""); + return this.out_substr(""); case ENUM: - return out_substr(param, arg.type.names[types::variant_to_int(arg, usize)!!]); + return this.out_substr(arg.type.names[types::variant_to_int(arg, usize)!!]); case STRUCT: - return out_substr(param, ""); + return this.print_with_function(arg, ""); case UNION: - return out_substr(param, ""); + return this.print_with_function(arg, ""); case BITSTRUCT: - return out_substr(param, ""); + return this.print_with_function(arg, ""); case FUNC: - return out_substr(param, ""); + return this.print_with_function(arg, ""); case FAILABLE: unreachable(); case DISTINCT: if (arg.type == String.typeid) { - return out_substr(param, ((String*)arg).str()); + return this.out_substr(((String*)arg).str()); } - return out_str(param, variant { arg.ptr, arg.type.inner }); + return this.out_str(variant { arg.ptr, arg.type.inner }); case POINTER: typeid inner = arg.type.inner; if (inner.kind == TypeKind.ARRAY && inner.inner == char.typeid) { char *ptr = *(char**)arg.ptr; - return out_substr(param, ptr[:inner.len]); + return this.out_substr(ptr[:inner.len]); } - return ntoa_variant(param, arg, 16); + return this.ntoa_variant(arg, 16); case SIGNED_INT: case UNSIGNED_INT: - return ntoa_variant(param, arg, 10); + return this.ntoa_variant(arg, 10); case FLOAT: - return ftoa(param, float_from_variant(arg)); + return this.ftoa(float_from_variant(arg)); case ARRAY: // this is SomeType[*] so grab the "SomeType" typeid inner = arg.type.inner; @@ -132,14 +180,14 @@ private fn void! out_str(PrintParam* param, variant arg) usize len = arg.type.len; // Pretend this is a char[] void* ptr = (void*)arg.ptr; - param.out('[')?; + this.out('[')?; for (usize i = 0; i < len; i++) { - if (i != 0) out_substr(param, ", ")?; - out_str(param, variant { ptr, inner })?; + if (i != 0) this.out_substr(", ")?; + this.out_str(variant { ptr, inner })?; ptr += size; } - return param.out(']'); + return this.out(']'); case VECTOR: // this is SomeType[*] so grab the "SomeType" typeid inner = arg.type.inner; @@ -147,64 +195,48 @@ private fn void! out_str(PrintParam* param, variant arg) usize len = arg.type.len; // Pretend this is a char[] void* ptr = (void*)arg.ptr; - out_substr(param, "[<")?; + this.out_substr("[<")?; for (usize i = 0; i < len; i++) { - if (i != 0) out_substr(param, ", ")?; - out_str(param, variant { ptr, inner })?; + if (i != 0) this.out_substr(", ")?; + this.out_str(variant { ptr, inner })?; ptr += size; } - return out_substr(param, ">]"); + return this.out_substr(">]"); case SUBARRAY: // this is SomeType[] so grab the "SomeType" typeid inner = arg.type.inner; if (inner == char.typeid) { - return out_substr(param, *(char[]*)arg); + return this.out_substr(*(char[]*)arg); } usize size = inner.sizeof; // Pretend this is a char[] char[]* temp = (void*)arg.ptr; void* ptr = (void*)temp.ptr; usize len = temp.len; - param.out('[')?; + this.out('[')?; for (usize i = 0; i < len; i++) { - if (i != 0) out_substr(param, ", ")?; - out_str(param, variant { ptr, inner })?; + if (i != 0) this.out_substr(", ")?; + this.out_str(variant { ptr, inner })?; ptr += size; } - param.out(']')?; + this.out(']')?; case BOOL: if (*(bool*)arg.ptr) { - return out_substr(param, "true"); + return this.out_substr("true"); } else { - return out_substr(param, "false"); + return this.out_substr("false"); } default: - return out_substr(param, "Invalid type"); + return this.out_substr("Invalid type"); } } - -private fn uint simple_atoi(char* buf, usize maxlen, usize* len_ptr) @inline -{ - uint i = 0; - usize len = *len_ptr; - while (len < maxlen) - { - char c = buf[len]; - if (c < '0' || c > '9') break; - i = i * 10 + c - '0'; - len++; - } - *len_ptr = len; - return i; -} - fault FormattingFault { UNTERMINATED_FORMAT, @@ -213,626 +245,111 @@ fault FormattingFault INVALID_FORMAT_TYPE, } -private fn void! printf_advance_format(usize format_len, usize *index_ptr) @inline + +private fn void! out_buffer_fn(char c, void *data) { - usize val = ++(*index_ptr); - if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT!; + BufferData *buffer_data = data; + if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED!; + buffer_data.buffer[buffer_data.written++] = c; } -private fn variant! next_variant(variant* args_ptr, usize args_len, usize* arg_index_ptr) @inline -{ - if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG!; - return args_ptr[(*arg_index_ptr)++]; -} - -private fn int! printf_parse_format_field(variant* args_ptr, usize args_len, usize* args_index_ptr, char* format_ptr, usize format_len, usize* index_ptr) @inline -{ - char c = format_ptr[*index_ptr]; - if (c >= '0' && c <= '9') return simple_atoi(format_ptr, format_len, index_ptr); - if (c != '*') return 0; - printf_advance_format(format_len, index_ptr)?; - variant val = next_variant(args_ptr, args_len, args_index_ptr)?; - if (!val.type.kind.is_int()) return FormattingFault.INVALID_WIDTH_ARG!; - uint! intval = types::variant_to_int(val, int); - if (catch intval) return FormattingFault.INVALID_WIDTH_ARG!; - return intval; -} - - -private fn void! out_buffer_fn(char c, char[] buffer, usize buffer_idx) -{ - if (buffer_idx >= buffer.len) return PrintFault.BUFFER_EXCEEDED!; - buffer[buffer_idx] = c; -} - -private fn void! out_null_fn(char c @unused, void* data @unused, usize idx @unused) +private fn void! out_null_fn(char c @unused, void* data @unused) { } -private fn void! out_putchar_fn(char c, void* data @unused, usize idx @unused) +private fn void! out_putchar_fn(char c, void* data @unused) { libc::putchar(c); } -private fn void! out_fputchar_fn(char c, void* data, usize idx @unused) +private fn void! out_fputchar_fn(char c, void* data) { File* f = data; f.putc(c)?; } -private fn void! out_string_append_fn(char c, void* data, usize idx @unused) +private fn void! out_string_append_fn(char c, void* data) { String* s = data; s.append_char(c); } -private fn void! PrintParam.out_reverse(PrintParam* param, char[] buf) -{ - usize buffer_start_idx = param.idx; - usize len = buf.len; - // pad spaces up to given width - if (!param.flags.left && !param.flags.zeropad) - { - for (usize i = len; i < param.width; i++) - { - param.out(' ')?; - } - } - // reverse string - while (len) param.out(buf[--len])?; - - // append pad spaces up to given width - return param.left_adjust(param.idx - buffer_start_idx); -} - -private fn void! out_char(PrintParam* param, variant arg) -{ - uint l = 1; - // pre padding - param.right_adjust(l)?; - // char output - Char32 c = types::variant_to_int(arg, uint) ?? 0xFFFD; - switch (true) - { - case c < 0x7f: - param.out((char)c)?; - case c < 0x7ff: - param.out((char)(0xC0 | c >> 6))?; - param.out((char)(0x80 | (c & 0x3F)))?; - case c < 0xffff: - param.out((char)(0xE0 | c >> 12))?; - param.out((char)(0x80 | (c >> 6 & 0x3F)))?; - param.out((char)(0x80 | (c & 0x3F)))?; - default: - param.out((char)(0xF0 | c >> 18))?; - param.out((char)(0x80 | (c >> 12 & 0x3F)))?; - param.out((char)(0x80 | (c >> 6 & 0x3F)))?; - param.out((char)(0x80 | (c & 0x3F)))?; - } - return param.left_adjust(l); -} - -private fn void! ntoa_format(PrintParam* param, char[] buf, usize len, bool negative, uint base) -{ - // pad leading zeros - if (!param.flags.left) - { - if (param.width && param.flags.zeropad && (negative || param.flags.plus || param.flags.space)) param.width--; - while (len < param.prec) - { - if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - buf[len++] = '0'; - } - while (param.flags.zeropad && len < param.width) - { - if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - buf[len++] = '0'; - } - } - - // handle hash - if (param.flags.hash && base != 10) - { - if (!param.flags.precision && len && len == param.prec && len == param.width) - { - len--; - if (len) len--; - } - if (base != 10) - { - if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - switch (base) - { - case 16: - buf[len++] = param.flags.uppercase ? 'X' : 'x'; - case 8: - buf[len++] = param.flags.uppercase ? 'O' : 'o'; - case 2: - buf[len++] = param.flags.uppercase ? 'B' : 'b'; - default: - unreachable(); - } - buf[len++] = '0'; - } - } - - switch (true) - { - case negative: - if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - buf[len++] = '-'; - case param.flags.plus: - if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - buf[len++] = '+'; - case param.flags.space: - if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - buf[len++] = ' '; - } - if (!len) return; - return param.out_reverse(buf[:len]); -} - -$if (env::I128_SUPPORT): -define NtoaType = uint128; -$else: -define NtoaType = ulong; -$endif; - -private fn void! ntoa_variant(PrintParam* param, variant arg, uint base) -{ - bool is_neg; - NtoaType val = int_from_variant(arg, &is_neg); - return ntoa(param, val, is_neg, base) @inline; -} - -private fn void! ntoa(PrintParam* param, NtoaType value, bool negative, uint base) -{ - char[PRINTF_NTOA_BUFFER_SIZE] buf = void; - usize len = 0; - - // no hash for 0 values - if (!value) param.flags.hash = false; - - // write if precision != 0 or value is != 0 - if (!param.flags.precision || value) - { - char past_10 = (param.flags.uppercase ? 'A' : 'a') - 10; - do - { - if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - char digit = (char)(value % base); - buf[len++] = digit + (digit < 10 ? '0' : past_10); - value /= base; - } - while (value); - } - return ntoa_format(param, buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base); -} - - -define FloatType = double; - -// internal ftoa for fixed decimal floating point -private fn void! ftoa(PrintParam* param, FloatType value) -{ - char[PRINTF_FTOA_BUFFER_SIZE] buf = void; - usize len = 0; - const FloatType[] POW10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; - FloatType diff = 0.0; - - // powers of 10 - - // test for special values - if (value != value) - { - return param.out_reverse("nan"); - } - if (value < -FloatType.max) - { - return param.out_reverse("fni-"); - } - if (value > FloatType.max) - { - if (param.flags.plus) - { - return param.out_reverse("fni+"); - } - return param.out_reverse("fni"); - } - - // test for very large values - // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad - if (value > PRINTF_MAX_FLOAT || value < -PRINTF_MAX_FLOAT) - { - return etoa(param, value); - } - - // test for negative - bool negative = value < 0; - if (negative) value = 0 - value; - - // set default precision, if not set explicitly - if (!param.flags.precision) param.prec = PRINTF_DEFAULT_FLOAT_PRECISION; - - // limit precision to 9, cause a prec >= 10 can lead to overflow errors - while (param.prec > 9) - { - if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - buf[len++] = '0'; - param.prec--; - } - - // Safe due to 1e9 limit. - int whole = (int)value; - FloatType tmp = (value - whole) * POW10[param.prec]; - ulong frac = (ulong)tmp; - diff = tmp - frac; - - switch (true) - { - case diff > 0.5: - ++frac; - // handle rollover, e.g. case 0.99 with prec 1 is 1.0 - if (frac >= POW10[param.prec]) - { - frac = 0; - ++whole; - } - case diff < 0.5: - break; - case !frac && (frac & 1): - // if halfway, round up if odd OR if last digit is 0 - ++frac; - } - if (!param.prec) - { - diff = value - (FloatType)whole; - if ((!(diff < 0.5) || diff > 0.5) && (whole & 1)) - { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++whole; - } - } - else - { - uint count = param.prec; - // now do fractional part, as an unsigned number - do - { - if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - --count; - buf[len++] = (char)(48 + (frac % 10)); - } - while (frac /= 10); - // add extra 0s - while (count-- > 0) - { - if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - buf[len++] = '0'; - } - if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - // add decimal - buf[len++] = '.'; - } - - // do whole part, number is reversed - do - { - if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - buf[len++] = (char)(48 + (whole % 10)); - } - while (whole /= 10); - - // pad leading zeros - if (!param.flags.left && param.flags.zeropad) - { - if (param.width && (negative || param.flags.plus || param.flags.space)) param.width--; - while (len < param.width) - { - if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - buf[len++] = '0'; - } - } - - char next = {| - if (negative) return '-'; - if (param.flags.plus) return '+'; - if (param.flags.space) return ' '; - return 0; - |}; - if (next) - { - if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; - buf[len++] = next; - } - return param.out_reverse(buf[:len]); -} - -union ConvUnion -{ - ulong u; - double f; -} - -private fn void! etoa(PrintParam* param, FloatType value) -{ - // check for NaN and special values - if (value != value || value < FloatType.min || value > FloatType.max) - { - return ftoa(param, value); - } - - // determine the sign - bool negative = value < 0; - if (negative) value = -value; - - // default precision - if (!param.flags.precision) - { - param.prec = PRINTF_DEFAULT_FLOAT_PRECISION; - } - - // determine the decimal exponent - // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) - ConvUnion conv; - - conv.f = (double)value; - int exp2 = (int)(conv.u >> 52 & 0x7FF) - 1023; // effectively log2 - conv.u = (conv.u & (1u64 << 52 - 1)) | (1023u64 << 52); // drop the exponent so conv.F is now in [1,2) - // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 - int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.f - 1.5) * 0.289529654602168); - // now we want to compute 10^expval but we want to be sure it won't overflow - exp2 = (int)(expval * 3.321928094887362 + 0.5); - double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; - double z2 = z * z; - conv.u = (ulong)(exp2 + 1023) << 52; - // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex - conv.f *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); - // correct for rounding errors - if (value < conv.f) - { - expval--; - conv.f /= 10; - } - - // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters - uint minwidth = ((expval < 100) && (expval > -100)) ? 4 : 5; - - // in "%g" mode, "prec" is the number of *significant figures* not decimals - if (param.flags.adapt_exp) - { - // do we want to fall-back to "%f" mode? - if (value >= 1e-4 && value < 1e6) - { - param.prec = param.prec > expval ? param.prec - expval - 1 : 0; - param.flags.precision = true; // make sure ftoa respects precision - // no characters in exponent - minwidth = 0; - expval = 0; - } - else - { - // we use one sigfig for the whole part - if (param.prec > 0 && param.flags.precision) param.prec--; - } - } - - // Adjust width - uint fwidth = param.width > minwidth ? param.width - minwidth : 0; - - // if we're padding on the right, DON'T pad the floating part - if (param.flags.left && minwidth) fwidth = 0; - - // rescale the float value - if (expval) value /= conv.f; - - // output the floating part - usize start_idx = param.idx; - PrintFlags old = param.flags; - param.flags.adapt_exp = false; - param.width = fwidth; - ftoa(param, negative ? -value : value)?; - param.flags = old; - - // output the exponent part - if (minwidth) - { - // output the exponential symbol - param.out(param.flags.uppercase ? 'E' : 'e')?; - // output the exponent value - param.flags = { .zeropad = true, .plus = true }; - param.width = minwidth - 1; - param.prec = 0; - ntoa(param, (NtoaType)(expval < 0 ? -expval : expval), expval < 0, 10)?; - param.flags = old; - // might need to right-pad spaces - param.left_adjust(param.idx - start_idx)?; - } -} - -private fn FloatType float_from_variant(variant arg) -{ - $if (env::I128_SUPPORT): - switch (arg) - { - case int128: - return *arg; - case uint128: - return *arg; - } - $endif; - $if (env::F128_SUPPORT): - if (arg.type == float128.typeid) return *((float128*)arg.ptr); - $endif; - $if (env::F16_SUPPORT): - if (arg.type == float16.typeid) return *((float16*)arg.ptr); - $endif; - - if (arg.type.kind == TypeKind.POINTER) - { - return (FloatType)(uptr)(void*)arg.ptr; - } - switch (arg) - { - case bool: - return (FloatType)*arg; - case ichar: - return *arg; - case short: - return *arg; - case int: - return *arg; - case long: - return *arg; - case char: - return *arg; - case ushort: - return *arg; - case uint: - return *arg; - case ulong: - return *arg; - case float: - return (FloatType)*arg; - case double: - return (FloatType)*arg; - default: - return 0; - } -} - -private fn NtoaType int_from_variant(variant arg, bool *is_neg) -{ - *is_neg = false; - $if (NtoaType.typeid == uint128.typeid): - switch (arg) - { - case int128: - int128 val = *arg; - return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : val; - case uint128: - return *arg; - } - $endif; - - if (arg.type.kind == TypeKind.POINTER) - { - return (NtoaType)(uptr)*(void**)arg.ptr; - } - switch (arg) - { - case bool: - return (NtoaType)*arg; - case ichar: - int val = *arg; - return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val; - case short: - int val = *arg; - return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val; - case int: - int val = *arg; - return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val; - case long: - long val = *arg; - return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val; - case char: - return *arg; - case ushort: - return *arg; - case uint: - return *arg; - case ulong: - return *arg; - case float: - float f = *arg; - return (NtoaType)((*is_neg = f < 0) ? -f : f); - case double: - double d = *arg; - return (NtoaType)((*is_neg = d < 0) ? -d : d); - default: - return 0; - } -} - - fn usize! printf(char[] format, args...) @maydiscard { - return vsnprintf(&out_putchar_fn, null, format, args); + Formatter formatter; + formatter.init(&out_putchar_fn); + return formatter.vprintf(format, args); } fn usize! printfln(char[] format, args...) @maydiscard { - usize size = vsnprintf(&out_putchar_fn, null, format, args)?; + Formatter formatter; + formatter.init(&out_putchar_fn); + usize len = formatter.vprintf(format, args)?; putchar('\n'); - return size + 1; + return len + 1; } fn usize! String.printf(String* str, char[] format, args...) @maydiscard { - return vsnprintf(&out_string_append_fn, str, format, args); + Formatter formatter; + formatter.init(&out_string_append_fn, str); + return formatter.vprintf(format, args); } fn usize! String.printfln(String* str, char[] format, args...) @maydiscard { - usize size = vsnprintf(&out_string_append_fn, str, format, args)?; + Formatter formatter; + formatter.init(&out_string_append_fn, str); + usize len = formatter.vprintf(format, args)?; str.append('\n'); - return size + 1; + return len + 1; +} + +private struct BufferData +{ + char[] buffer; + usize written; +} + +fn char[]! bprintf(char[] buffer, char[] format, args...) @maydiscard +{ + Formatter formatter; + BufferData data = { .buffer = buffer }; + formatter.init(&out_buffer_fn, &data); + usize size = formatter.vprintf(format, args)?; + return buffer[:size]; } fn usize! File.printf(File file, char[] format, args...) @maydiscard { - return vsnprintf(&out_putchar_fn, &file, format, args); + Formatter formatter; + formatter.init(&out_putchar_fn, &file); + return formatter.vprintf(format, args)?; } fn usize! File.printfln(File file, char[] format, args...) @maydiscard { - usize size = vsnprintf(&out_putchar_fn, &file, format, args)?; + Formatter formatter; + formatter.init(&out_putchar_fn, &file); + usize len = formatter.vprintf(format, args)?; file.putc('\n')?; file.flush(); - return size + 1; + return len + 1; } -private fn void! PrintParam.left_adjust(PrintParam* param, usize len) +fn usize! Formatter.printf(Formatter* this, char[] format, args...) { - if (!param.flags.left) return; - for (usize l = len; l < param.width; l++) param.out(' ')?; + return this.vprintf(format, args) @inline; } -private fn void! PrintParam.right_adjust(PrintParam* param, usize len) +fn usize! Formatter.vprintf(Formatter* this, char[] format, variant[] variants) { - if (param.flags.left) return; - for (usize l = len; l < param.width; l++) param.out(' ')?; -} - -private fn void! out_substr(PrintParam* param, char[] str) -{ - usize l = conv::utf8_codepoints(str); - uint prec = param.prec; - if (param.flags.precision && l < prec) l = prec; - param.right_adjust(' ')?; - usize index = 0; - usize chars = str.len; - char* ptr = str.ptr; - while (index < chars) - { - char c = ptr[index]; - // Break if we have precision set and we ran out... - if (c & 0xC0 != 0x80 && param.flags.precision && !prec--) break; - param.out(c)?; - index++; - } - return param.left_adjust(l); -} - -private fn usize! vsnprintf(OutputFn out, void* data, char[] format, variant[] variants) -{ - if (!out) + if (!this.out_fn) { // use null output function - out = &out_null_fn; + this.out_fn = &out_null_fn; } - PrintParam param = { .outfn = out, .buffer = data }; usize format_len = format.len; usize variant_index = 0; for (usize i = 0; i < format_len; i++) @@ -842,7 +359,7 @@ private fn usize! vsnprintf(OutputFn out, void* data, char[] format, variant[] v if (c != '%') { // no - param.out(c)?; + this.out(c)?; continue; } i++; @@ -850,20 +367,20 @@ private fn usize! vsnprintf(OutputFn out, void* data, char[] format, variant[] v c = format[i]; if (c == '%') { - param.out(c)?; + this.out(c)?; continue; } // evaluate flags - param.flags = {}; + this.flags = {}; while FLAG_EVAL: (true) { switch (c) { - case '0': param.flags.zeropad = true; - case '-': param.flags.left = true; - case '+': param.flags.plus = true; - case ' ': param.flags.space = true; - case '#': param.flags.hash = true; + case '0': this.flags.zeropad = true; + case '-': this.flags.left = true; + case '+': this.flags.plus = true; + case ' ': this.flags.space = true; + case '#': this.flags.hash = true; default: break FLAG_EVAL; } if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!; @@ -874,18 +391,18 @@ private fn usize! vsnprintf(OutputFn out, void* data, char[] format, variant[] v c = format[i]; if (w < 0) { - param.flags.left = true; + this.flags.left = true; w = -w; } - param.width = w; + this.width = w; // evaluate precision field - param.prec = 0; + this.prec = 0; if (c == '.') { - param.flags.precision = true; + this.flags.precision = true; if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!; int prec = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?; - param.prec = prec < 0 ? 0 : prec; + this.prec = prec < 0 ? 0 : prec; c = format[i]; } @@ -897,71 +414,71 @@ private fn usize! vsnprintf(OutputFn out, void* data, char[] format, variant[] v { case 'd': base = 10; - param.flags.hash = false; + this.flags.hash = false; case 'X' : - param.flags.uppercase = true; + this.flags.uppercase = true; nextcase; case 'x' : base = 16; case 'O': - param.flags.uppercase = true; + this.flags.uppercase = true; nextcase; case 'o' : base = 8; case 'B': - param.flags.uppercase = true; + this.flags.uppercase = true; nextcase; case 'b' : base = 2; case 'F' : - param.flags.uppercase = true; + this.flags.uppercase = true; nextcase; case 'f': - ftoa(¶m, float_from_variant(current))?; + this.ftoa(float_from_variant(current))?; continue; case 'E': - param.flags.uppercase = true; + this.flags.uppercase = true; nextcase; case 'e': - etoa(¶m, float_from_variant(current))?; + this.etoa(float_from_variant(current))?; continue; case 'G': - param.flags.uppercase = true; + this.flags.uppercase = true; nextcase; case 'g': - param.flags.adapt_exp = true; - etoa(¶m, float_from_variant(current))?; + this.flags.adapt_exp = true; + this.etoa(float_from_variant(current))?; continue; case 'c': - out_char(¶m, current)?; + this.out_char(current)?; continue; case 's': - out_str(¶m, current)?; + this.out_str(current)?; continue; case 'p': - param.flags.zeropad = true; - param.flags.hash = true; + this.flags.zeropad = true; + this.flags.hash = true; base = 16; default: return PrintFault.INVALID_FORMAT_STRING!; } if (base != 10) { - param.flags.plus = false; - param.flags.space = false; + this.flags.plus = false; + this.flags.space = false; } // ignore '0' flag when precision is given - if (param.flags.precision) param.flags.zeropad = false; + if (this.flags.precision) this.flags.zeropad = false; bool is_neg; NtoaType v = int_from_variant(current, &is_neg); - ntoa(¶m, v, is_neg, base)?; + this.ntoa(v, is_neg, base)?; } // termination // out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); // return written chars without terminating \0 - return param.idx; + return this.idx; } diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 6f1e058b9..156f8773b 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -642,6 +642,7 @@ void llvm_emit_xxlizer(GenContext *c, Decl *decl) scratch_buffer_clear(); scratch_buffer_printf(is_finalizer ? ".static_finalize.%u" : ".static_initialize.%u", vec_size(*array_ref)); LLVMValueRef function = LLVMAddFunction(c->module, scratch_buffer_to_string(), initializer_type); + LLVMSetLinkage(function, LLVMInternalLinkage); if (llvm_use_debug(c)) { uint32_t row = decl->span.row; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index a1ebca994..abd8a12c5 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1761,6 +1761,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s call_expr->type = type_add_optional(sum_returns, failable); } + assert(call_expr->type); if (call_expr->call_expr.result_unused) { Type *type = call_expr->type; @@ -3275,6 +3276,7 @@ CHECK_DEEPER: return false; } + assert(member->type); if (member->decl_kind == DECL_VAR) { if (member->var.kind == VARDECL_BITMEMBER) diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index a02b92e81..a54d0d2d6 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -404,10 +404,6 @@ void sema_analysis_pass_decls(Module *module) { sema_analyse_decl(&context, unit->generic_defines[i]); } - VECEACH(unit->xxlizers, i) - { - sema_analyse_decl(&context, unit->xxlizers[i]); - } sema_context_destroy(&context); } DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found); @@ -417,11 +413,16 @@ void sema_analysis_pass_functions(Module *module) { DEBUG_LOG("Pass: Function analysis %s", module->name->module); + VECEACH(module->units, index) { CompilationUnit *unit = module->units[index]; SemaContext context; sema_context_init(&context, unit); + VECEACH(unit->xxlizers, i) + { + sema_analyse_decl(&context, unit->xxlizers[i]); + } VECEACH(unit->methods, i) { analyse_func_body(&context, unit->methods[i]); diff --git a/src/version.h b/src/version.h index 266a87354..b3504b55b 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.65" \ No newline at end of file +#define COMPILER_VERSION "0.3.66" \ No newline at end of file diff --git a/test/test_suite/initialize/initialize_finalize.c3t b/test/test_suite/initialize/initialize_finalize.c3t index 708f97ac7..7e15bce28 100644 --- a/test/test_suite/initialize/initialize_finalize.c3t +++ b/test/test_suite/initialize/initialize_finalize.c3t @@ -34,7 +34,7 @@ static finalize @llvm.global_ctors = appending global [3 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 300, void ()* @.static_initialize.0, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @.static_initialize.1, i8* null }, { i32, void ()*, i8* } { i32 200, void ()* @.static_initialize.2, i8* null }] @llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @.static_finalize.0, i8* null }] -define void @.static_initialize.0() { +define internal void @.static_initialize.0() { entry: call void @puts(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str, i32 0, i32 0)) ret void @@ -43,19 +43,19 @@ entry: ; Function Attrs: nounwind declare void @puts(i8*) #0 -define void @.static_initialize.1() { +define internal void @.static_initialize.1() { entry: call void @puts(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str.1, i32 0, i32 0)) ret void } -define void @.static_initialize.2() { +define internal void @.static_initialize.2() { entry: call void @puts(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.2, i32 0, i32 0)) ret void } -define void @.static_finalize.0() { +define internal void @.static_finalize.0() { entry: call void @puts(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i32 0, i32 0)) ret void diff --git a/test/test_suite/stdlib/map.c3t b/test/test_suite/stdlib/map.c3t index b71347e7f..b35ba3a1c 100644 --- a/test/test_suite/stdlib/map.c3t +++ b/test/test_suite/stdlib/map.c3t @@ -18,7 +18,7 @@ fn char[] Foo.to_string(Foo* foo, Allocator* allocator = mem::current_allocator( static initialize { - io::printf_register_to_string(Foo); + io::formatter_register_type(Foo); } fn void main() @@ -58,9 +58,19 @@ fn void main() @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @.static_initialize.0, i8* null }] -define void @.static_initialize.0() { +define internal void @.static_initialize.0() { entry: - call void @std_io_printf_register(i64 ptrtoint (%.introspect* @"ct$test_Foo" to i64), { i8*, i64 } (i8*, %Allocator*)* bitcast ({ i8*, i64 } (%Foo*, %Allocator*)* @test_Foo_to_string to { i8*, i64 } (i8*, %Allocator*)*)) + %0 = load i64, i64* getelementptr inbounds (%HashMap, %HashMap* @std_io_tostring_functions, i32 0, i32 0, i32 1), align 8 + %not = icmp eq i64 %0, 0 + br i1 %not, label %if.then, label %if.exit + +if.then: ; preds = %entry + %1 = load %Allocator*, %Allocator** @std_core_mem_thread_allocator, align 8 + call void @"std_map$$typeid.p$std_io$ToStringFunction_HashMap_init"(%HashMap* @std_io_tostring_functions, i32 512, float 7.500000e-01, %Allocator* %1) + br label %if.exit + +if.exit: ; preds = %if.then, %entry + %2 = call i8 @"std_map$$typeid.p$std_io$ToStringFunction_HashMap_set"(%HashMap* @std_io_tostring_functions, i64 ptrtoint (%.introspect* @"ct$test_Foo" to i64), { i8*, i64 } (i8*, %Allocator*)* bitcast ({ i8*, i64 } (%Foo*, %Allocator*)* @test_Foo_to_string to { i8*, i64 } (i8*, %Allocator*)*)) ret void } @@ -125,7 +135,7 @@ voiderr: ; preds = %after_check, %entry ; Function Attrs: nounwind define void @test_main() #0 { entry: - %map = alloca %HashMap, align 8 + %map = alloca %HashMap.0, align 8 %retparam = alloca i64, align 8 %taddr = alloca %"char[]", align 8 %vararg = alloca %"variant[]", align 8 @@ -161,7 +171,7 @@ entry: %vararg75 = alloca %"variant[]", align 8 %varargslots76 = alloca [1 x %variant], align 16 %result = alloca %"Foo[]", align 8 - %map2 = alloca %HashMap.0, align 8 + %map2 = alloca %HashMap.3, align 8 %retparam82 = alloca i64, align 8 %taddr83 = alloca %"char[]", align 8 %vararg86 = alloca %"variant[]", align 8 @@ -186,7 +196,7 @@ entry: %error_var = alloca i64, align 8 %retparam130 = alloca %TempAllocator*, align 8 %mark = alloca i64, align 8 - %map3 = alloca %HashMap.0, align 8 + %map3 = alloca %HashMap.3, align 8 %error_var135 = alloca i64, align 8 %retparam136 = alloca %TempAllocator*, align 8 %retparam143 = alloca i64, align 8 @@ -194,17 +204,17 @@ entry: %vararg147 = alloca %"variant[]", align 8 %varargslots148 = alloca [1 x %variant], align 16 %result149 = alloca %"int[]", align 8 - %0 = bitcast %HashMap* %map to i8* + %0 = bitcast %HashMap.0* %map to i8* call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 40, i1 false) %1 = load %Allocator*, %Allocator** @std_core_mem_thread_allocator, align 8 - call void @"std_map$$int.test_Foo_HashMap_init"(%HashMap* %map, i32 16, float 7.500000e-01, %Allocator* %1) + call void @"std_map$$int.test_Foo_HashMap_init"(%HashMap.0* %map, i32 16, float 7.500000e-01, %Allocator* %1) store %"char[]" { i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i32 0, i32 0), i64 12 }, %"char[]"* %taddr, align 8 %2 = bitcast %"char[]"* %taddr to { i8*, i64 }* %3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %2, i32 0, i32 0 %lo = load i8*, i8** %3, align 8 %4 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %2, i32 0, i32 1 %hi = load i64, i64* %4, align 8 - %5 = getelementptr inbounds %HashMap, %HashMap* %map, i32 0, i32 2 + %5 = getelementptr inbounds %HashMap.0, %HashMap.0* %map, i32 0, i32 2 %6 = bitcast i32* %5 to i8* %7 = insertvalue %variant undef, i8* %6, 0 %8 = insertvalue %variant %7, i64 ptrtoint (%.introspect* @"ct$uint" to i64), 1 @@ -237,14 +247,14 @@ voiderr: ; preds = %after_check, %entry %lo3 = load i64, i64* %20, align 8 %21 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %19, i32 0, i32 1 %hi4 = load i8*, i8** %21, align 8 - %22 = call i8 @"std_map$$int.test_Foo_HashMap_set"(%HashMap* %map, i32 1, i64 %lo3, i8* %hi4) + %22 = call i8 @"std_map$$int.test_Foo_HashMap_set"(%HashMap.0* %map, i32 1, i64 %lo3, i8* %hi4) store %"char[]" { i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.1, i32 0, i32 0), i64 12 }, %"char[]"* %taddr6, align 8 %23 = bitcast %"char[]"* %taddr6 to { i8*, i64 }* %24 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %23, i32 0, i32 0 %lo7 = load i8*, i8** %24, align 8 %25 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %23, i32 0, i32 1 %hi8 = load i64, i64* %25, align 8 - %26 = getelementptr inbounds %HashMap, %HashMap* %map, i32 0, i32 2 + %26 = getelementptr inbounds %HashMap.0, %HashMap.0* %map, i32 0, i32 2 %27 = bitcast i32* %26 to i8* %28 = insertvalue %variant undef, i8* %27, 0 %29 = insertvalue %variant %28, i64 ptrtoint (%.introspect* @"ct$uint" to i64), 1 @@ -277,14 +287,14 @@ voiderr15: ; preds = %after_check14, %voi %lo17 = load i64, i64* %41, align 8 %42 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %40, i32 0, i32 1 %hi18 = load i8*, i8** %42, align 8 - %43 = call i8 @"std_map$$int.test_Foo_HashMap_set"(%HashMap* %map, i32 1, i64 %lo17, i8* %hi18) + %43 = call i8 @"std_map$$int.test_Foo_HashMap_set"(%HashMap.0* %map, i32 1, i64 %lo17, i8* %hi18) store %"char[]" { i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.2, i32 0, i32 0), i64 12 }, %"char[]"* %taddr20, align 8 %44 = bitcast %"char[]"* %taddr20 to { i8*, i64 }* %45 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %44, i32 0, i32 0 %lo21 = load i8*, i8** %45, align 8 %46 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %44, i32 0, i32 1 %hi22 = load i64, i64* %46, align 8 - %47 = getelementptr inbounds %HashMap, %HashMap* %map, i32 0, i32 2 + %47 = getelementptr inbounds %HashMap.0, %HashMap.0* %map, i32 0, i32 2 %48 = bitcast i32* %47 to i8* %49 = insertvalue %variant undef, i8* %48, 0 %50 = insertvalue %variant %49, i64 ptrtoint (%.introspect* @"ct$uint" to i64), 1 @@ -314,7 +324,7 @@ voiderr29: ; preds = %after_check28, %voi %lo32 = load i8*, i8** %60, align 8 %61 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %59, i32 0, i32 1 %hi33 = load i64, i64* %61, align 8 - %62 = call i64 @"std_map$$int.test_Foo_HashMap_get"(%Foo* %retparam36, %HashMap* %map, i32 1) + %62 = call i64 @"std_map$$int.test_Foo_HashMap_get"(%Foo* %retparam36, %HashMap.0* %map, i32 1) %not_err37 = icmp eq i64 %62, 0 br i1 %not_err37, label %after_check38, label %voiderr43 @@ -349,7 +359,7 @@ voiderr43: ; preds = %after_check42, %aft %lo46 = load i8*, i8** %76, align 8 %77 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %75, i32 0, i32 1 %hi47 = load i64, i64* %77, align 8 - %78 = call i8 @"std_map$$int.test_Foo_HashMap_has_key"(%HashMap* %map, i32 1) + %78 = call i8 @"std_map$$int.test_Foo_HashMap_has_key"(%HashMap.0* %map, i32 1) store i8 %78, i8* %taddr50, align 1 %79 = insertvalue %variant undef, i8* %taddr50, 0 %80 = insertvalue %variant %79, i64 ptrtoint (%.introspect* @"ct$bool" to i64), 1 @@ -379,7 +389,7 @@ voiderr55: ; preds = %after_check54, %voi %lo58 = load i8*, i8** %90, align 8 %91 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %89, i32 0, i32 1 %hi59 = load i64, i64* %91, align 8 - %92 = call i8 @"std_map$$int.test_Foo_HashMap_has_key"(%HashMap* %map, i32 2) + %92 = call i8 @"std_map$$int.test_Foo_HashMap_has_key"(%HashMap.0* %map, i32 2) store i8 %92, i8* %taddr62, align 1 %93 = insertvalue %variant undef, i8* %taddr62, 0 %94 = insertvalue %variant %93, i64 ptrtoint (%.introspect* @"ct$bool" to i64), 1 @@ -412,14 +422,14 @@ voiderr67: ; preds = %after_check66, %voi %lo69 = load i64, i64* %106, align 8 %107 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %105, i32 0, i32 1 %hi70 = load i8*, i8** %107, align 8 - %108 = call i8 @"std_map$$int.test_Foo_HashMap_set"(%HashMap* %map, i32 7, i64 %lo69, i8* %hi70) + %108 = call i8 @"std_map$$int.test_Foo_HashMap_set"(%HashMap.0* %map, i32 7, i64 %lo69, i8* %hi70) store %"char[]" { i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.6, i32 0, i32 0), i64 10 }, %"char[]"* %taddr72, align 8 %109 = bitcast %"char[]"* %taddr72 to { i8*, i64 }* %110 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %109, i32 0, i32 0 %lo73 = load i8*, i8** %110, align 8 %111 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %109, i32 0, i32 1 %hi74 = load i64, i64* %111, align 8 - %112 = call { i8*, i64 } @"std_map$$int.test_Foo_HashMap_value_list"(%HashMap* %map, %Allocator* null) + %112 = call { i8*, i64 } @"std_map$$int.test_Foo_HashMap_value_list"(%HashMap.0* %map, %Allocator* null) %113 = bitcast %"Foo[]"* %result to { i8*, i64 }* store { i8*, i64 } %112, { i8*, i64 }* %113, align 8 %114 = bitcast %"Foo[]"* %result to i8* @@ -445,18 +455,18 @@ after_check80: ; preds = %voiderr67 br label %voiderr81 voiderr81: ; preds = %after_check80, %voiderr67 - %125 = bitcast %HashMap.0* %map2 to i8* + %125 = bitcast %HashMap.3* %map2 to i8* call void @llvm.memset.p0i8.i64(i8* align 8 %125, i8 0, i64 40, i1 false) %126 = load %Allocator*, %Allocator** @std_core_mem_thread_allocator, align 8 - call void @"std_map$$int.double_HashMap_init"(%HashMap.0* %map2, i32 16, float 7.500000e-01, %Allocator* %126) - %127 = call i8 @"std_map$$int.double_HashMap_set"(%HashMap.0* %map2, i32 4, double 1.300000e+00) + call void @"std_map$$int.double_HashMap_init"(%HashMap.3* %map2, i32 16, float 7.500000e-01, %Allocator* %126) + %127 = call i8 @"std_map$$int.double_HashMap_set"(%HashMap.3* %map2, i32 4, double 1.300000e+00) store %"char[]" { i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.7, i32 0, i32 0), i64 12 }, %"char[]"* %taddr83, align 8 %128 = bitcast %"char[]"* %taddr83 to { i8*, i64 }* %129 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %128, i32 0, i32 0 %lo84 = load i8*, i8** %129, align 8 %130 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %128, i32 0, i32 1 %hi85 = load i64, i64* %130, align 8 - %131 = call i8 @"std_map$$int.double_HashMap_has_value"(%HashMap.0* %map2, double 1.300000e+00) + %131 = call i8 @"std_map$$int.double_HashMap_has_value"(%HashMap.3* %map2, double 1.300000e+00) store i8 %131, i8* %taddr88, align 1 %132 = insertvalue %variant undef, i8* %taddr88, 0 %133 = insertvalue %variant %132, i64 ptrtoint (%.introspect* @"ct$bool" to i64), 1 @@ -486,7 +496,7 @@ voiderr93: ; preds = %after_check92, %voi %lo96 = load i8*, i8** %143, align 8 %144 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %142, i32 0, i32 1 %hi97 = load i64, i64* %144, align 8 - %145 = call i8 @"std_map$$int.double_HashMap_has_value"(%HashMap.0* %map2, double 1.200000e+00) + %145 = call i8 @"std_map$$int.double_HashMap_has_value"(%HashMap.3* %map2, double 1.200000e+00) store i8 %145, i8* %taddr100, align 1 %146 = insertvalue %variant undef, i8* %taddr100, 0 %147 = insertvalue %variant %146, i64 ptrtoint (%.introspect* @"ct$bool" to i64), 1 @@ -510,14 +520,14 @@ after_check104: ; preds = %voiderr93 br label %voiderr105 voiderr105: ; preds = %after_check104, %voiderr93 - %156 = call i8 @"std_map$$int.double_HashMap_set"(%HashMap.0* %map2, i32 100, double 3.400000e+00) + %156 = call i8 @"std_map$$int.double_HashMap_set"(%HashMap.3* %map2, i32 100, double 3.400000e+00) store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.9, i32 0, i32 0), i64 2 }, %"char[]"* %taddr107, align 8 %157 = bitcast %"char[]"* %taddr107 to { i8*, i64 }* %158 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %157, i32 0, i32 0 %lo108 = load i8*, i8** %158, align 8 %159 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %157, i32 0, i32 1 %hi109 = load i64, i64* %159, align 8 - %160 = call { i8*, i64 } @"std_map$$int.double_HashMap_key_list"(%HashMap.0* %map2, %Allocator* null) + %160 = call { i8*, i64 } @"std_map$$int.double_HashMap_key_list"(%HashMap.3* %map2, %Allocator* null) %161 = bitcast %"int[]"* %result112 to { i8*, i64 }* store { i8*, i64 } %160, { i8*, i64 }* %161, align 8 %162 = bitcast %"int[]"* %result112 to i8* @@ -549,7 +559,7 @@ voiderr117: ; preds = %after_check116, %vo %lo120 = load i8*, i8** %174, align 8 %175 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %173, i32 0, i32 1 %hi121 = load i64, i64* %175, align 8 - %176 = call { i8*, i64 } @"std_map$$int.double_HashMap_value_list"(%HashMap.0* %map2, %Allocator* null) + %176 = call { i8*, i64 } @"std_map$$int.double_HashMap_value_list"(%HashMap.3* %map2, %Allocator* null) %177 = bitcast %"double[]"* %result124 to { i8*, i64 }* store { i8*, i64 } %176, { i8*, i64 }* %177, align 8 %178 = bitcast %"double[]"* %result124 to i8* @@ -607,7 +617,7 @@ if.exit: ; preds = %noerr_block, %voide %194 = getelementptr inbounds %TempAllocator, %TempAllocator* %193, i32 0, i32 3 %195 = load i64, i64* %194, align 8 store i64 %195, i64* %mark, align 8 - %196 = bitcast %HashMap.0* %map3 to i8* + %196 = bitcast %HashMap.3* %map3 to i8* call void @llvm.memset.p0i8.i64(i8* align 8 %196, i8 0, i64 40, i1 false) %197 = load %TempAllocator*, %TempAllocator** @std_core_mem_thread_temp_allocator, align 8 %not133 = icmp eq %TempAllocator* %197, null @@ -637,16 +647,16 @@ noerr_block141: ; preds = %after_check139 if.exit142: ; preds = %noerr_block141, %if.exit %200 = load %TempAllocator*, %TempAllocator** @std_core_mem_thread_temp_allocator, align 8 %ptrptr = bitcast %TempAllocator* %200 to %Allocator* - call void @"std_map$$int.double_HashMap_init"(%HashMap.0* %map3, i32 16, float 7.500000e-01, %Allocator* %ptrptr) - %201 = call i8 @"std_map$$int.double_HashMap_set"(%HashMap.0* %map3, i32 5, double 3.200000e+00) - %202 = call i8 @"std_map$$int.double_HashMap_set"(%HashMap.0* %map3, i32 7, double 5.200000e+00) + call void @"std_map$$int.double_HashMap_init"(%HashMap.3* %map3, i32 16, float 7.500000e-01, %Allocator* %ptrptr) + %201 = call i8 @"std_map$$int.double_HashMap_set"(%HashMap.3* %map3, i32 5, double 3.200000e+00) + %202 = call i8 @"std_map$$int.double_HashMap_set"(%HashMap.3* %map3, i32 7, double 5.200000e+00) store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.16, i32 0, i32 0), i64 2 }, %"char[]"* %taddr144, align 8 %203 = bitcast %"char[]"* %taddr144 to { i8*, i64 }* %204 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %203, i32 0, i32 0 %lo145 = load i8*, i8** %204, align 8 %205 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %203, i32 0, i32 1 %hi146 = load i64, i64* %205, align 8 - %206 = call { i8*, i64 } @"std_map$$int.double_HashMap_key_tlist"(%HashMap.0* %map3) + %206 = call { i8*, i64 } @"std_map$$int.double_HashMap_key_tlist"(%HashMap.3* %map3) %207 = bitcast %"int[]"* %result149 to { i8*, i64 }* store { i8*, i64 } %206, { i8*, i64 }* %207, align 8 %208 = bitcast %"int[]"* %result149 to i8* diff --git a/test/test_suite2/initialize/initialize_finalize.c3t b/test/test_suite2/initialize/initialize_finalize.c3t index 9b1fbbb89..a3df1e4e2 100644 --- a/test/test_suite2/initialize/initialize_finalize.c3t +++ b/test/test_suite2/initialize/initialize_finalize.c3t @@ -34,7 +34,7 @@ static finalize @llvm.global_ctors = appending global [3 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 300, ptr @.static_initialize.0, ptr null }, { i32, ptr, ptr } { i32 65535, ptr @.static_initialize.1, ptr null }, { i32, ptr, ptr } { i32 200, ptr @.static_initialize.2, ptr null }] @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @.static_finalize.0, ptr null }] -define void @.static_initialize.0() { +define internal void @.static_initialize.0() { entry: call void @puts(ptr @.str) ret void @@ -43,19 +43,19 @@ entry: ; Function Attrs: nounwind declare void @puts(ptr) #0 -define void @.static_initialize.1() { +define internal void @.static_initialize.1() { entry: call void @puts(ptr @.str.1) ret void } -define void @.static_initialize.2() { +define internal void @.static_initialize.2() { entry: call void @puts(ptr @.str.2) ret void } -define void @.static_finalize.0() { +define internal void @.static_finalize.0() { entry: call void @puts(ptr @.str.3) ret void diff --git a/test/test_suite2/stdlib/map.c3t b/test/test_suite2/stdlib/map.c3t index 27b4825e2..5e16d565a 100644 --- a/test/test_suite2/stdlib/map.c3t +++ b/test/test_suite2/stdlib/map.c3t @@ -18,7 +18,7 @@ fn char[] Foo.to_string(Foo* foo, Allocator* allocator = mem::current_allocator( static initialize { - io::printf_register_to_string(Foo); + io::formatter_register_type(Foo); } fn void main() @@ -54,13 +54,21 @@ fn void main() } /* #expect: test.ll - - @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @.static_initialize.0, ptr null }] -define void @.static_initialize.0() { +define internal void @.static_initialize.0() { entry: - call void @std_io_printf_register(i64 ptrtoint (ptr @"ct$test_Foo" to i64), ptr @test_Foo_to_string) + %0 = load i64, ptr getelementptr inbounds (%"Entry*[]", ptr @std_io_tostring_functions, i32 0, i32 1), align 8 + %not = icmp eq i64 %0, 0 + br i1 %not, label %if.then, label %if.exit + +if.then: ; preds = %entry + %1 = load ptr, ptr @std_core_mem_thread_allocator, align 8 + call void @"std_map$$typeid.p$std_io$ToStringFunction_HashMap_init"(ptr @std_io_tostring_functions, i32 512, float 7.500000e-01, ptr %1) + br label %if.exit + +if.exit: ; preds = %if.then, %entry + %2 = call i8 @"std_map$$typeid.p$std_io$ToStringFunction_HashMap_set"(ptr @std_io_tostring_functions, i64 ptrtoint (ptr @"ct$test_Foo" to i64), ptr @test_Foo_to_string) ret void } @@ -117,7 +125,7 @@ voiderr: ; preds = %after_check, %entry ; Function Attrs: nounwind define void @test_main() #0 { entry: - %map = alloca %HashMap, align 8 + %map = alloca %HashMap.0, align 8 %retparam = alloca i64, align 8 %taddr = alloca %"char[]", align 8 %vararg = alloca %"variant[]", align 8 @@ -153,7 +161,7 @@ entry: %vararg75 = alloca %"variant[]", align 8 %varargslots76 = alloca [1 x %variant], align 16 %result = alloca %"Foo[]", align 8 - %map2 = alloca %HashMap.0, align 8 + %map2 = alloca %HashMap.3, align 8 %retparam82 = alloca i64, align 8 %taddr83 = alloca %"char[]", align 8 %vararg86 = alloca %"variant[]", align 8 @@ -178,7 +186,7 @@ entry: %error_var = alloca i64, align 8 %retparam130 = alloca ptr, align 8 %mark = alloca i64, align 8 - %map3 = alloca %HashMap.0, align 8 + %map3 = alloca %HashMap.3, align 8 %error_var135 = alloca i64, align 8 %retparam136 = alloca ptr, align 8 %retparam143 = alloca i64, align 8 @@ -194,7 +202,7 @@ entry: %lo = load ptr, ptr %1, align 8 %2 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 1 %hi = load i64, ptr %2, align 8 - %3 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2 + %3 = getelementptr inbounds %HashMap.0, ptr %map, i32 0, i32 2 %4 = insertvalue %variant undef, ptr %3, 0 %5 = insertvalue %variant %4, i64 ptrtoint (ptr @"ct$uint" to i64), 1 %6 = getelementptr inbounds [1 x %variant], ptr %varargslots, i64 0, i64 0 @@ -229,7 +237,7 @@ voiderr: ; preds = %after_check, %entry %lo7 = load ptr, ptr %17, align 8 %18 = getelementptr inbounds { ptr, i64 }, ptr %taddr6, i32 0, i32 1 %hi8 = load i64, ptr %18, align 8 - %19 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2 + %19 = getelementptr inbounds %HashMap.0, ptr %map, i32 0, i32 2 %20 = insertvalue %variant undef, ptr %19, 0 %21 = insertvalue %variant %20, i64 ptrtoint (ptr @"ct$uint" to i64), 1 %22 = getelementptr inbounds [1 x %variant], ptr %varargslots10, i64 0, i64 0 @@ -264,7 +272,7 @@ voiderr15: ; preds = %after_check14, %voi %lo21 = load ptr, ptr %33, align 8 %34 = getelementptr inbounds { ptr, i64 }, ptr %taddr20, i32 0, i32 1 %hi22 = load i64, ptr %34, align 8 - %35 = getelementptr inbounds %HashMap, ptr %map, i32 0, i32 2 + %35 = getelementptr inbounds %HashMap.0, ptr %map, i32 0, i32 2 %36 = insertvalue %variant undef, ptr %35, 0 %37 = insertvalue %variant %36, i64 ptrtoint (ptr @"ct$uint" to i64), 1 %38 = getelementptr inbounds [1 x %variant], ptr %varargslots24, i64 0, i64 0