diff --git a/releasenotes.md b/releasenotes.md index f617b0008..2c331618d 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -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 diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 8d69ad13f..278ae64dc 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -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); } } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index ab0073481..147d4c0fb 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -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); diff --git a/src/compiler/types.c b/src/compiler/types.c index badd41bb2..51bdfa04e 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -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 } diff --git a/test/test_suite/expressions/casts/inline_to_underlying.c3t b/test/test_suite/expressions/casts/inline_to_underlying.c3t new file mode 100644 index 000000000..a2fd1b893 --- /dev/null +++ b/test/test_suite/expressions/casts/inline_to_underlying.c3t @@ -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 +} \ No newline at end of file