diff --git a/lib/std/io_printf.c3 b/lib/std/io_printf.c3 index 5bde03dca..e4bb8d6b3 100644 --- a/lib/std/io_printf.c3 +++ b/lib/std/io_printf.c3 @@ -1,8 +1,18 @@ module std::io; import libc; -const PRINTF_NTOA_BUFFER_SIZE = 123; +const int PRINTF_NTOA_BUFFER_SIZE = 256; +const int PRINTF_FTOA_BUFFER_SIZE = 256; +const float PRINTF_MAX_FLOAT = 1e9; +const uint PRINTF_DEFAULT_FLOAT_PRECISION = 6; +fault PrintFault +{ + BUFFER_EXCEEDED, + INTERNAL_BUFFER_EXCEEDED, + INVALID_FORMAT_STRING, + MISSING_ARG, +} bitstruct PrintFlags : uint { bool zeropad : 0; @@ -15,150 +25,70 @@ bitstruct PrintFlags : uint bool adapt_exp : 7; } -/* -private macro usize out_rev(#output, usize idx, char* buf, uint len, uint width, PrintFlags flags) +define OutputFn = fn void!(char c, char[] buffer, usize buffer_idx); + + +private fn usize! out_str(OutputFn out, char[] buffer, usize idx, variant arg, uint prec, uint width, PrintFlags flags) { - usize start_idx = idx; - // pad spaces up to given width - if (!flags.left && !flags.zeropad) - { - for (usize i = len; i < width; i++) - { - output(' '); - idx++; - } - } - - while (len) + switch (arg.type.kind) { - output(buf[--len]); - idx++; - } - - // append pad spaces up to given width - if (flags & FLAGS_LEFT) - { - while (idx - start_idx < width) - { - output(' '); - idx++; - } - } - return idx; -} - -private macro usize ntoa_format(#output, uint idx, uint len, char* buf, uint prec, bool is_negative, PrintFlags flags) -{ - if (!flags.left) - { - if (width && flags.zeropad && (is_negative || flags.plus || flags.space)) - { - width--; - } - while (len < prec && len < PRINTF_NTOA_BUFFER_SIZE) buf[len++] = '0'; - if (flags.zeropad) - { - while (len < width && len < PRINTF_NTOA_BUFFER_SIZE) buf[len++] = '0'; - } - } - if (flags.hash) - { - if (!flags.precision && len && (len == prec || len == width)) - { - len--; - if (len && base == 16) + case TYPEID: + return out_substr(out, buffer, idx, "", prec, width, flags); + case VOID: + return out_substr(out, buffer, idx, "void", prec, width, flags); + case ANYERR: + return out_substr(out, buffer, idx, "", prec, width, flags); + case VARIANT: + return out_substr(out, buffer, idx, "", prec, width, flags); + case ENUM: + return out_substr(out, buffer, idx, "", prec, width, flags); + case FAULT: + return out_substr(out, buffer, idx, "", prec, width, flags); + case STRUCT: + return out_substr(out, buffer, idx, "", prec, width, flags); + case UNION: + return out_substr(out, buffer, idx, "", prec, width, flags); + case BITSTRUCT: + return out_substr(out, buffer, idx, "", prec, width, flags); + case FUNC: + return out_substr(out, buffer, idx, "", prec, width, flags); + case FAILABLE: + return out_substr(out, buffer, idx, "", prec, width, flags); + case DISTINCT: + return out_substr(out, buffer, idx, "", prec, width, flags); + case POINTER: + typeid inner = arg.type.inner; + if (inner.kind == TypeKind.ARRAY && inner.inner == char.typeid) { - len--; + char *ptr = *(char**)arg.ptr; + return out_substr(out, buffer, idx, ptr[0..inner.len - 1], prec, width, flags); } - } - if (len < PRINTF_NTOA_BUFFER_SIZE) - { - switch (base) + return ntoa_variant(out, buffer, idx, arg, 16, prec, width, flags); + case SIGNED_INT: + case UNSIGNED_INT: + return ntoa_variant(out, buffer, idx, arg, 10, prec, width, flags); + case FLOAT: + return ftoa(out, buffer, idx, float_from_variant(arg), prec, width, flags); + case ARRAY: + return out_substr(out, buffer, idx, "[]", prec, width, flags); + case VECTOR: + return out_substr(out, buffer, idx, "[]", prec, width, flags); + case SUBARRAY: + return out_substr(out, buffer, idx, *(char[]*)arg.ptr, prec, width, flags); + case BOOL: + if (*(bool*)arg.ptr) { - case 16: - buf[len++] = flags.uppercase ? 'X' : 'x'; - case 8: - buf[len++] = flags.uppercase ? 'O' : 'o'; - case 2: - buf[len++] = flags.uppercase ? 'B' : 'b'; - default: - buf[len++] = '0'; + return out_substr(out, buffer, idx, "true", prec, width, flags); + } + else + { + return out_substr(out, buffer, idx, "false", prec, width, flags); } - } - } - if (len < PRINTF_NTOA_BUFFER_SIZE) - { - switch (true) - { - case is_negative: - buf[len++] = '-'; - case flags.plus: - buf[len++] = '+'; - case flags.space: - buf[len++] = ' '; - } - } - return out_rev(#output, idx, maxlen, buf, len, width, flags); -} -*/ - -private macro usize! @printf_integer(#output, variant, usize idx, uint precision, uint width, PrintFlags flags) -{ - return 0; -} - -private macro usize! @printf_efloat(#output, variant, usize idx, uint precision, uint width, PrintFlags flags) -{ - return 0; -} - -private macro usize! @printf_char(#output, variant, usize idx, uint precision, uint width, PrintFlags flags) -{ - return 0; -} - -private macro usize! @printf_ptr(#output, variant, usize idx, uint precision, uint width, PrintFlags flags) -{ - return 0; -} - -private macro usize! @printf_str(#output, variant, usize idx, uint precision, uint width, PrintFlags flags) -{ - return 0; -} - -private macro void @format_print_str_variant(#output, variant arg) -{ - if (arg.type.kind == TypeKind.POINTER) - { - libc::printf("%p", *((void**)arg)); - return; - } - switch (arg) - { - case char[]: - @format_print(#output, *arg); - case bool: - @format_print(#output, *arg ? (char[])"true" : (char[])"false"); - case long: - libc::printf("%lld", (CLongLong)*arg); - case int: - libc::printf("%d", *arg); - case short: - libc::printf("%d", *arg); - case ichar: - libc::printf("%d", *arg); - case char: - libc::printf("%u", *arg); default: - print("Invalid type"); + return out_substr(out, buffer, idx, "Invalid type", prec, width, flags); } } -private macro void @format_print(#output, char[] str) -{ - foreach (char c : str) #output(c); -} private fn uint simple_atoi(char* buf, usize maxlen, usize* len_ptr) @inline { @@ -186,7 +116,7 @@ fault FormattingFault 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!; + if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT!; } private fn variant! next_variant(variant* args_ptr, usize args_len, usize* arg_index_ptr) @inline @@ -195,7 +125,7 @@ private fn variant! next_variant(variant* args_ptr, usize args_len, usize* arg_i return args_ptr[(*arg_index_ptr)++]; } -private fn uint! printf_parse_format_field(variant* args_ptr, usize args_len, usize* args_index_ptr, char* format_ptr, usize format_len, usize* index_ptr) @inline +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); @@ -203,248 +133,733 @@ private fn uint! printf_parse_format_field(variant* args_ptr, usize args_len, us printf_advance_format(format_len, index_ptr)?; variant val = next_variant(args_ptr, args_len, args_index_ptr)?; if (!types::kind_is_int(val.type.kind)) return FormattingFault.INVALID_WIDTH_ARG!; - uint! intval = types::variant_to_int(val, uint); + uint! intval = types::variant_to_int(val, int); if (catch intval) return FormattingFault.INVALID_WIDTH_ARG!; return intval; } -private macro usize! @printf_generic(#output, char[] format, variant[] args) + +private fn void! out_buffer_fn(char c, char[] buffer, usize buffer_idx) { - usize idx = 0; - char* format_ptr = format.ptr; + if (buffer_idx >= buffer.len) return PrintFault.BUFFER_EXCEEDED!; + buffer[buffer_idx] = c; +} + +private fn void! out_null_fn(char c @unused, char[] buffer @unused, usize idx @unused) +{ +} + +private fn void! out_putchar_fn(char c, char[] buffer @unused, usize idx @unused) +{ + libc::putchar(c); +} + +private fn usize! out_reverse(OutputFn out, char[] out_buffer, usize buffer_idx, char[] buf, uint width, PrintFlags flags) +{ + usize buffer_start_idx = buffer_idx; + usize len = buf.len; + // pad spaces up to given width + if (!flags.left && !flags.zeropad) + { + for (usize i = len; i < width; i++) + { + out(' ', out_buffer, buffer_idx++)?; + } + } + // reverse string + while (len) out(buf[--len], out_buffer, buffer_idx++)?; + + // append pad spaces up to given width + if (flags.left) + { + while (buffer_idx - buffer_start_idx < width) + { + out(' ', out_buffer, buffer_idx++)?; + } + } + return buffer_idx; +} + +private fn usize! out_char(OutputFn out, char[] buffer, usize idx, variant arg, uint width, PrintFlags flags) +{ + uint l = 1; + // pre padding + if (!flags.left) + { + while (l++ < width) + { + out(' ', buffer, idx++); + } + } + // char output + Char32 c = types::variant_to_int(arg, uint) ?? 0xFFFD; + switch (true) + { + case c < 0x7f: + out((char)c, buffer, idx++); + case c < 0x7ff: + out((char)(0xC0 | c >> 6), buffer, idx++); + out((char)(0x80 | (c & 0x3F)), buffer, idx++); + case c < 0xffff: + out((char)(0xE0 | c >> 12), buffer, idx++); + out((char)(0x80 | (c >> 6 & 0x3F)), buffer, idx++); + out((char)(0x80 | (c & 0x3F)), buffer, idx++); + default: + out((char)(0xF0 | c >> 18), buffer, idx++); + out((char)(0x80 | (c >> 12 & 0x3F)), buffer, idx++); + out((char)(0x80 | (c >> 6 & 0x3F)), buffer, idx++); + out((char)(0x80 | (c & 0x3F)), buffer, idx++); + } + if (flags.left) + { + while (l++ < width) + { + out(' ', buffer, idx++); + } + } + return idx; +} + +private fn usize! ntoa_format(OutputFn out, char[] out_buffer, usize buffer_idx, char[] buf, usize len, bool negative, uint base, uint prec, uint width, PrintFlags flags) +{ + // pad leading zeros + if (!flags.left) + { + if (width && flags.zeropad && (negative || flags.plus || flags.space)) width--; + while (len < prec) + { + if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '0'; + } + while (flags.zeropad && len < width) + { + if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '0'; + } + } + + // handle hash + if (flags.hash && base != 10) + { + if (!flags.precision && len && len == prec && len == width) + { + len--; + if (len) len--; + } + if (base != 10) + { + if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + switch (base) + { + case 16: + buf[len++] = flags.uppercase ? 'X' : 'x'; + case 8: + buf[len++] = flags.uppercase ? 'O' : 'o'; + case 2: + buf[len++] = 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 flags.plus: + if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '+'; + case flags.space: + if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = ' '; + } + if (!len) return buffer_idx; + return out_reverse(out, out_buffer, buffer_idx, buf[0..len - 1], width, flags); +} + +$if (env::I128_SUPPORT): +define NtoaType = uint128; +$else: +define NtoaType = ulong; +$endif; + +private fn usize! ntoa_variant(OutputFn out, char[] out_buffer, usize buffer_idx, variant arg, uint base, uint prec, uint width, PrintFlags flags) +{ + bool is_neg; + NtoaType val = int_from_variant(arg, &is_neg); + return ntoa(out, out_buffer, buffer_idx, val, is_neg, base, prec, width, flags) @inline; +} + +private fn usize! ntoa(OutputFn out, char[] out_buffer, usize buffer_idx, NtoaType value, bool negative, uint base, uint prec, uint width, PrintFlags flags) +{ + char[PRINTF_NTOA_BUFFER_SIZE] buf = void; + usize len = 0; + + // no hash for 0 values + if (!value) flags.hash = false; + + // write if precision != 0 or value is != 0 + if (!flags.precision || value) + { + char past_10 = (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(out, out_buffer, buffer_idx, buf[..PRINTF_NTOA_BUFFER_SIZE - 1], len, negative, base, prec, width, flags); +} + + +define FloatType = double; + +// internal ftoa for fixed decimal floating point +private fn usize! ftoa(OutputFn out, char[] buffer, usize idx, FloatType value, uint prec, uint width, PrintFlags flags) +{ + 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 out_reverse(out, buffer, idx, "nan", width, flags); + } + if (value < -FloatType.max) + { + return out_reverse(out, buffer, idx, "fni-", width, flags); + } + if (value > FloatType.max) + { + if (flags.plus) + { + return out_reverse(out, buffer, idx, "fni+", width, flags); + } + return out_reverse(out, buffer, idx, "fni", width, flags); + } + + // 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(out, buffer, idx, value, prec, width, flags); + } + + // test for negative + bool negative = value < 0; + if (negative) value = 0 - value; + + // set default precision, if not set explicitly + if (!flags.precision) prec = PRINTF_DEFAULT_FLOAT_PRECISION; + + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while (prec > 9) + { + if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '0'; + prec--; + } + + // Safe due to 1e9 limit. + int whole = (int)value; + FloatType tmp = (value - whole) * POW10[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[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 (!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 = 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 (!flags.left && flags.zeropad) + { + if (width && (negative || flags.plus || flags.space)) width--; + while (len < width) + { + if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = '0'; + } + } + + char next = {| + if (negative) return '-'; + if (flags.plus) return '+'; + if (flags.space) return ' '; + return 0; + |}; + if (next) + { + if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!; + buf[len++] = next; + } + return out_reverse(out, buffer, idx, buf[..len-1], width, flags); +} + +union ConvUnion +{ + ulong u; + double f; +} + +private fn usize! etoa(OutputFn out, char[] buffer, usize idx, FloatType value, uint prec, uint width, PrintFlags flags) +{ + // check for NaN and special values + if (value != value || value < FloatType.min || value > FloatType.max) + { + return ftoa(out, buffer, idx, value, prec, width, flags); + } + + // determine the sign + bool negative = value < 0; + if (negative) value = -value; + + // default precision + if (!flags.precision) + { + 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 (flags.adapt_exp) + { + // do we want to fall-back to "%f" mode? + if (value >= 1e-4 && value < 1e6) + { + prec = prec > expval ? prec - expval - 1 : 0; + 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 (prec > 0 && flags.precision) prec--; + } + } + + // Adjust width + uint fwidth = width > minwidth ? width - minwidth : 0; + + // if we're padding on the right, DON'T pad the floating part + if (flags.left && minwidth) fwidth = 0; + + // rescale the float value + if (expval) value /= conv.f; + + // output the floating part + usize start_idx = idx; + PrintFlags ftoa_flags = flags; + flags.adapt_exp = false; + idx = ftoa(out, buffer, idx, negative ? -value : value, prec, fwidth, ftoa_flags)?; + + // output the exponent part + if (minwidth) + { + // output the exponential symbol + out(flags.uppercase ? 'E' : 'e', buffer, idx++); + // output the exponent value + idx = ntoa(out, buffer, idx, (NtoaType)(expval < 0 ? -expval : expval), expval < 0, 10, 0, minwidth - 1, { .zeropad = true, .plus = true } )?; + // might need to right-pad spaces + if (flags.left) + { + while (idx - start_idx < width) out(' ', buffer, idx++); + } + } + return 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 (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) ? -val : 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 (NtoaType)((*is_neg = val < 0) ? -val : val); + case short: + int val = *arg; + return (NtoaType)((*is_neg = val < 0) ? -val : val); + case int: + int val = *arg; + return (NtoaType)((*is_neg = val < 0) ? -val : val); + case long: + long val = *arg; + return (NtoaType)((*is_neg = val < 0) ? -val : 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...) +{ + return vsnprintf(&out_putchar_fn, " ", format, args); +} + +private fn usize! out_substr(OutputFn out, char[] buffer, usize idx, char[] str, uint prec, uint width, PrintFlags flags) +{ + usize l = conv::utf8_codepoints(str); + if (flags.precision && l < prec) l = prec; + if (!flags.left) + { + while (l++ < width) out(' ', buffer, idx++)?; + } + 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 && flags.precision && !prec--) break; + out(c, buffer, idx++); + index++; + } + if (flags.left) + { + while (l++ < width) out(' ', buffer, idx++)?; + } + return idx; +} + +private fn usize! vsnprintf(OutputFn out, char[] buffer, char[] format, variant[] variants) +{ + usize idx; + + if (!buffer) + { + // use null output function + out = &out_null_fn; + } usize format_len = format.len; - usize arg_index = 0; + usize variant_index = 0; for (usize i = 0; i < format_len; i++) { - char c = format_ptr[i]; + // format specifier? %[flags][width][.precision][length] + char c = format[i]; if (c != '%') { - #output(c); - idx++; + // no + out(c, buffer, idx++)?; continue; } - printf_advance_format(format_len, &i)?; - c = format_ptr[i]; - if (c == '%') - { - #output(c); - idx++; - continue; - } - // format specifier? %[flags][width][.precision][length] - PrintFlags flags; - while FLAG_EVAL: (1) - { - switch (c) - { - case '0': - flags.zeropad = true; - case '-': - flags.left = true; - case '+': - flags.plus = true; - case ' ': - flags.space = true; - case '#': - flags.hash = true; - default: - break FLAG_EVAL; - } - printf_advance_format(format_len, &i)?; - c = format_ptr[i]; - } - // evaluate width field - uint width = printf_parse_format_field(args.ptr, args.len, &arg_index, format_ptr, format_len, &i)?; - c = format_ptr[i]; + i++; + if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING!; + c = format[i]; + if (c == '%') + { + out(c, buffer, idx++)?; + continue; + } + // evaluate flags + PrintFlags flags; + while FLAG_EVAL: (true) + { + switch (c) + { + case '0': flags.zeropad = true; + case '-': flags.left = true; + case '+': flags.plus = true; + case ' ': flags.space = true; + case '#': flags.hash = true; + default: break FLAG_EVAL; + } + if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!; + c = format[i]; + } + + // evaluate width field + int width = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?; + c = format[i]; + if (width < 0) + { + flags.left = true; + width = -width; + } + + // evaluate precision field uint precision = 0; if (c == '.') { flags.precision = true; - printf_advance_format(format_len, &i)?; - precision = printf_parse_format_field(args.ptr, args.len, &arg_index, format_ptr, format_len, &i)?; - c = format_ptr[i]; - } + 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)?; + precision = prec < 0 ? 0 : prec; + c = format[i]; + } - uint base = void; - switch (c) - { - case 'd': - flags.hash = false; - base = 10; - case 'X': - flags.uppercase = true; - nextcase; - case 'x': - base = 16; - case 'o': - base = 8; - case 'b': - base = 2; - case 'F': - flags.uppercase = true; - nextcase; - case 'f': - idx = @printf_efloat(#output, next_variant(args.ptr, args.len, &arg_index), idx, precision, width, flags)?; - continue; - case 'E': + // evaluate specifier + uint base = 0; + if (variant_index >= variants.len) return PrintFault.MISSING_ARG!; + variant current = variants[variant_index++]; + switch (c) + { + case 'd': + base = 10; + flags.hash = false; + case 'X' : flags.uppercase = true; nextcase; - case 'e': - idx = @printf_efloat(#output, next_variant(args.ptr, args.len, &arg_index), idx, precision, width, flags)?; + case 'x' : + base = 16; + case 'O': + flags.uppercase = true; + nextcase; + case 'o' : + base = 8; + case 'B': + flags.uppercase = true; + nextcase; + case 'b' : + base = 2; + case 'F' : + flags.uppercase = true; + nextcase; + case 'f': + idx = ftoa(out, buffer, idx, float_from_variant(current), precision, width, flags)?; + continue; + case 'E': + flags.uppercase = true; + nextcase; + case 'e': + idx = etoa(out, buffer, idx, float_from_variant(current), precision, width, flags)?; continue; case 'G': flags.uppercase = true; nextcase; case 'g': flags.adapt_exp = true; - nextcase 'e'; + idx = etoa(out, buffer, idx, float_from_variant(current), precision, width, flags)?; + continue; case 'c': - idx = @printf_char(#output, next_variant(args.ptr, args.len, &arg_index), idx, precision, width, flags)?; + idx = out_char(out, buffer, idx, current, width, flags)?; + continue; + case 's': + idx = out_str(out, buffer, idx, current, precision, width, flags)?; continue; case 'p': - idx = @printf_ptr(#output, next_variant(args.ptr, args.len, &arg_index), idx, precision, width, flags)?; - continue; - case 's': - idx = @printf_str(#output, next_variant(args.ptr, args.len, &arg_index), idx, precision, width, flags)?; - continue; - case 'n': - idx = @printf_str(#output, &&"\n", idx, precision, width, flags)?; - continue; - + width = (uint)(void*.sizeof * 2); + flags.zeropad = true; + flags.hash = true; + base = 16; default: - return FormattingFault.INVALID_FORMAT_TYPE!; - } - if (base != 10) - { - flags.plus = false; - flags.space = false; - } - if (flags.precision) flags.zeropad = false; - idx = @printf_integer(#output, next_variant(args.ptr, args.len, &arg_index), idx, precision, width, flags)?; + return PrintFault.INVALID_FORMAT_STRING!; + } + if (base != 10) + { + flags.plus = false; + flags.space = false; + } + // ignore '0' flag when precision is given + if (flags.precision) flags.zeropad = false; + + bool is_neg; + NtoaType v = int_from_variant(current, &is_neg); + + ntoa(out, buffer, idx, v, is_neg, base, precision, width, flags)?; } - return idx; /* - precision = 0U; - - case 'c' : { - unsigned int l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // char output - out((char)va_arg(va, int), buffer, idx++, maxlen); - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 's' : { - const char* p = va_arg(va, char*); - unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); - // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { - out(*(p++), buffer, idx++, maxlen); - } - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 'p' : { - width = sizeof(void*) * 2U; - flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; -#if defined(PRINTF_SUPPORT_LONG_LONG) - const bool is_ll = sizeof(uintptr_t) == sizeof(long long); - if (is_ll) { - idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); - } - else { -#endif - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); -#if defined(PRINTF_SUPPORT_LONG_LONG) - } -#endif - format++; - break; - } - - case '%' : - out('%', buffer, idx++, maxlen); - format++; - break; - - default : - out(*format, buffer, idx++, maxlen); - format++; - break; - } - } - - // termination - out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); - - // return written chars without terminating \0 - return (int)idx;*/ -} - -private macro void @formatting(#output, char[] format, variant[] args) -{ - usize len = format.len; - usize arg_len = args.len; - usize current_arg = 0; - for (usize i = 0; i < len; i++) - { - char c = format[i]; - if (c != '%' || i == len - 1) - { - #output(c); - continue; - } - c = format[++i]; - switch (c) - { - case 's': - if (current_arg >= arg_len) - { - @format_print(#output, ""); - continue; + case 's' : { + const char* p = va_arg(va, char*); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); } - @format_print_str_variant(#output, args[current_arg++]); - default: - @format_print(#output, "Unknown specifier"); - } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + + + } +*/ + // termination +// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; } -fn void printf2(char[] format, args...) -{ - @printf_generic(putchar, format, args)!!; -} - -fn void printf(char[] format, args... ) -{ - @formatting(putchar, format, args); -} \ No newline at end of file diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 91f9aab56..4f4ad6ba7 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -228,7 +228,6 @@ bool expr_is_pure(Expr *expr) switch (expr->expr_kind) { case EXPR_BUILTIN: - case EXPR_TYPEID_KIND: return false; case EXPR_COMPILER_CONST: case EXPR_CONST: @@ -294,6 +293,8 @@ bool expr_is_pure(Expr *expr) if (!expr_is_pure(expr->expression_list[i])) return false; } return true; + case EXPR_TYPEID_INFO: + return exprid_is_pure(expr->typeid_info_expr.parent); case EXPR_TYPEOFANY: case EXPR_CT_EVAL: return expr_is_pure(expr->inner_expr); @@ -329,7 +330,7 @@ bool expr_is_simple(Expr *expr) expr = expr->inner_expr; goto RETRY; case EXPR_TERNARY: - return false; + return expr_is_simple(exprptr(expr->ternary_expr.else_expr)) && expr_is_simple(exprptr(expr->ternary_expr.then_expr)); case EXPR_RETHROW: expr = expr->rethrow_expr.inner; goto RETRY; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 7cdada7d8..273420200 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -909,6 +909,12 @@ typedef struct }; } ExprVariantSwitch; +typedef struct +{ + ExprId parent; + TypeIdInfoKind kind; +} ExprTypeidInfo; + typedef struct { Decl *argc; @@ -922,6 +928,7 @@ struct Expr_ ExprKind expr_kind : 8; ResolveStatus resolve_status : 4; union { + ExprTypeidInfo typeid_info_expr; ExprVariantSwitch variant_switch; // 32 ExprLen len_expr; // 8 ExprCast cast_expr; // 12 @@ -1593,6 +1600,7 @@ extern const char *kw_distinct; extern const char *kw_inline; extern const char *kw_inf; extern const char *kw_kind; +extern const char *kw_inner; extern const char *kw_elementat; extern const char *kw_elementref; extern const char *kw_elementset; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 929024709..cc5931615 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -241,9 +241,11 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) case EXPR_PTR: case EXPR_STRINGIFY: case EXPR_CT_EVAL: - case EXPR_TYPEID_KIND: MACRO_COPY_EXPR(expr->inner_expr); return expr; + case EXPR_TYPEID_INFO: + MACRO_COPY_EXPRID(expr->typeid_info_expr.parent); + return expr; case EXPR_COND: MACRO_COPY_EXPR_LIST(expr->cond_expr); return expr; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 9909ef901..481818cf1 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -247,9 +247,18 @@ typedef enum EXPR_UNARY, EXPR_VARIANTSWITCH, EXPR_NOP, - EXPR_TYPEID_KIND, + EXPR_TYPEID_INFO, } ExprKind; +typedef enum +{ + TYPEID_INFO_KIND, + TYPEID_INFO_MIN, + TYPEID_INFO_MAX, + TYPEID_INFO_INNER, + TYPEID_INFO_LEN, +} TypeIdInfoKind; + typedef enum { CONST_FLOAT, diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index b49ab5059..db29c8179 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -5296,13 +5296,85 @@ static inline void llvm_emit_ptr(GenContext *c, BEValue *value, Expr *expr) llvm_emit_subarray_pointer(c, value, value); } -static inline void llvm_emit_typeid_kind(GenContext *c, BEValue *value, Expr *expr) +static inline void llvm_emit_typeid_info(GenContext *c, BEValue *value, Expr *expr) { - llvm_emit_expr(c, value, expr->inner_expr); + llvm_emit_exprid(c, value, expr->typeid_info_expr.parent); llvm_value_rvalue(c, value); - LLVMValueRef ref = LLVMBuildIntToPtr(c->builder, value->value, llvm_get_ptr_type(c, type_char), ""); - LLVMValueRef kind = llvm_load(c, c->byte_type, ref, 1, ""); - llvm_value_set(value, kind, expr->type); + + LLVMValueRef kind; + if (active_target.feature.safe_mode || expr->typeid_info_expr.kind == TYPEID_INFO_KIND) + { + LLVMValueRef ref = LLVMBuildIntToPtr(c->builder, value->value, llvm_get_ptr_type(c, type_char), ""); + kind = llvm_load(c, c->byte_type, ref, 1, ""); + } + switch (expr->typeid_info_expr.kind) + { + case TYPEID_INFO_KIND: + llvm_value_set(value, kind, expr->type); + return; + case TYPEID_INFO_INNER: + if (active_target.feature.safe_mode) + { + BEValue check; + LLVMBasicBlockRef exit = llvm_basic_block_new(c, "check_type_ok"); + IntrospectType checks[7] = { INTROSPECT_TYPE_ARRAY, INTROSPECT_TYPE_POINTER, + INTROSPECT_TYPE_VECTOR, + INTROSPECT_TYPE_SUBARRAY, INTROSPECT_TYPE_DISTINCT, + INTROSPECT_TYPE_FAILABLE, INTROSPECT_TYPE_SUBARRAY }; + for (int i = 0; i < 7; i++) + { + llvm_emit_int_comp(c, &check, type_char, type_char, kind, llvm_const_int(c, type_char, checks[i]), BINARYOP_EQ); + LLVMBasicBlockRef next = llvm_basic_block_new(c, "check_next"); + llvm_emit_cond_br(c, &check, exit, next); + llvm_emit_block(c, next); + } + File *file = source_file_by_id(expr->span.file_id); + llvm_emit_panic(c, "Attempted to access 'inner' on non composite type", file->name, c->cur_func_decl->name, expr->span.row); + c->current_block = NULL; + c->current_block_is_target = false; + LLVMBuildUnreachable(c->builder); + llvm_emit_block(c, exit); + } + { + LLVMTypeRef typeid = llvm_get_type(c, type_typeid); + LLVMValueRef ref = LLVMBuildIntToPtr(c->builder, value->value, llvm_get_ptr_type(c, type_typeid), ""); + LLVMValueRef val = llvm_emit_pointer_gep_raw(c, typeid, ref, llvm_const_int(c, type_usize, 1)); + val = llvm_load(c, typeid, val, type_abi_alignment(type_typeid), ""); + llvm_value_set(value, val, expr->type); + } + break; + case TYPEID_INFO_LEN: + if (active_target.feature.safe_mode) + { + BEValue check; + LLVMBasicBlockRef exit = llvm_basic_block_new(c, "check_type_ok"); + IntrospectType checks[3] = { INTROSPECT_TYPE_ARRAY, INTROSPECT_TYPE_VECTOR, + INTROSPECT_TYPE_SUBARRAY }; + for (int i = 0; i < 3; i++) + { + llvm_emit_int_comp(c, &check, type_char, type_char, kind, llvm_const_int(c, type_char, checks[i]), BINARYOP_EQ); + LLVMBasicBlockRef next = llvm_basic_block_new(c, "check_next"); + llvm_emit_cond_br(c, &check, exit, next); + llvm_emit_block(c, next); + } + File *file = source_file_by_id(expr->span.file_id); + llvm_emit_panic(c, "Attempted to access 'len' on non array type", file->name, c->cur_func_decl->name, expr->span.row); + c->current_block = NULL; + c->current_block_is_target = false; + LLVMBuildUnreachable(c->builder); + llvm_emit_block(c, exit); + } + { + LLVMTypeRef typeid = llvm_get_type(c, type_usize); + LLVMValueRef ref = LLVMBuildIntToPtr(c->builder, value->value, llvm_get_ptr_type(c, type_usize), ""); + LLVMValueRef val = llvm_emit_pointer_gep_raw(c, typeid, ref, llvm_const_int(c, type_usize, 2)); + val = llvm_load(c, typeid, val, type_abi_alignment(type_usize), ""); + llvm_value_set(value, val, expr->type); + } + break; + default: + UNREACHABLE + } } void llvm_emit_try_unwrap_chain(GenContext *c, BEValue *value, Expr *expr) @@ -5463,8 +5535,8 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_PTR: llvm_emit_ptr(c, value, expr); return; - case EXPR_TYPEID_KIND: - llvm_emit_typeid_kind(c, value, expr); + case EXPR_TYPEID_INFO: + llvm_emit_typeid_info(c, value, expr); return; case EXPR_BUILTIN: UNREACHABLE; diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 2c8f6f871..62fa0443d 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -780,6 +780,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case EXPR_SLICE: case EXPR_SUBSCRIPT: case EXPR_RETVAL: + case EXPR_TYPEID_INFO: if (type_size(expr->type) > type_size(type)) return expr; return NULL; case EXPR_EXPRESSION_LIST: @@ -835,7 +836,6 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case EXPR_COMPILER_CONST: case EXPR_STRINGIFY: case EXPR_CT_EVAL: - case EXPR_TYPEID_KIND: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_float(expr->unary_expr.expr, type); @@ -935,6 +935,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case EXPR_SLICE: case EXPR_SUBSCRIPT: case EXPR_RETVAL: + case EXPR_TYPEID_INFO: if (type_size(expr->type) > type_size(type)) return expr; return NULL; case EXPR_LEN: @@ -988,7 +989,6 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case EXPR_COMPILER_CONST: case EXPR_STRINGIFY: case EXPR_CT_EVAL: - case EXPR_TYPEID_KIND: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_int(expr->unary_expr.expr, type); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 7295483d8..55f1498b5 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -370,9 +370,11 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) } case EXPR_EXPRESSION_LIST: return expr_list_is_constant_eval(expr->expression_list, eval_kind); + case EXPR_TYPEID_INFO: + expr = exprptr(expr->typeid_info_expr.parent); + goto RETRY; case EXPR_FAILABLE: case EXPR_GROUP: - case EXPR_TYPEID_KIND: expr = expr->inner_expr; goto RETRY; case EXPR_INITIALIZER_LIST: @@ -591,7 +593,11 @@ bool sema_expr_check_assign(SemaContext *c, Expr *expr) SEMA_ERROR(expr, "An assignable expression, like a variable, was expected here."); return false; } - if (expr->expr_kind == EXPR_IDENTIFIER) expr->identifier_expr.decl->var.is_written = true; + if (expr->expr_kind == EXPR_BITACCESS || expr->expr_kind == EXPR_ACCESS) expr = expr->access_expr.parent; + if (expr->expr_kind == EXPR_IDENTIFIER) + { + expr->identifier_expr.decl->var.is_written = true; + } if (expr->expr_kind != EXPR_UNARY) return true; Expr *inner = expr->inner_expr; if (inner->expr_kind != EXPR_IDENTIFIER) return true; @@ -3283,6 +3289,92 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp return true; } +static inline void sema_rewrite_typeid_kind(Expr *expr, Expr *parent, Expr *current_parent) +{ + Module *module = global_context_find_module(kw_std__core__types); + Decl *type_kind = module ? module_find_symbol(module, kw_typekind) : NULL; + Type *type_for_kind = type_kind ? type_kind->type : type_char; + if (current_parent->expr_kind == EXPR_CONST) + { + unsigned val = type_get_introspection_kind(current_parent->const_expr.typeid->type_kind); + expr_rewrite_to_int_const(expr, type_for_kind, val, false); + return; + } + expr->expr_kind = EXPR_TYPEID_INFO; + expr->typeid_info_expr.parent = exprid(parent); + expr->typeid_info_expr.kind = TYPEID_INFO_KIND; + expr->type = type_for_kind; +} + +static inline bool sema_rewrite_typeid_inner(Expr *expr, Expr *parent, Expr *current_parent) +{ + if (current_parent->expr_kind == EXPR_CONST) + { + Type *type = type_flatten_distinct(current_parent->const_expr.typeid); + Type *inner = NULL; + switch (type->type_kind) + { + case TYPE_POINTER: + inner = type->pointer; + break; + case TYPE_FAILABLE: + inner = type->failable; + break; + case TYPE_ARRAY: + case TYPE_FLEXIBLE_ARRAY: + case TYPE_SUBARRAY: + case TYPE_INFERRED_ARRAY: + case TYPE_VECTOR: + inner = type->array.base; + break; + default: + inner = NULL; + break; + } + if (!inner) + { + SEMA_ERROR(expr, "Cannot access 'inner' of non pointer/array type %s.", type_quoted_error_string(current_parent->const_expr.typeid)); + return false; + } + expr_replace(expr, current_parent); + expr->const_expr.typeid = inner; + return true; + } + expr->expr_kind = EXPR_TYPEID_INFO; + expr->typeid_info_expr.parent = exprid(parent); + expr->typeid_info_expr.kind = TYPEID_INFO_INNER; + expr->type = type_typeid; + return true; +} + +static inline bool sema_rewrite_typeid_len(Expr *expr, Expr *parent, Expr *current_parent) +{ + if (current_parent->expr_kind == EXPR_CONST) + { + Type *type = type_flatten_distinct(current_parent->const_expr.typeid); + size_t len; + switch (type->type_kind) + { + case TYPE_ARRAY: + case TYPE_FLEXIBLE_ARRAY: + case TYPE_SUBARRAY: + case TYPE_INFERRED_ARRAY: + case TYPE_VECTOR: + len = type->array.len; + default: + SEMA_ERROR(expr, "Cannot access 'len' of non array/vector type %s.", type_quoted_error_string(current_parent->const_expr.typeid)); + return false; + } + expr_rewrite_to_int_const(expr, type_usize, len, true); + return true; + } + expr->expr_kind = EXPR_TYPEID_INFO; + expr->typeid_info_expr.parent = exprid(parent); + expr->typeid_info_expr.kind = TYPEID_INFO_LEN; + expr->type = type_usize; + return true; +} + /** * Analyse "x.y" */ @@ -3380,23 +3472,23 @@ CHECK_DEEPER: return true; } } - - if (kw == kw_kind && flat_type->type_kind == TYPE_TYPEID) + if (flat_type->type_kind == TYPE_TYPEID) { - Module *module = global_context_find_module(kw_std__core__types); - Decl *type_kind = module ? module_find_symbol(module, kw_typekind) : NULL; - Type *type_for_kind = type_kind ? type_kind->type : type_char; - if (current_parent->expr_kind == EXPR_CONST) + if (kw == kw_kind) { - unsigned val = type_get_introspection_kind(current_parent->const_expr.typeid->type_kind); - expr_rewrite_to_int_const(expr, type_for_kind, val, false); + sema_rewrite_typeid_kind(expr, parent, current_parent); return true; } - expr->expr_kind = EXPR_TYPEID_KIND; - expr->inner_expr = parent; - expr->type = type_for_kind; - return true; + if (kw == kw_inner) + { + return sema_rewrite_typeid_inner(expr, parent, current_parent); + } + if (kw == kw_len) + { + return sema_rewrite_typeid_len(expr, parent, current_parent); + } } + // Hard coded ptr on subarrays and variant if (kw == kw_ptr) { @@ -7036,7 +7128,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_CATCH_UNWRAP: case EXPR_PTR: case EXPR_VARIANTSWITCH: - case EXPR_TYPEID_KIND: + case EXPR_TYPEID_INFO: UNREACHABLE case EXPR_STRINGIFY: if (!sema_expr_analyse_ct_stringify(context, expr)) return false; diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 6ede155f3..42169523b 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -55,6 +55,7 @@ const char *kw_type; const char *kw_inf; const char *kw_inline; const char *kw_kind; +const char *kw_inner; const char *kw_elementat; const char *kw_elementref; const char *kw_elementset; @@ -160,6 +161,7 @@ void symtab_init(uint32_t capacity) kw_noinline = KW_DEF("noinline"); kw_ordinal = KW_DEF("ordinal"); kw_ptr = KW_DEF("ptr"); + kw_inner = KW_DEF("inner"); kw_pure = KW_DEF("pure"); kw_std = KW_DEF("std"); kw_values = KW_DEF("values"); diff --git a/src/version.h b/src/version.h index 7e43e4d36..e5586af61 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.2.14" \ No newline at end of file +#define COMPILER_VERSION "0.2.15" \ No newline at end of file diff --git a/test/test_suite2/bitstruct/param_bitstruct.c3t b/test/test_suite2/bitstruct/param_bitstruct.c3t new file mode 100644 index 000000000..61a580136 --- /dev/null +++ b/test/test_suite2/bitstruct/param_bitstruct.c3t @@ -0,0 +1,24 @@ +// #target: macos-x64 +module foo; + +bitstruct Abc : uint +{ + bool x : 0; +} + +fn void test(Abc x) +{ + x.x = false; +} + +/* #expect: foo.ll + +define void @foo.test(i32 %0) #0 { +entry: + %x = alloca i32, align 4 + store i32 %0, ptr %x, align 4 + %1 = load i32, ptr %x, align 4 + %2 = and i32 %1, -2 + store i32 %2, ptr %x, align 4 + ret void +} \ No newline at end of file