math::overflow_* wrappers incorrectly don't allow distinct integers #2221.

This commit is contained in:
Christoffer Lerno
2025-06-21 13:28:45 +02:00
parent fa730e7ec2
commit a0497e9274
7 changed files with 111 additions and 8 deletions

View File

@@ -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:

View File

@@ -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));

View File

@@ -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);

View File

@@ -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.

View File

@@ -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))

View File

@@ -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);
}

View File

@@ -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
}