From a0497e9274241404d8e745935b85d9c5e4df6175 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 21 Jun 2025 13:28:45 +0200 Subject: [PATCH] `math::overflow_*` wrappers incorrectly don't allow distinct integers #2221. --- lib/std/core/types.c3 | 16 +++++ lib/std/core/values.c3 | 3 + lib/std/math/math.c3 | 6 +- releasenotes.md | 1 + src/compiler/sema_builtins.c | 2 +- src/compiler/types.c | 8 +-- test/test_suite/builtins/math_overflow.c3t | 83 ++++++++++++++++++++++ 7 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 test/test_suite/builtins/math_overflow.c3t diff --git a/lib/std/core/types.c3 b/lib/std/core/types.c3 index 9a6d71d06..d5962abba 100644 --- a/lib/std/core/types.c3 +++ b/lib/std/core/types.c3 @@ -184,6 +184,22 @@ macro bool is_ref_indexable($Type) @const return $defined(&($Type){}[0]); } +macro bool is_flat_intlike($Type) @const +{ + $echo $Type.nameof; + $switch $Type.kindof: + $case SIGNED_INT: + $case UNSIGNED_INT: + return true; + $case VECTOR: + return is_flat_intlike($Type.inner); + $case DISTINCT: + return is_flat_intlike($Type.inner); + $default: + return false; + $endswitch +} + macro bool is_intlike($Type) @const { $switch $Type.kindof: diff --git a/lib/std/core/values.c3 b/lib/std/core/values.c3 index cc658e0d3..5faf24095 100644 --- a/lib/std/core/values.c3 +++ b/lib/std/core/values.c3 @@ -1,4 +1,6 @@ module std::core::values; +import std::core::types; + macro typeid @typeid(#value) @const @builtin => $typeof(#value).typeid; macro TypeKind @typekind(#value) @const @builtin => $typeof(#value).kindof; @@ -9,6 +11,7 @@ macro bool @typeis(#value, $Type) @const @builtin => $typeof(#value).typeid == $ macro bool @is_same_type(#value1, #value2) @const => $typeof(#value1).typeid == $typeof(#value2).typeid; macro bool @is_bool(#value) @const => types::is_bool($typeof(#value)); macro bool @is_int(#value) @const => types::is_int($typeof(#value)); +macro bool @is_flat_intlike(#value) @const => types::is_flat_intlike($typeof(#value)); macro bool @is_floatlike(#value) @const => types::is_floatlike($typeof(#value)); macro bool @is_float(#value) @const => types::is_float($typeof(#value)); macro bool @is_promotable_to_floatlike(#value) @const => types::is_promotable_to_floatlike($typeof(#value)); diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index 0a10cd9ab..f986dbb46 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -1117,7 +1117,7 @@ macro overflow_mul_helper(x, y) @local @param [&out] out : "Where the result of the addition is stored" @return "Whether the addition resulted in an integer overflow" @require values::@is_same_type(a, b) : "a and b must be the same type" - @require values::@is_int(a) &&& values::@is_int(b) : "a and b must both be integers" + @require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based" @require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b" *> macro bool overflow_add(a, b, out) => $$overflow_add(a, b, out); @@ -1126,7 +1126,7 @@ macro bool overflow_add(a, b, out) => $$overflow_add(a, b, out); @param [&out] out : "Where the result of the subtraction is stored" @return "Whether the subtraction resulted in an integer overflow" @require values::@is_same_type(a, b) : "a and b must be the same type" - @require values::@is_int(a) &&& values::@is_int(b) : "a and b must both be integers" + @require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based" @require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b" *> macro bool overflow_sub(a, b, out) => $$overflow_sub(a, b, out); @@ -1135,7 +1135,7 @@ macro bool overflow_sub(a, b, out) => $$overflow_sub(a, b, out); @param [&out] out : "Where the result of the multiplication is stored" @return "Whether the multiplication resulted in an integer overflow" @require values::@is_same_type(a, b) : "a and b must be the same type" - @require values::@is_int(a) &&& values::@is_int(b) : "a and b must both be integers" + @require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based" @require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b" *> macro bool overflow_mul(a, b, out) => $$overflow_mul(a, b, out); diff --git a/releasenotes.md b/releasenotes.md index 8e290f74e..70ee6de0b 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -58,6 +58,7 @@ - Lambda C-style vaargs were not properly rejected, leading to crash #2229. - Incorrect handling of constant null fault causing compiler crash #2232. - Overload resolution fixes to inline typedef #2226. +- `math::overflow_*` wrappers incorrectly don't allow distinct integers #2221. ### Stdlib changes - Deprecate `String.is_zstr` and `String.quick_zstr` #2188. diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index 15e84520d..5fb54a74b 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -629,7 +629,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) case BUILTIN_OVERFLOW_SUB: ASSERT(arg_count == 3); if (!sema_check_builtin_args(context, args, - (BuiltinArg[]) {BA_INTEGER, BA_INTEGER, BA_POINTER}, + (BuiltinArg[]) {BA_INTLIKE, BA_INTLIKE, BA_POINTER}, 3)) return false; if (!sema_check_builtin_args_match(context, args, 2)) return false; if (type_no_optional(args[0]->type->canonical) != type_no_optional(args[2]->type->canonical->pointer)) diff --git a/src/compiler/types.c b/src/compiler/types.c index f1ccc493e..1e699d028 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -382,7 +382,7 @@ FunctionPrototype *type_get_resolved_prototype(Type *type) bool type_flat_is_numlike(Type *type) { type = type_flatten(type); - if (type->type_kind == TYPE_VECTOR) type = type->array.base; + if (type->type_kind == TYPE_VECTOR) type = type_flatten(type->array.base); TypeKind kind = type->type_kind; return kind >= TYPE_NUM_FIRST && kind <= TYPE_NUM_LAST; } @@ -390,7 +390,7 @@ bool type_flat_is_numlike(Type *type) bool type_flat_is_floatlike(Type *type) { type = type_flatten(type); - if (type->type_kind == TYPE_VECTOR) type = type->array.base; + if (type->type_kind == TYPE_VECTOR) type = type_flatten(type->array.base); TypeKind kind = type->type_kind; return kind >= TYPE_FLOAT_FIRST && kind <= TYPE_FLOAT_LAST; } @@ -398,7 +398,7 @@ bool type_flat_is_floatlike(Type *type) bool type_flat_is_intlike(Type *type) { type = type_flatten(type); - if (type->type_kind == TYPE_VECTOR) type = type->array.base; + if (type->type_kind == TYPE_VECTOR) type = type_flatten(type->array.base); TypeKind kind = type->type_kind; return kind >= TYPE_INTEGER_FIRST && kind <= TYPE_INTEGER_LAST; } @@ -406,7 +406,7 @@ bool type_flat_is_intlike(Type *type) bool type_flat_is_boolintlike(Type *type) { type = type_flatten(type); - if (type->type_kind == TYPE_VECTOR) type = type->array.base; + if (type->type_kind == TYPE_VECTOR) type = type_flatten(type->array.base); TypeKind kind = type->type_kind; return kind == TYPE_BOOL || (kind >= TYPE_INTEGER_FIRST && kind <= TYPE_INTEGER_LAST); } diff --git a/test/test_suite/builtins/math_overflow.c3t b/test/test_suite/builtins/math_overflow.c3t new file mode 100644 index 000000000..18a2be705 --- /dev/null +++ b/test/test_suite/builtins/math_overflow.c3t @@ -0,0 +1,83 @@ +// #target: macos-x64 +module test; +import std::math; +typedef Foo = int; + +fn int main() +{ + + Foo a, b, c; + math::overflow_mul(a, b, &c); + int[<2>] ab, bb, cb; + math::overflow_mul(ab, bb, &cb); + Foo[<2>] av, bv, cv; + math::overflow_mul(av, bv, &cv); + return 0; +} + +/* #expect: test.ll + +define i32 @main() #0 { +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + %a1 = alloca i32, align 4 + %b2 = alloca i32, align 4 + %ab = alloca <2 x i32>, align 8 + %bb = alloca <2 x i32>, align 8 + %cb = alloca <2 x i32>, align 8 + %a3 = alloca <2 x i32>, align 8 + %b4 = alloca <2 x i32>, align 8 + %av = alloca <2 x i32>, align 8 + %bv = alloca <2 x i32>, align 8 + %cv = alloca <2 x i32>, align 8 + %a6 = alloca <2 x i32>, align 8 + %b7 = alloca <2 x i32>, align 8 + store i32 0, ptr %a, align 4 + store i32 0, ptr %b, align 4 + store i32 0, ptr %c, align 4 + %0 = load i32, ptr %a, align 4 + store i32 %0, ptr %a1, align 4 + %1 = load i32, ptr %b, align 4 + store i32 %1, ptr %b2, align 4 + %neq = icmp ne ptr %c, null + call void @llvm.assume(i1 %neq) + %2 = load i32, ptr %a1, align 4 + %3 = load i32, ptr %b2, align 4 + %4 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %2, i32 %3) + %5 = extractvalue { i32, i1 } %4, 1 + %6 = extractvalue { i32, i1 } %4, 0 + store i32 %6, ptr %c, align 4 + store <2 x i32> zeroinitializer, ptr %ab, align 8 + store <2 x i32> zeroinitializer, ptr %bb, align 8 + store <2 x i32> zeroinitializer, ptr %cb, align 8 + %7 = load <2 x i32>, ptr %ab, align 8 + store <2 x i32> %7, ptr %a3, align 8 + %8 = load <2 x i32>, ptr %bb, align 8 + store <2 x i32> %8, ptr %b4, align 8 + %neq5 = icmp ne ptr %cb, null + call void @llvm.assume(i1 %neq5) + %9 = load <2 x i32>, ptr %a3, align 8 + %10 = load <2 x i32>, ptr %b4, align 8 + %11 = call { <2 x i32>, <2 x i1> } @llvm.smul.with.overflow.v2i32(<2 x i32> %9, <2 x i32> %10) + %12 = extractvalue { <2 x i32>, <2 x i1> } %11, 1 + %13 = extractvalue { <2 x i32>, <2 x i1> } %11, 0 + store <2 x i32> %13, ptr %cb, align 8 + store <2 x i32> zeroinitializer, ptr %av, align 8 + store <2 x i32> zeroinitializer, ptr %bv, align 8 + store <2 x i32> zeroinitializer, ptr %cv, align 8 + %14 = load <2 x i32>, ptr %av, align 8 + store <2 x i32> %14, ptr %a6, align 8 + %15 = load <2 x i32>, ptr %bv, align 8 + store <2 x i32> %15, ptr %b7, align 8 + %neq8 = icmp ne ptr %cv, null + call void @llvm.assume(i1 %neq8) + %16 = load <2 x i32>, ptr %a6, align 8 + %17 = load <2 x i32>, ptr %b7, align 8 + %18 = call { <2 x i32>, <2 x i1> } @llvm.smul.with.overflow.v2i32(<2 x i32> %16, <2 x i32> %17) + %19 = extractvalue { <2 x i32>, <2 x i1> } %18, 1 + %20 = extractvalue { <2 x i32>, <2 x i1> } %18, 0 + store <2 x i32> %20, ptr %cv, align 8 + ret i32 0 +}