diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 07f077f2b..28f27aee6 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2071,7 +2071,6 @@ bool cast_implicit_maybe_optional(SemaContext *context, Expr *expr, Type *to_typ bool cast_explicit(SemaContext *context, Expr *expr, Type *to_type); bool cast(Expr *expr, Type *to_type); -bool cast_may_bool_convert(Type *type); Type *type_infer_len_from_actual_type(Type *to_infer, Type *actual_type); bool cast_to_index(Expr *index); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 7eae44058..4bb5c677c 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -1,22 +1,28 @@ -// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Copyright (c) 2019-2023 Christoffer Lerno. All rights reserved. // Use of this source code is governed by the GNU LGPLv3.0 license // a copy of which can be found in the LICENSE file. +// +// This source file contains functions related to both explicit and +// implicit conversions. C3 has a fairly complex set of rules, +// which makes this code somewhat lengthy. #include "sema_internal.h" #pragma clang diagnostic push #pragma ide diagnostic ignored "ConstantFunctionResult" +static bool cast_expr_inner(SemaContext *context, Expr *expr, Type *to_type, bool is_explicit, bool silent, bool may_not_be_optional); +static bool cast_from_pointer(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent); +static bool cast_from_subarray(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent); +static bool cast_from_vector(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent); +static bool cast_from_array(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent); -static inline bool sema_error_cannot_convert(Expr *expr, Type *to, bool may_cast_explicit, bool silent); -static bool cast_expr_inner(SemaContext *context, Expr *expr, Type *to_type, bool is_explicit, bool silent, - bool may_not_be_optional); static bool bitstruct_cast(Expr *expr, Type *from_type, Type *to, Type *to_type); static void sema_error_const_int_out_of_range(Expr *expr, Expr *problem, Type *to_type); static Expr *recursive_may_narrow(Expr *expr, Type *type); -static void recursively_rewrite_untyped_list(Expr *expr, Expr **list); +static void expr_recursively_rewrite_untyped_list(Expr *expr, Expr **list); static inline bool cast_may_implicit_ptr(Type *from_pointee, Type *to_pointee); -static inline bool cast_may_array(Type *from, Type *to, bool is_explicit); +static inline bool cast_to_array_is_valid(Type *from, Type *to); static inline bool insert_cast(Expr *expr, CastKind kind, Type *type); static bool pointer_to_integer(Expr *expr, Type *type); static bool pointer_to_bool(Expr *expr, Type *type); @@ -36,14 +42,13 @@ static bool array_to_vector(Expr *expr, Type *to_type); static bool vector_to_array(Expr *expr, Type *to_type); static bool vector_to_vector(Expr *expr, Type *to_type); INLINE bool subarray_to_subarray(Expr *expr, Type *to_type); - static void vector_const_initializer_convert_to_type(ConstInitializer *initializer, Type *to_type); - +static bool cast_vector_element_may_implicitly_convert(Expr *expr, Type *from, Type *to); static void enum_to_int_lowering(Expr* expr); - static bool voidfail_to_error(Expr *expr, Type *type); - INLINE bool insert_runtime_cast_unless_const(Expr *expr, CastKind kind, Type *type); +INLINE bool cast_with_optional(Expr *expr, Type *to_type, bool add_optional); +static inline bool sema_error_cannot_convert(Expr *expr, Type *to, bool may_cast_explicit, bool silent); /** * Insert a cast. This will assume that the cast is valid. No typeinfo will be registered. @@ -602,6 +607,19 @@ bool cast_promote_vararg(Expr *arg) return true; } +/** + * Cast an untyped list to a particular type. + */ +bool cast_untyped_to_type(SemaContext *context, Expr *expr, Type *to_type) +{ + // Recursively set the type of all ConstInitializer inside. + expr_recursively_rewrite_untyped_list(expr, expr->const_expr.untyped_list); + // We can now analyse the list (this is where the actual check happens) + if (!sema_expr_analyse_initializer_list(context, type_flatten(to_type), expr)) return false; + // And set the type. + expr->type = to_type; + return true; +} /** * Given lhs and rhs, promote to the maximum bit size, this will retain @@ -658,11 +676,21 @@ CastKind cast_to_bool_kind(Type *type) return CAST_PTRBOOL; case TYPE_FAULTTYPE: return CAST_ERBOOL; + case TYPE_TYPEDEF: + case TYPE_DISTINCT: + case TYPE_OPTIONAL: + case TYPE_OPTIONAL_ANY: + case TYPE_ENUM: + // These are not possible due to flattening. + UNREACHABLE + case TYPE_INFERRED_ARRAY: + case TYPE_INFERRED_VECTOR: + // These should never be here, type should already be known. + UNREACHABLE case TYPE_POISONED: case TYPE_VOID: case TYPE_STRUCT: case TYPE_UNION: - case TYPE_ENUM: case TYPE_FUNC: case TYPE_ARRAY: case TYPE_TYPEID: @@ -670,192 +698,17 @@ CastKind cast_to_bool_kind(Type *type) case TYPE_VECTOR: case TYPE_BITSTRUCT: case TYPE_UNTYPED_LIST: - case TYPE_OPTIONAL: case TYPE_ANY: - case TYPE_OPTIONAL_ANY: case TYPE_FLEXIBLE_ARRAY: case TYPE_SCALED_VECTOR: - case TYPE_INFERRED_VECTOR: case TYPE_MEMBER: + // Everything else is an error return CAST_ERROR; - case TYPE_TYPEDEF: - case TYPE_DISTINCT: - case TYPE_INFERRED_ARRAY: - UNREACHABLE } UNREACHABLE } -static inline bool cast_may_array(Type *from, Type *to, bool is_explicit) -{ - RETRY:; - assert(!type_is_optional(from) && !type_is_optional(to) && "Optional should already been handled"); - - bool compare_len = true; - if (from->type_kind != to->type_kind) - { - switch (to->type_kind) - { - case TYPE_INFERRED_ARRAY: - switch (from->type_kind) - { - case TYPE_INFERRED_VECTOR: - case TYPE_VECTOR: - if (!is_explicit) return false; - FALLTHROUGH; - case TYPE_ARRAY: - compare_len = false; - break; - default: - return false; - } - break; - case TYPE_ARRAY: - switch (from->type_kind) - { - case TYPE_INFERRED_VECTOR: - compare_len = false; - FALLTHROUGH; - case TYPE_VECTOR: - if (!is_explicit) return false; - break; - case TYPE_INFERRED_ARRAY: - compare_len = false; - break; - default: - return false; - } - break; - case TYPE_INFERRED_VECTOR: - switch (from->type_kind) - { - case TYPE_INFERRED_ARRAY: - case TYPE_ARRAY: - if (!is_explicit) return false; - FALLTHROUGH; - case TYPE_VECTOR: - case TYPE_SCALED_VECTOR: - compare_len = false; - break; - default: - return false; - } - break; - case TYPE_VECTOR: - switch (from->type_kind) - { - case TYPE_INFERRED_ARRAY: - compare_len = false; - FALLTHROUGH; - case TYPE_ARRAY: - if (!is_explicit) return false; - break; - case TYPE_INFERRED_VECTOR: - compare_len = false; - break; - default: - return false; - } - break; - case TYPE_SCALED_VECTOR: - if (from->type_kind != TYPE_INFERRED_VECTOR) return false; - compare_len = false; - break; - default: - return false; - } - } - if (compare_len && to->array.len != from->array.len) return false; - - Type *from_base = from->array.base; - Type *to_base = to->array.base; - if (is_explicit) - { - from_base = type_flatten(from_base); - to_base = type_flatten(to_base); - } - - if (from_base == to_base) return true; - - switch (to_base->type_kind) - { - case TYPE_POINTER: - if (from_base->type_kind == TYPE_POINTER) - { - if (is_explicit) return true; - return cast_may_implicit_ptr(to_base->pointer, from_base->pointer); - } - return false; - case TYPE_ARRAY: - case TYPE_INFERRED_ARRAY: - case TYPE_VECTOR: - case TYPE_INFERRED_VECTOR: - to = to_base; - from = from_base; - goto RETRY; - default: - return is_explicit && type_is_structurally_equivalent(to_base, from_base); - } -} - -static inline bool cast_may_implicit_ptr(Type *from_pointee, Type *to_pointee) -{ - assert(!type_is_optional(from_pointee) && !type_is_optional(to_pointee) && "Optional should already been handled"); - if (from_pointee == to_pointee) return true; - - // For void* on either side, no checks. - if (to_pointee == type_voidptr || from_pointee == type_voidptr) return true; - - // Step through all *: - while (from_pointee->type_kind == TYPE_POINTER && to_pointee->type_kind == TYPE_POINTER) - { - if (from_pointee == type_voidptr || to_pointee == type_voidptr) return true; - from_pointee = from_pointee->pointer; - to_pointee = to_pointee->pointer; - } - - assert(to_pointee != from_pointee); - - // Functions compare raw types. - if (from_pointee->type_kind == TYPE_FUNC && to_pointee->type_kind == TYPE_FUNC) - { - return to_pointee->function.prototype->raw_type == from_pointee->function.prototype->raw_type; - } - - // Special handling of int* = int[4]* (so we have int[4] -> int) - if (type_is_arraylike(from_pointee)) - { - if (cast_may_implicit_ptr(to_pointee, from_pointee->array.base)) return true; - } - - if (type_is_any_arraylike(to_pointee) || type_is_any_arraylike(from_pointee)) - { - return cast_may_array(from_pointee, to_pointee, false); - } - // Use subtype matching - return type_is_subtype(to_pointee, from_pointee); -} - -bool cast_may_bool_convert(Type *type) -{ - switch (type_flatten_distinct(type)->type_kind) - { - case TYPE_BOOL: - case ALL_INTS: - case ALL_FLOATS: - case TYPE_SUBARRAY: - case TYPE_POINTER: - case TYPE_ANY: - case TYPE_FAULTTYPE: - case TYPE_ANYERR: - return true; - default: - return false; - } - return true; -} - /** * Check whether an expression may narrow. * 1. If it has an intrinsic type, then compare it against the type. If the bitwidth is smaller or same => ok @@ -1024,6 +877,136 @@ CHECK_SIZE: return NULL; } +/** + * Cast to a given type, making it an optional type as needed. + * + */ +INLINE bool cast_with_optional(Expr *expr, Type *to_type, bool add_optional) +{ + if (!cast(expr, to_type)) return false; + if (add_optional) expr->type = type_add_optional(expr->type, true); + return true; +} + +/** + * Cast a subarray to some other type: + * 1. To another subarray => works if the array elements are equivalent. + * 2. To another pointer => works if the pointer is void* or the array element and pointee is equivalent. + * 3. To bool => explicit cast is ok. + */ +static bool cast_from_subarray(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) +{ + switch (to->type_kind) + { + case TYPE_SUBARRAY: + // Casting to another subarray works if the elements are equivalent. + if (!type_array_element_is_equivalent(from->array.base, to->array.base, is_explicit)) + { + return sema_error_cannot_convert(expr, to, !is_explicit && type_array_element_is_equivalent(from->array.base, to->array.base, true), silent); + } + return cast_with_optional(expr, to_type, add_optional); + case TYPE_POINTER: + // Casting to another pointer works if either the pointer is void* or the element and pointee are equivalent. + if (to != type_voidptr && !type_array_element_is_equivalent(from->array.base, to->pointer, is_explicit)) + { + return sema_error_cannot_convert(expr, to, !is_explicit && type_array_element_is_equivalent(from->array.base, to->pointer, true), silent); + } + return cast_with_optional(expr, to_type, add_optional); + case TYPE_BOOL: + // Only explicit bool casts are allowed. + if (!is_explicit) return sema_error_cannot_convert(expr, to_type, true, silent); + return cast_with_optional(expr, to_type, add_optional); + default: + return sema_error_cannot_convert(expr, to_type, false, silent); + } + UNREACHABLE +} + +/** + * Try casting to a pointer. + * 1. Any pointer -> variant, any pointer -> void*, void* -> any pointer - always works. + * 2. Pointer -> integer must be explicit and type size >= uptr + * 3. Pointer -> subarray if the pointer points to a vector or array, allow void*[2]* -> int*[2]* (pointer equivalence). + * 4. Pointer -> bool must be explicit (conditionals are treated as a special case. + * 5. Pointer -> pointer explicit always works, otherwise they must have pointer equivalence. + */ +static bool cast_from_pointer(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) +{ + // pointer -> any, void* -> pointer pointer -> void* + if (to == type_any || to == type_voidptr || (from == type_voidptr && type_is_pointer(to))) return cast_with_optional(expr, to_type, add_optional); + + Type *pointee = from->pointer; + pointee = is_explicit ? type_flatten(pointee) : pointee->canonical; + TypeKind pointee_kind = pointee->type_kind; + switch (to->type_kind) + { + case ALL_INTS: + // Only explicit cast + if (!is_explicit) return sema_error_cannot_convert(expr, to_type, true, silent); + // The type must be uptr or bigger. + if (type_size(to_type) < type_size(type_uptr)) + { + SEMA_ERROR(expr, "Casting %s to %s is not allowed because '%s' is smaller than a pointer. " + "Use (%s)(iptr) if you want this lossy cast.", + type_quoted_error_string(expr->type), type_quoted_error_string(to_type), + type_to_error_string(to_type), type_to_error_string(to_type)); + return false; + } + return cast_with_optional(expr, to_type, add_optional); + case TYPE_SUBARRAY: + // int[<2>]*, int[2]* + if (pointee_kind == TYPE_ARRAY || pointee_kind == TYPE_VECTOR) + { + Type *subarray_base = to->array.base->canonical; + Type *from_base = pointee->array.base; + if (is_explicit) + { + subarray_base = type_flatten_distinct(subarray_base); + from_base = type_flatten_distinct(from_base); + } + // Same base type? E.g. int[2]* -> int[], then we're done. + if (subarray_base == from_base) return cast_with_optional(expr, to_type, add_optional); + + // Otherwise we might have int*[2]* -> void*[], use pointer equivalence. + if (subarray_base->type_kind == TYPE_POINTER && from_base->type_kind == TYPE_POINTER) + { + if (type_is_pointer_equivalent(subarray_base, from_base, is_explicit)) return cast_with_optional(expr, to_type, add_optional); + } + // Silent? Then we're done. + if (silent) return false; + // Check if this would work if explicit for a good error message: + bool would_work_explicit = false; + if (!is_explicit) + { + would_work_explicit = cast_from_pointer(context, expr_copy(expr), + from, to, to_type, add_optional, true, true); + } + return sema_error_cannot_convert(expr, to_type, would_work_explicit, false); + } + // All other fails. + return sema_error_cannot_convert(expr, to_type, false, silent); + case TYPE_BOOL: + // Only explicit conversion allowed. + if (is_explicit) return cast_with_optional(expr, to_type, add_optional); + return sema_error_cannot_convert(expr, to_type, true, silent); + case TYPE_POINTER: + // Explicit conversion always works. + if (is_explicit) return cast_with_optional(expr, to_type, add_optional); + // See if the pointee is equivalent. + if (type_is_pointer_equivalent(from, to, false)) + { + return cast_with_optional(expr, to_type, add_optional); + } + return sema_error_cannot_convert(expr, to_type, true, silent); + case TYPE_OPTIONAL_ANY: + case TYPE_OPTIONAL: + UNREACHABLE + default: + // All other types are forbidden. + return sema_error_cannot_convert(expr, to_type, false, silent); + } +} + static void sema_error_const_int_out_of_range(Expr *expr, Expr *problem, Type *to_type) { assert(expr->expr_kind == EXPR_CONST); @@ -1064,15 +1047,11 @@ static inline bool cast_maybe_string_lit_to_char_array(Expr *expr, Type *expr_ca return true; } -bool cast_untyped_to_type(SemaContext *context, Expr *expr, Type *to_type) -{ - recursively_rewrite_untyped_list(expr, expr->const_expr.untyped_list); - if (!sema_expr_analyse_initializer_list(context, type_flatten(to_type), expr)) return false; - expr->type = to_type; - return true; -} -static void recursively_rewrite_untyped_list(Expr *expr, Expr **list) +/** + * Recursively change a const list to an initializer list. + */ +static void expr_recursively_rewrite_untyped_list(Expr *expr, Expr **list) { expr->expr_kind = EXPR_INITIALIZER_LIST; expr->initializer_list = list; @@ -1080,38 +1059,46 @@ static void recursively_rewrite_untyped_list(Expr *expr, Expr **list) FOREACH_BEGIN(Expr *inner, list) if (expr_is_const_untyped_list(inner)) { - recursively_rewrite_untyped_list(inner, inner->const_expr.untyped_list); + expr_recursively_rewrite_untyped_list(inner, inner->const_expr.untyped_list); } FOREACH_END(); } +/** + * Try to make an implicit cast. Optional types are allowed. + */ bool cast_implicit(SemaContext *context, Expr *expr, Type *to_type) { return cast_expr_inner(context, expr, to_type, false, false, false); } - -bool cast_implicit_maybe_optional(SemaContext *context, Expr *expr, Type *to_type, bool may_be_optional) -{ - return cast_expr_inner(context, expr, to_type, false, false, !may_be_optional); -} - -bool cast_implicit_silent(SemaContext *context, Expr *expr, Type *to_type) -{ - return cast_expr_inner(context, expr, to_type, false, true, false); -} - +/** + * Try to make an explicit cast, Optional types are allowed. + */ bool cast_explicit(SemaContext *context, Expr *expr, Type *to_type) { return cast_expr_inner(context, expr, to_type, true, false, false); } -static inline bool cast_with_optional(Expr *expr, Type *to_type, bool add_optional) +/** + * Try to make an implicit cast. If optional is allowed or not depends on the parameter. + * This is mostly used with assignment. + */ +bool cast_implicit_maybe_optional(SemaContext *context, Expr *expr, Type *to_type, bool may_be_optional) { - if (!cast(expr, to_type)) return false; - if (add_optional) expr->type = type_add_optional(expr->type, true); - return true; + return cast_expr_inner(context, expr, to_type, false, false, !may_be_optional); } +/** + * Silent implicit casting will attempt a cast, but will silently back out if it fails. + */ +bool cast_implicit_silent(SemaContext *context, Expr *expr, Type *to_type) +{ + return cast_expr_inner(context, expr, to_type, false, true, false); +} + +/** + * Common error reporting for casts. + */ static inline bool sema_error_cannot_convert(Expr *expr, Type *to, bool may_cast_explicit, bool silent) { if (silent) return false; @@ -1133,166 +1120,141 @@ static inline bool sema_error_cannot_convert(Expr *expr, Type *to, bool may_cast } -static inline bool cast_subarray(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) +/** + * Attempt casting an array. + * 1. Inferred array / vector => infer type + * 2. Array / vector => ok if len matches. + * 3. Check for array element equivalence. + */ +static bool cast_from_array(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) { - switch (to->type_kind) - { - case TYPE_SUBARRAY: - if (type_array_element_is_equivalent(from->array.base, to->array.base, is_explicit)) goto CAST; - return sema_error_cannot_convert(expr, to, !is_explicit && type_array_element_is_equivalent(from->array.base, to->array.base, true), silent); - case TYPE_POINTER: - if (to == type_voidptr) goto CAST; - if (type_array_element_is_equivalent(from->array.base, to->pointer, is_explicit)) goto CAST; - return sema_error_cannot_convert(expr, to, !is_explicit && type_array_element_is_equivalent(from->array.base, to->pointer, true), silent); - case TYPE_BOOL: - if (!is_explicit) goto CAST_MAY_EXPLICIT; - goto CAST; - default: - goto CAST_ILLEGAL; - } -CAST_ILLEGAL: - return sema_error_cannot_convert(expr, to_type, false, silent); -CAST_MAY_EXPLICIT: - return sema_error_cannot_convert(expr, to_type, true, silent); -CAST: - return cast_with_optional(expr, to_type, add_optional); -} - -static inline bool cast_array(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) -{ - bool infer_type = false; switch (to->type_kind) { case TYPE_INFERRED_ARRAY: case TYPE_INFERRED_VECTOR: - infer_type = true; - goto CAST_ELEMENT; + // We infer the target type (note that to and to_type are now of different kinds. + to_type = type_infer_len_from_actual_type(to_type, from); + break; case TYPE_VECTOR: case TYPE_ARRAY: - if (to->array.len != from->array.len) goto CAST_ILLEGAL; - goto CAST_ELEMENT; - case TYPE_STRUCT: - if (type_is_structurally_equivalent(from, to)) - { - if (is_explicit) goto CAST; - if (silent) return false; - return sema_error_cannot_convert(expr, to_type, true, silent); - } + // Len must be checked. + if (to->array.len != from->array.len) return sema_error_cannot_convert(expr, to_type, false, silent); + break; default: - goto CAST_ILLEGAL; + // No other conversions are allowed. + return sema_error_cannot_convert(expr, to_type, false, silent); } -CAST_ELEMENT: + // Check array element equivalence. if (!type_array_element_is_equivalent(from->array.base, to->array.base, is_explicit)) { - if (!silent && !is_explicit && type_array_element_is_equivalent(from->array.base, to->array.base, true)) - { - return sema_error_cannot_convert(expr, to_type, true, silent); - } - goto CAST_ILLEGAL; - } -CAST: - if (infer_type) - { - to_type = type_infer_len_from_actual_type(to_type, from); + if (silent) return false; + // Create a good error message, so we can give a hint that maybe you can do an explicit cast. + bool explicit_would_work = !is_explicit && type_array_element_is_equivalent(from->array.base, to->array.base, true); + return sema_error_cannot_convert(expr, to_type, explicit_would_work, silent); } + // Insert the cast. return cast_with_optional(expr, to_type, add_optional); -CAST_ILLEGAL: - return sema_error_cannot_convert(expr, to_type, false, silent); } -static bool cast_may_number_convert(Expr *expr, Type *from, Type *to, bool is_explicit, bool cast_from_bool) +static bool cast_vector_element_may_implicitly_convert(Expr *expr, Type *from, Type *to) { - // Same canonical type - we're fine. + from = from->canonical; + to = to->canonical; + + // Same type - we're fine. if (from == to) return true; + // If any of the elements are distinct we know it doesn't work. if (from->type_kind == TYPE_DISTINCT || to->type_kind == TYPE_DISTINCT) return false; + // Casting from bool always works (for int, float) + if (from == type_bool) return true; + + // Casting to bool never works (for int, float) + if (to == type_bool) return false; + // Handle floats if (type_is_float(to)) { // Any integer may convert to a float. if (type_is_integer(from)) return true; - // Cast from bool if explicit or cast from bool. - if (from == type_bool) return cast_from_bool || is_explicit; - - // Any narrower float if (type_is_float(from)) { - if (is_explicit) return true; + // Explict casts always work ByteSize to_size = type_size(to); ByteSize from_size = type_size(from); + // Same size is ok if (to_size == from_size) return true; + // Widening is ok if the expression is simple. return to_size > from_size && expr_is_simple(expr); } - + // No other possibility UNREACHABLE; } - // Handle ints - if (type_is_integer(to)) - { - if (type_is_float(from)) return is_explicit; + // Last case is integer. + assert(type_is_integer(to)); - // Cast from bool if explicit or cast from bool. - if (from == type_bool) return cast_from_bool || is_explicit; + // float -> int can't be done implicitly + if (type_is_float(from)) return false; - if (type_is_integer(from)) - { - if (is_explicit) return true; - ByteSize to_size = type_size(to); - ByteSize from_size = type_size(from); - if (to_size == from_size) return true; - return to_size > from_size && expr_is_simple(expr); - } + assert(type_is_integer(from)); - UNREACHABLE - } - assert(to == type_bool); - if (!is_explicit) return false; - return true; + ByteSize to_size = type_size(to); + ByteSize from_size = type_size(from); + // Same size is ok + if (to_size == from_size) return true; + // Widening is ok if expression is simple. Narrowing is prohibited. + return to_size > from_size && expr_is_simple(expr); } -static inline bool cast_vector(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) + +static bool cast_from_vector(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) { - bool is_scaled = from->type_kind == TYPE_SCALED_VECTOR; - bool to_vector = type_kind_is_any_vector(to->type_kind); - bool infer_len = false; + bool not_to_vector = !type_kind_is_any_vector(to->type_kind); switch (to->type_kind) { case TYPE_INFERRED_ARRAY: - infer_len = true; - if (is_scaled) goto CAST_ILLEGAL; - goto TRY_CAST; case TYPE_INFERRED_VECTOR: - infer_len = true; - if (is_scaled) goto CAST_ILLEGAL; - goto TRY_CONVERT; + // Update the to_type (but not to!) + to_type = type_infer_len_from_actual_type(to_type, from); + break; case TYPE_ARRAY: - if (is_scaled || to->array.len != from->array.len) goto CAST_ILLEGAL; - goto TRY_CAST; case TYPE_VECTOR: - if (is_scaled || to->array.len != from->array.len) goto CAST_ILLEGAL; - goto TRY_CONVERT; - case TYPE_SCALED_VECTOR: - if (!is_scaled) goto CAST_ILLEGAL; - goto TRY_CONVERT; + // Len must match. + if (to->array.len != from->array.len) return sema_error_cannot_convert(expr, to_type, false, silent); + break; default: - goto CAST_ILLEGAL; + // All other conversions fail + return sema_error_cannot_convert(expr, to_type, false, silent); + } + Type *from_base = from->array.base; + Type *to_base = to->array.base; + // vector -> array cast. + if (not_to_vector) + { + // Here we do simple array element equivalence, that is int[<2>] -> int[2] is ok, but not int[<2>] -> float[2] + if (!type_array_element_is_equivalent(from_base, to_base, is_explicit)) + { + // Give us a nice hint in case an explicit conversion would work. + if (silent) return false; + bool may_explicit = !is_explicit && type_array_element_is_equivalent(from_base, to_base, true); + return sema_error_cannot_convert(expr, to, may_explicit, true); + } + return cast_with_optional(expr, to_type, add_optional); + } + // Vector -> vector cast + // This allows for things like int[<2>] -> float[<2>] conversions. + if (!is_explicit && !cast_vector_element_may_implicitly_convert(expr, from_base, to_base)) + { + if (silent) return false; + // Give us a nice hint that explicit conversion would work. + return sema_error_cannot_convert(expr, to, true, true); } -TRY_CONVERT: - if (cast_may_number_convert(expr, from->array.base, to->array.base, is_explicit, to_vector)) goto CAST; - return sema_error_cannot_convert(expr, to, !is_explicit && cast_may_number_convert(expr, from->array.base, to->array.base, true, to_vector), silent); -TRY_CAST: - if (type_array_element_is_equivalent(from->array.base, to->array.base, is_explicit)) goto CAST; - if (silent) return false; - return sema_error_cannot_convert(expr, to, !is_explicit && type_array_element_is_equivalent(from->array.base, to->array.base, true), true); -CAST_ILLEGAL: - return sema_error_cannot_convert(expr, to_type, false, silent); -CAST: - if (infer_len) to_type = type_infer_len_from_actual_type(to_type, from); return cast_with_optional(expr, to_type, add_optional); } + static inline bool cast_integer(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) { bool no_report = silent; @@ -1447,69 +1409,6 @@ CAST: return cast_with_optional(expr, to_type, add_optional); } -static inline bool cast_pointer(SemaContext *context, Expr *expr, Type *from, Type *to, Type *to_type, bool add_optional, bool is_explicit, bool silent) -{ - // pointer -> any, void* -> pointer pointer -> void* - if (to == type_any || to == type_voidptr || (from == type_voidptr && type_is_pointer(to))) return cast_with_optional(expr, to_type, add_optional); - - Type *pointee = from->pointer; - pointee = is_explicit ? type_flatten(pointee) : pointee->canonical; - TypeKind pointee_kind = pointee->type_kind; - switch (to->type_kind) - { - case ALL_INTS: - if (!is_explicit) return sema_error_cannot_convert(expr, to_type, true, silent); - if (type_size(to_type) < type_size(type_iptr)) - { - SEMA_ERROR(expr, "Casting %s to %s is not allowed because '%s' is smaller than a pointer. " - "Use (%s)(iptr) if you want this lossy cast.", - type_quoted_error_string(expr->type), type_quoted_error_string(to_type), - type_to_error_string(to_type), type_to_error_string(to_type)); - return false; - } - return cast_with_optional(expr, to_type, add_optional); - case TYPE_SUBARRAY: - // int[<2>], int[2], int[<*>] - if (pointee_kind == TYPE_ARRAY || pointee_kind == TYPE_VECTOR || pointee_kind == TYPE_SCALED_VECTOR) - { - Type *subarray_base = to->array.base->canonical; - Type *from_base = pointee->array.base; - if (is_explicit) - { - subarray_base = type_flatten_distinct(subarray_base); - from_base = type_flatten_distinct(from_base); - } - if (subarray_base == from_base) return cast_with_optional(expr, to_type, add_optional); - if (subarray_base->type_kind == TYPE_POINTER && from_base->type_kind == TYPE_POINTER) - { - if (type_is_pointer_equivalent(subarray_base, from_base, is_explicit)) return cast_with_optional(expr, to_type, add_optional); - } - if (silent) return false; - bool would_work_explicit = false; - if (!is_explicit) - { - would_work_explicit = cast_pointer(context, expr_copy(expr), from, to, to_type, add_optional, true, true); - } - return sema_error_cannot_convert(expr, to_type, would_work_explicit, false); - } - return sema_error_cannot_convert(expr, to_type, false, silent); - case TYPE_BOOL: - if (is_explicit) return cast_with_optional(expr, to_type, add_optional); - return sema_error_cannot_convert(expr, to_type, true, silent); - case TYPE_POINTER: - if (is_explicit) return cast_with_optional(expr, to_type, add_optional); - if (cast_may_implicit_ptr(from->pointer, to->pointer)) - { - return cast_with_optional(expr, to_type, add_optional); - } - return sema_error_cannot_convert(expr, to_type, true, silent); - case TYPE_OPTIONAL_ANY: - case TYPE_OPTIONAL: - UNREACHABLE - default: - return sema_error_cannot_convert(expr, to_type, false, silent); - } -} /** * Do the following: * 1. Special optional conversions. @@ -1634,18 +1533,18 @@ static bool cast_expr_inner(SemaContext *context, Expr *expr, Type *to_type, boo } goto CAST_FAILED; case TYPE_POINTER: - return cast_pointer(context, expr, from, to, to_type, add_optional, is_explicit, silent); + return cast_from_pointer(context, expr, from, to, to_type, add_optional, is_explicit, silent); case TYPE_SUBARRAY: - return cast_subarray(context, expr, from, to, to_type, add_optional, is_explicit, silent); + return cast_from_subarray(context, expr, from, to, to_type, add_optional, is_explicit, silent); case TYPE_BOOL: // Bool may convert into integers and floats but only explicitly. if (type_is_integer(to) || type_is_float(to)) goto CAST_IF_EXPLICIT; goto CAST_FAILED; case TYPE_VECTOR: case TYPE_SCALED_VECTOR: - return cast_vector(context, expr, from, to, to_type, add_optional, is_explicit, silent); + return cast_from_vector(context, expr, from, to, to_type, add_optional, is_explicit, silent); case TYPE_ARRAY: - return cast_array(context, expr, from, to, to_type, add_optional, is_explicit, silent); + return cast_from_array(context, expr, from, to, to_type, add_optional, is_explicit, silent); case TYPE_ENUM: case ALL_INTS: return cast_integer(context, expr, from, to, to_type, add_optional, is_explicit, silent); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 7da68a20a..1d15624f1 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -5484,7 +5484,7 @@ static inline bool sema_expr_analyse_not(SemaContext *context, Expr *expr) } } - if (!cast_may_bool_convert(type)) + if (cast_to_bool_kind(type_flatten(type)) == CAST_ERROR) { SEMA_ERROR(expr, "The %s can't be converted to a boolean value.", type_quoted_error_string(inner->type)); return false; diff --git a/src/compiler/types.c b/src/compiler/types.c index 9cf330323..79fd2cd04 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1658,9 +1658,33 @@ Type *type_from_token(TokenType type) } } +bool type_array_is_equivalent(Type *from, Type *to, bool is_explicit) +{ + TypeKind to_kind = to->type_kind; + switch (from->type_kind) + { + case TYPE_INFERRED_ARRAY: + assert(to_kind != TYPE_INFERRED_ARRAY); + if (to_kind != TYPE_ARRAY) return false; + return type_array_element_is_equivalent(from->array.base, to->array.base, is_explicit); + case TYPE_ARRAY: + if (to_kind != TYPE_ARRAY && to_kind != TYPE_INFERRED_ARRAY) return false; + if (to->type_kind == TYPE_ARRAY && from->array.len != to->array.len) return false; + return type_array_element_is_equivalent(from->array.base, to->array.base, is_explicit); + case TYPE_INFERRED_VECTOR: + assert(to_kind != TYPE_INFERRED_VECTOR); + if (to->type_kind != TYPE_VECTOR) return false; + return type_array_element_is_equivalent(from->array.base, to->array.base, is_explicit); + case TYPE_VECTOR: + if (to_kind != TYPE_VECTOR && to_kind != TYPE_INFERRED_VECTOR) return false; + if (to->type_kind == TYPE_VECTOR && from->array.len != to->array.len) return false; + return type_array_element_is_equivalent(from->array.base, to->array.base, is_explicit); + default: + return false; + } +} bool type_array_element_is_equivalent(Type *element1, Type *element2, bool is_explicit) { - RETRY: if (is_explicit) { element1 = type_flatten_distinct(element1); @@ -1677,19 +1701,14 @@ bool type_array_element_is_equivalent(Type *element1, Type *element2, bool is_ex case TYPE_POINTER: if (element2->type_kind != TYPE_POINTER) return false; return type_is_pointer_equivalent(element1, element2, is_explicit); - case TYPE_ARRAY: - if (element2->type_kind != TYPE_INFERRED_ARRAY && element1->array.len != element2->array.len) return false; - element1 = element1->array.base; - element2 = element2->array.base; - goto RETRY; - case TYPE_VECTOR: - if (element2->type_kind != TYPE_INFERRED_VECTOR && element1->array.len != element2->array.len) return false; - element1 = element1->array.base; - element2 = element2->array.base; - goto RETRY; case TYPE_STRUCT: if (is_explicit) return type_is_structurally_equivalent(element1, element2); return false; + case TYPE_VECTOR: + case TYPE_ARRAY: + case TYPE_INFERRED_ARRAY: + case TYPE_INFERRED_VECTOR: + return type_array_is_equivalent(element1, element2, is_explicit); default: return false; } @@ -1703,6 +1722,7 @@ RETRY: pointer1 = type_flatten_distinct(pointer1); pointer2 = type_flatten_distinct(pointer2); } + if (pointer1 == pointer2) return true; if (pointer1 == type_voidptr || pointer2 == type_voidptr) return true; Type *pointee1 = pointer1->pointer->canonical; Type *pointee2 = pointer2->pointer->canonical; @@ -1711,16 +1731,37 @@ RETRY: pointee1 = type_flatten_distinct(pointee1); pointee2 = type_flatten_distinct(pointee2); } - if (pointee1->type_kind != pointee2->type_kind) return false; + if (pointee1 == pointee2) return true; + if (type_is_subtype(pointee2, pointee1)) return true; + + if (pointee1->type_kind != pointee2->type_kind) + { + if (type_is_any_arraylike(pointee1)) + { + // Try array equivalence. + if (type_is_any_arraylike(pointee2)) + { + if (type_array_is_equivalent(pointee1, pointee2, flatten_distinct)) return true; + + } + // A possible int[4]* -> int* decay? + return type_is_pointer_equivalent(type_get_ptr(pointee1->array.base), pointer2, flatten_distinct); + } + // Not arraylike and no array decay. Failure. + return false; + } + + if (pointee1->type_kind == TYPE_FUNC && pointee2->type_kind == TYPE_FUNC) + { + return pointee1->function.prototype->raw_type == pointee2->function.prototype->raw_type; + } if (pointee1->type_kind == TYPE_POINTER) { pointer1 = pointee1; pointer2 = pointee2; goto RETRY; } - if (!type_is_arraylike(pointee1)) return false; - if (pointee1->array.len != pointee2->array.len) return false; - return type_array_element_is_equivalent(pointee1->array.base, pointer2->array.base, flatten_distinct); + return false; } bool type_may_have_method(Type *type) diff --git a/test/test_suite/cast/implicit_void_ptr_deep.c3 b/test/test_suite/cast/implicit_void_ptr_deep.c3 new file mode 100644 index 000000000..46b829c1f --- /dev/null +++ b/test/test_suite/cast/implicit_void_ptr_deep.c3 @@ -0,0 +1,7 @@ +fn void main() +{ + int** y; + int* z; + void** x = y; + void** z2 = z; // #error: Implicitly casting +} \ No newline at end of file