int val = some_int + some_distinct_inline_int errors that int cannot be cast to DistinctInt #2468

This commit is contained in:
Christoffer Lerno
2025-09-08 10:21:47 +02:00
parent 7b5277d52c
commit a2206f1bcd
5 changed files with 190 additions and 72 deletions

View File

@@ -17,6 +17,7 @@
- Correctly silence "unsupported architecture" warning with `--quiet` #2465
- Overloading &[] should be enough for foreach. #2466
- Any register allowed in X86_64 inline asm address. #2463
- int val = some_int + some_distinct_inline_int errors that int cannot be cast to DistinctInt #2468
### Stdlib changes
- Added generic `InterfaceList` to store a list of values that implement a specific interface

View File

@@ -2541,7 +2541,7 @@ AlignSize type_abi_alignment(Type *type);
bool type_func_match(Type *fn_type, Type *rtype, unsigned arg_count, ...);
AlignSize type_alloca_alignment(Type *type);
Type *type_find_largest_union_element(Type *type);
Type *type_find_max_type(Type *type, Type *other);
Type *type_find_max_type(Type *type, Type *other, Expr *first, Expr *second);
Type *type_find_max_type_may_fail(Type *type, Type *other);
Type *type_abi_find_single_struct_element(Type *type);
Module *type_base_module(Type *type);
@@ -3118,16 +3118,32 @@ static inline Type *type_base(Type *type)
}
}
static const bool is_distinct_like[TYPE_LAST + 1] = {
[TYPE_ENUM] = true,
[TYPE_CONST_ENUM] = true,
[TYPE_DISTINCT] = true
};
INLINE bool typekind_is_distinct_like(TypeKind kind)
{
return is_distinct_like[kind];
}
INLINE bool type_is_distinct_like(Type *type)
{
TypeKind kind = type->type_kind;
return kind == TYPE_DISTINCT || kind == TYPE_CONST_ENUM;
return is_distinct_like[type->type_kind];
}
static bool type_has_inline(Type *type)
{
return is_distinct_like[type->type_kind] && type->decl->is_substruct;
}
static inline Type *type_inline(Type *type)
{
assert(type_is_distinct_like(type));
return type->type_kind == TYPE_CONST_ENUM ? type->decl->enums.type_info->type : type->decl->distinct->type;
return type->type_kind == TYPE_DISTINCT ? type->decl->distinct->type : type->decl->enums.type_info->type;
}
@@ -3136,16 +3152,8 @@ static inline Type *type_flat_distinct_inline(Type *type)
while (1)
{
type = type->canonical;
switch (type->type_kind)
{
case TYPE_DISTINCT:
case TYPE_CONST_ENUM:
if (!type->decl->is_substruct) return type;
type = type_inline(type);
break;
default:
return type;
}
if (!type_has_inline(type)) return type;
type = type_inline(type);
}
}

View File

@@ -1108,7 +1108,7 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Type *infer_t
Type *right_canonical = right->type->canonical;
if (left_canonical != right_canonical)
{
Type *max = type_find_max_type(type_no_optional(left_canonical), type_no_optional(right_canonical));
Type *max = type_find_max_type(type_no_optional(left_canonical), type_no_optional(right_canonical), left, right);
if (!max)
{
SEMA_ERROR(expr, "Cannot find a common parent type of '%s' and '%s'",
@@ -2566,7 +2566,7 @@ static inline Type *context_unify_returns(SemaContext *context)
if (common_type == rtype || (type_is_void(common_type) && rtype == type_wildcard)) continue;
// 4. Find the max of the old and new.
Type *max = type_find_max_type(common_type, rtype); // NOLINT
Type *max = type_find_max_type(common_type, rtype, NULL, NULL);
// 5. No match -> error.
if (!max)
@@ -4256,7 +4256,7 @@ INLINE bool sema_expr_analyse_range_internal(SemaContext *context, Range *range,
if (IS_OPTIONAL(start)) range->is_optional = true;
if (end && end_type != start_type)
{
Type *common = type_find_max_type(start_type, end_type);
Type *common = type_find_max_type(start_type, end_type, NULL, NULL);
if (!common)
{
SourceSpan span = start->span;
@@ -7373,7 +7373,7 @@ static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, E
}
}
Type *max = cast_numeric_arithmetic_promotion(type_find_max_type(left_type, right_type));
Type *max = cast_numeric_arithmetic_promotion(type_find_max_type(left_type, right_type, left, right));
if (!max || (!type_underlying_is_numeric(max) && !(allow_bool_vec && type_flat_is_bool_vector(max))))
{
CHECK_ON_DEFINED(failed_ref);
@@ -8275,7 +8275,7 @@ NEXT:
// 3. In the normal case, treat this as a binary op, finding the max type.
Type *max = type_find_max_type(left_type, right_type);
Type *max = type_find_max_type(left_type, right_type, left, right);
// 4. If no common type, then that's an error:
if (!max)
@@ -9143,7 +9143,7 @@ static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr,
bool add_optional = type_is_optional(else_type);
type = type_no_optional(type);
else_type = type_no_optional(else_type);
Type *common = type_find_max_type(type, else_type);
Type *common = type_find_max_type(type, else_type, left, right);
if (!common)
{
CHECK_ON_DEFINED(failed_ref);

View File

@@ -1865,6 +1865,10 @@ Type *type_find_max_num_type(Type *num_type, Type *other_num)
ASSERT(kind <= other_kind && "Expected ordering");
ASSERT(kind != other_kind);
// If the other is a vector then we always set that one as the max.
if (other_kind == TYPE_VECTOR) return other_num;
// 1. The only conversions need to happen if the other type is a number.
if (other_kind < TYPE_INTEGER_FIRST || other_kind > TYPE_FLOAT_LAST) return NULL;
@@ -1925,7 +1929,7 @@ static inline Type *type_find_max_ptr_type(Type *type, Type *other)
// Slice and vararray can implicitly convert to a pointer.
if (other->type_kind == TYPE_SLICE)
{
Type *max_type = type_find_max_type(type->pointer, other->pointer);
Type *max_type = type_find_max_type(type->pointer, other->pointer, false, false);
if (!max_type) return NULL;
return type_get_ptr(max_type);
}
@@ -1959,7 +1963,7 @@ static inline Type *type_find_max_ptr_type(Type *type, Type *other)
{
return other;
}
Type *max_type = type_find_max_type(pointer_type, other_pointer_type);
Type *max_type = type_find_max_type(pointer_type, other_pointer_type, false, false);
if (!max_type) return NULL;
return type_get_ptr(max_type);
}
@@ -2011,57 +2015,97 @@ static inline Type *type_find_max_distinct_type(Type *left, Type *right)
}
Type *type_find_max_type(Type *type, Type *other)
Type *type_find_max_type(Type *type, Type *other, Expr *first, Expr *second)
{
type = type->canonical;
other = other->canonical;
ASSERT(!type_is_optional(type) && !type_is_optional(other));
RETRY_DISTINCT:
// Same type, max is the type
if (type == other) return type;
// One type is wildcard, max is the type which isn't wildcard
if (type == type_wildcard) return other;
if (other == type_wildcard) return type;
// Sort types
// Sort types to make it easier
if (type->type_kind > other->type_kind)
{
Type *temp = type;
Expr *first_expr = first;
type = other;
first = second;
other = temp;
second = first_expr;
}
// The following relies on type kind ordering
// The following relies on type kind ordering, with the "lowest" type kind on top, this means the other type kind is
// further down in the list
switch (type->type_kind)
{
case TYPE_INFERRED_ARRAY:
case TYPE_INFERRED_VECTOR:
case TYPE_POISONED:
case TYPE_OPTIONAL:
case TYPE_WILDCARD:
// Should never get here
UNREACHABLE
case TYPE_VOID:
case TYPE_BOOL:
case TYPE_TYPEINFO:
case TYPE_BITSTRUCT:
case TYPE_FLEXIBLE_ARRAY:
if (type_has_inline(other))
{
other = type_flat_distinct_inline(other);
goto RETRY_DISTINCT;
}
return NULL;
case ALL_INTS:
if (type_is_distinct_like(other) && type_underlying_is_numeric(other)) return other;
if (other->type_kind == TYPE_VECTOR) return other;
{
// If Foo + 1, then we allow this if Foo is a distinct type or const enum that has
// integer or float as the base type.
if (first && type_is_distinct_like(other) && type_underlying_is_numeric(other) && expr_is_const(first)) return other;
// See if we can flatten it.
if (type_has_inline(other))
{
other = type_flat_distinct_inline(other);
goto RETRY_DISTINCT;
}
// Now let's just compare the numerical type, including vectors
return type_find_max_num_type(type, other);
}
case ALL_FLOATS:
if (type_is_distinct_like(other) && type_is_float(type_flatten(other))) return other;
if (other->type_kind == TYPE_VECTOR) return other;
{
// If Foo + 1.0, then we allow this if Foo is a distinct type or const enum that has
// float as the base type.
if (first && type_is_distinct_like(other) && type_underlying_is_numeric(other) && expr_is_const(first)) return other;
// See if we can flatten it.
if (type_has_inline(other))
{
other = type_flat_distinct_inline(other);
goto RETRY_DISTINCT;
}
// Now let's just compare the numerical type, including vectors
return type_find_max_num_type(type, other);
}
case TYPE_ANY:
// any + interface => any
if (other == type_voidptr) return other;
if (type_has_inline(other))
{
other = type_flat_distinct_inline(other);
goto RETRY_DISTINCT;
}
return other->type_kind == TYPE_INTERFACE ? type : NULL;
case TYPE_INTERFACE:
// interface + void* => void*
if (type_has_inline(other))
{
other = type_flat_distinct_inline(other);
goto RETRY_DISTINCT;
}
return other == type_voidptr ? type_voidptr : NULL;
case TYPE_POINTER:
if (type_has_inline(other))
{
other = type_flat_distinct_inline(other);
goto RETRY_DISTINCT;
}
if (type->pointer->type_kind == TYPE_ARRAY)
{
Type *array_base = type->pointer->array.base->canonical;
@@ -2092,38 +2136,62 @@ RETRY_DISTINCT:
// And possibly the other pointer as well
if (other->type_kind == TYPE_POINTER) other = type_decay_array_pointer(other);
return type_find_max_ptr_type(type, other);
case TYPE_ENUM:
if (type->decl->is_substruct)
{
return type_find_max_type(type_flat_distinct_enum_inline(type), other);
}
return NULL;
case TYPE_ANYFAULT:
return type_fault;
case TYPE_TYPEID:
if (type_has_inline(other))
{
other = type_flat_distinct_inline(other);
goto RETRY_DISTINCT;
}
return type;
case TYPE_FUNC_PTR:
if (other == type_voidptr) return other;
if (type_has_inline(other))
{
other = type_flat_distinct_inline(other);
goto RETRY_DISTINCT;
}
if (other->type_kind != TYPE_FUNC_PTR) return NULL;
if (other->pointer->function.prototype->raw_type != type->pointer->function.prototype->raw_type) return NULL;
return type;
case TYPE_DISTINCT:
case TYPE_CONST_ENUM:
if (type_is_distinct_like(other))
{
return type_find_max_distinct_type(type, other);
}
// Try matching with its inline type
if (type->decl->is_substruct)
{
type = type_inline(type);
goto RETRY_DISTINCT;
}
// distinct + any other type => no
return NULL;
case TYPE_ENUM:
// Note that the int case is already handled
if (type->decl->is_substruct)
{
return type_find_max_type(type_flat_distinct_enum_inline(type), other, first, second);
}
return NULL;
case TYPE_FUNC_RAW:
UNREACHABLE
case TYPE_UNION:
case TYPE_STRUCT:
case TYPE_BITSTRUCT:
// union/struct + anything else => no
// even if the struct has an inline type, this should not
// be implicit
return NULL;
case TYPE_TYPEDEF:
UNREACHABLE // Should only handle canonical types
case TYPE_UNTYPED_LIST:
if (other->type_kind == TYPE_ARRAY) return other;
if (other->type_kind == TYPE_VECTOR) return other;
if (other->type_kind == TYPE_STRUCT) return other;
if (other->type_kind == TYPE_SLICE) return other;
return NULL;
case TYPE_UNION:
case TYPE_STRUCT:
// union/struct + anything else => no
// even if the struct has an inline type, this should not
// be implicit
return NULL;
case TYPE_TYPEID:
case TYPE_MEMBER:
return NULL;
case TYPE_TYPEDEF:
UNREACHABLE
case TYPE_SLICE:
// slice + [array, vector of the same type] => yes
if (type_is_arraylike(other) && (other->array.base->canonical == type->array.base->canonical))
@@ -2135,27 +2203,24 @@ RETRY_DISTINCT:
case TYPE_ARRAY:
// array + [other array, vector] => no
return NULL;
case TYPE_FLEXIBLE_ARRAY:
case TYPE_INFERRED_ARRAY:
case TYPE_INFERRED_VECTOR:
// Already handled
UNREACHABLE
case TYPE_OPTIONAL:
// Should never be passed here
UNREACHABLE
case TYPE_WILDCARD:
// Handled above
UNREACHABLE
case TYPE_TYPEINFO:
case TYPE_MEMBER:
return NULL;
UNREACHABLE
case TYPE_VECTOR:
// No implicit conversion between vectors
return NULL;
case TYPE_DISTINCT:
case TYPE_CONST_ENUM:
if (type_is_distinct_like(other))
{
return type_find_max_distinct_type(type, other);
}
if (other->type_kind == TYPE_ENUM && other->decl->is_substruct)
{
return type_find_max_type(type, type_flat_distinct_enum_inline(other));
}
// Try matching with its inline type
if (type->decl->is_substruct)
{
type = type_inline(type);
goto RETRY_DISTINCT;
}
// distinct + any other type => no
return NULL;
}
UNREACHABLE
}

View File

@@ -0,0 +1,44 @@
// #target: macos-x64
module test;
import std;
typedef DistinctInt = inline int;
enum Foo : inline int
{
ABC,
}
enum Bar : const inline int
{
HELLO = 1
}
fn int main()
{
int a = 1;
DistinctInt b = 2;
int c = a + b;
c = a + Bar.HELLO;
c = a + Foo.ABC;
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
store i32 1, ptr %a, align 4
store i32 2, ptr %b, align 4
%0 = load i32, ptr %a, align 4
%1 = load i32, ptr %b, align 4
%add = add i32 %0, %1
store i32 %add, ptr %c, align 4
%2 = load i32, ptr %a, align 4
%add1 = add i32 %2, 1
store i32 %add1, ptr %c, align 4
%3 = load i32, ptr %a, align 4
%add2 = add i32 %3, 0
store i32 %add2, ptr %c, align 4
ret i32 0
}