From fa707db07819b6b835f691aacbbb0d01748a99a2 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 25 Mar 2025 12:27:51 +0100 Subject: [PATCH] Do not implicitly convert enums to ordinal in formatting functions. --- lib/std/core/string.c3 | 6 +++--- lib/std/io/formatter.c3 | 2 +- lib/std/io/formatter_private.c3 | 1 - lib/std/time/format.c3 | 12 ++++++------ releasenotes.md | 1 + src/compiler/sema_expr.c | 4 ++++ test/unit/stdlib/io/printf.c3 | 4 ++-- 7 files changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index bec71f2f0..5f567e8ad 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -46,7 +46,7 @@ macro Char16[] @char16(String $string) @builtin @param [in] fmt : `The formatting string` *> -fn ZString tformat_zstr(String fmt, args...) +fn ZString tformat_zstr(String fmt, args...) @format(0) { DString str = dstring::temp_with_capacity(fmt.len + args.len * 8); str.appendf(fmt, ...args); @@ -59,7 +59,7 @@ fn ZString tformat_zstr(String fmt, args...) @param [inout] allocator : `The allocator to use` @param [in] fmt : `The formatting string` *> -fn String format(Allocator allocator, String fmt, args...) => @pool() +fn String format(Allocator allocator, String fmt, args...) @format(1) => @pool() { DString str = dstring::temp_with_capacity(fmt.len + args.len * 8); str.appendf(fmt, ...args); @@ -71,7 +71,7 @@ fn String format(Allocator allocator, String fmt, args...) => @pool() @param [in] fmt : `The formatting string` *> -fn String tformat(String fmt, args...) +fn String tformat(String fmt, args...) @format(0) { DString str = dstring::temp_with_capacity(fmt.len + args.len * 8); str.appendf(fmt, ...args); diff --git a/lib/std/io/formatter.c3 b/lib/std/io/formatter.c3 index 1a9d92935..eb7b9dd07 100644 --- a/lib/std/io/formatter.c3 +++ b/lib/std/io/formatter.c3 @@ -339,7 +339,7 @@ macro usz? @wrap_bad(Formatter* f, #action) case INTERNAL_BUFFER_EXCEEDED: return f.first_err(err)?; default: - err = f.first_err(INVALID_ARGUMENT); + err = f.first_err(INVALID_ARGUMENT); f.out_substr("")!; return err?; } diff --git a/lib/std/io/formatter_private.c3 b/lib/std/io/formatter_private.c3 index 3a7ed5363..e3251dfe2 100644 --- a/lib/std/io/formatter_private.c3 +++ b/lib/std/io/formatter_private.c3 @@ -43,7 +43,6 @@ fn uint128? int_from_any(any arg, bool *is_neg) @private *is_neg = false; return (uint128)(uptr)*(void**)arg.ptr; case TypeKind.DISTINCT: - case TypeKind.ENUM: return int_from_any(arg.as_inner(), is_neg); default: break; diff --git a/lib/std/time/format.c3 b/lib/std/time/format.c3 index 31a05af70..51ee3a205 100644 --- a/lib/std/time/format.c3 +++ b/lib/std/time/format.c3 @@ -45,18 +45,18 @@ fn String format(Allocator allocator, DateTimeFormat type, TzDateTime dt) return string::format(allocator, "%s, %02d %s %d %02d:%02d:%02d %s", dt.weekday.abbrev, dt.day, dt.month.abbrev, dt.year, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix(dt.gmt_offset)); case RFC3339: dt = dt.to_gmt_offset(0); - return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec); + return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02dZ", dt.year, dt.month.ordinal + 1, dt.day, dt.hour, dt.min, dt.sec); case RFC3339Z: - return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix_colon(dt.gmt_offset)); + return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d%s", dt.year, dt.month.ordinal + 1, dt.day, dt.hour, dt.min, dt.sec, temp_numeric_tzsuffix_colon(dt.gmt_offset)); case RFC3339MS: dt = dt.to_gmt_offset(0); - return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d.%dZ", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec); + return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d.%dZ", dt.year, dt.month.ordinal + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec); case RFC3339ZMS: - return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d.%d%s", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec, temp_numeric_tzsuffix_colon(dt.gmt_offset)); + return string::format(allocator, "%04d-%02d-%02dT%02d:%02d:%02d.%d%s", dt.year, dt.month.ordinal + 1, dt.day, dt.hour, dt.min, dt.sec, dt.usec, temp_numeric_tzsuffix_colon(dt.gmt_offset)); case DATETIME: - return string::format(allocator, "%04d-%02d-%02d %02d:%02d:%02d", dt.year, dt.month + 1, dt.day, dt.hour, dt.min, dt.sec); + return string::format(allocator, "%04d-%02d-%02d %02d:%02d:%02d", dt.year, dt.month.ordinal + 1, dt.day, dt.hour, dt.min, dt.sec); case DATEONLY: - return string::format(allocator, "%04d-%02d-%02d", dt.year, dt.month + 1, dt.day); + return string::format(allocator, "%04d-%02d-%02d", dt.year, dt.month.ordinal + 1, dt.day); case TIMEONLY: return string::format(allocator, "%02d:%02d:%02d", dt.hour, dt.min, dt.sec); } diff --git a/releasenotes.md b/releasenotes.md index 21744252b..76d5fefe6 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -44,6 +44,7 @@ - `tmem` is now a variable. - Compile test and benchmark functions when invoking `--lsp` #2058. - Added `@format` attribute for compile time printf validation #2057. +- Formatter no longer implicitly converts enums to ordinals. ### Fixes - Fix address sanitizer to work on MachO targets (e.g. MacOS). diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index e4c3b6d1c..265c40777 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1869,6 +1869,10 @@ CHECK_FORMAT:; case 'G': if (!type_is_number_or_bool(type)) { + if (type->type_kind == TYPE_ENUM) + { + RETURN_SEMA_ERROR(vaargs[idx], "An enum cannot directly be turned into a number. Use '.ordinal' to convert it to its value.", type_quoted_error_string(type)); + } RETURN_SEMA_ERROR(vaargs[idx], "Expected a number here, but was %s", type_quoted_error_string(type)); } goto NEXT; diff --git a/test/unit/stdlib/io/printf.c3 b/test/unit/stdlib/io/printf.c3 index 263643a0d..701ad0e99 100644 --- a/test/unit/stdlib/io/printf.c3 +++ b/test/unit/stdlib/io/printf.c3 @@ -107,10 +107,10 @@ fn void printf_enum() assert(s == "ENUMB", "got '%s'; want 'ENUMB'", s); free(s); - s = string::format(mem, "%d", PrintfTest.ENUMA); + s = string::format(mem, "%d", PrintfTest.ENUMA.ordinal); assert(s == "0", "got '%s'; want '0'", s); free(s); - s = string::format(mem, "%d", PrintfTest.ENUMB); + s = string::format(mem, "%d", PrintfTest.ENUMB.ordinal); assert(s == "1", "got '%s'; want '1'", s); free(s); } \ No newline at end of file