mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
Further casting cleanup.
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
7
test/test_suite/cast/implicit_void_ptr_deep.c3
Normal file
7
test/test_suite/cast/implicit_void_ptr_deep.c3
Normal file
@@ -0,0 +1,7 @@
|
||||
fn void main()
|
||||
{
|
||||
int** y;
|
||||
int* z;
|
||||
void** x = y;
|
||||
void** z2 = z; // #error: Implicitly casting
|
||||
}
|
||||
Reference in New Issue
Block a user