mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-30 11:20:08 +00:00
Add #no_broadcast procedure parameter to disallow automatic array programming broadcasting on procedure arguments
This commit is contained in:
+48
-28
@@ -623,7 +623,7 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
|
||||
|
||||
#define MAXIMUM_TYPE_DISTANCE 10
|
||||
|
||||
gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
|
||||
gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming) {
|
||||
if (c == nullptr) {
|
||||
GB_ASSERT(operand->mode == Addressing_Value);
|
||||
GB_ASSERT(is_type_typed(operand->type));
|
||||
@@ -832,7 +832,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
|
||||
if (dst->Union.variants.count == 1) {
|
||||
Type *vt = dst->Union.variants[0];
|
||||
i64 score = check_distance_between_types(c, operand, vt);
|
||||
i64 score = check_distance_between_types(c, operand, vt, allow_array_programming);
|
||||
if (score >= 0) {
|
||||
return score+2;
|
||||
}
|
||||
@@ -840,7 +840,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
i64 prev_lowest_score = -1;
|
||||
i64 lowest_score = -1;
|
||||
for (Type *vt : dst->Union.variants) {
|
||||
i64 score = check_distance_between_types(c, operand, vt);
|
||||
i64 score = check_distance_between_types(c, operand, vt, allow_array_programming);
|
||||
if (score >= 0) {
|
||||
if (lowest_score < 0) {
|
||||
lowest_score = score;
|
||||
@@ -863,14 +863,14 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
}
|
||||
|
||||
if (is_type_relative_pointer(dst)) {
|
||||
i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type);
|
||||
i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type, allow_array_programming);
|
||||
if (score >= 0) {
|
||||
return score+2;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_relative_multi_pointer(dst)) {
|
||||
i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type);
|
||||
i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type, allow_array_programming);
|
||||
if (score >= 0) {
|
||||
return score+2;
|
||||
}
|
||||
@@ -896,19 +896,21 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_array(dst)) {
|
||||
Type *elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, elem);
|
||||
if (distance >= 0) {
|
||||
return distance + 6;
|
||||
if (allow_array_programming) {
|
||||
if (is_type_array(dst)) {
|
||||
Type *elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, elem, allow_array_programming);
|
||||
if (distance >= 0) {
|
||||
return distance + 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_simd_vector(dst)) {
|
||||
Type *dst_elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, dst_elem);
|
||||
if (distance >= 0) {
|
||||
return distance + 6;
|
||||
if (is_type_simd_vector(dst)) {
|
||||
Type *dst_elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming);
|
||||
if (distance >= 0) {
|
||||
return distance + 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -918,7 +920,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
}
|
||||
if (dst->Matrix.row_count == dst->Matrix.column_count) {
|
||||
Type *dst_elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, dst_elem);
|
||||
i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming);
|
||||
if (distance >= 0) {
|
||||
return distance + 7;
|
||||
}
|
||||
@@ -966,9 +968,9 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) {
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false) {
|
||||
gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) {
|
||||
i64 score = 0;
|
||||
i64 distance = check_distance_between_types(c, operand, type);
|
||||
i64 distance = check_distance_between_types(c, operand, type, allow_array_programming);
|
||||
bool ok = distance >= 0;
|
||||
if (ok) {
|
||||
score = assign_score_function(distance, is_variadic);
|
||||
@@ -978,9 +980,9 @@ gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *o
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) {
|
||||
gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming=true) {
|
||||
i64 score = 0;
|
||||
return check_is_assignable_to_with_score(c, operand, type, &score);
|
||||
return check_is_assignable_to_with_score(c, operand, type, &score, /*is_variadic*/false, allow_array_programming);
|
||||
}
|
||||
|
||||
gb_internal bool internal_check_is_assignable_to(Type *src, Type *dst) {
|
||||
@@ -3142,6 +3144,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (is_type_array(dst)) {
|
||||
Type *elem = base_array_type(dst);
|
||||
if (check_is_castable_to(c, operand, elem)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
|
||||
if (src->SimdVector.count != dst->SimdVector.count) {
|
||||
return false;
|
||||
@@ -5853,15 +5863,20 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
}
|
||||
}
|
||||
|
||||
auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 {
|
||||
auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error, bool allow_array_programming) -> i64 {
|
||||
i64 s = 0;
|
||||
if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic)) {
|
||||
if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic, allow_array_programming)) {
|
||||
bool ok = false;
|
||||
if (e && e->flags & EntityFlag_AnyInt) {
|
||||
if (e && (e->flags & EntityFlag_AnyInt)) {
|
||||
if (is_type_integer(param_type)) {
|
||||
ok = check_is_castable_to(c, o, param_type);
|
||||
}
|
||||
}
|
||||
if (!allow_array_programming && check_is_assignable_to_with_score(c, o, param_type, nullptr, param_is_variadic, !allow_array_programming)) {
|
||||
if (show_error) {
|
||||
error(o->expr, "'#no_broadcast' disallows automatic broadcasting a value across all elements of an array-like type in a procedure argument");
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
s = assign_score_function(MAXIMUM_TYPE_DISTANCE);
|
||||
} else {
|
||||
@@ -5878,7 +5893,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
}
|
||||
err = CallArgumentError_WrongTypes;
|
||||
}
|
||||
|
||||
} else if (show_error) {
|
||||
check_assignment(c, o, param_type, str_lit("procedure argument"));
|
||||
}
|
||||
@@ -5963,12 +5977,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
if (param_is_variadic) {
|
||||
continue;
|
||||
}
|
||||
score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error);
|
||||
bool allow_array_programming = !(e && (e->flags & EntityFlag_NoBroadcast));
|
||||
score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error, allow_array_programming);
|
||||
}
|
||||
}
|
||||
|
||||
if (variadic) {
|
||||
Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type;
|
||||
Entity *var_entity = pt->params->Tuple.variables[pt->variadic_index];
|
||||
Type *slice = var_entity->type;
|
||||
GB_ASSERT(is_type_slice(slice));
|
||||
Type *elem = base_type(slice)->Slice.elem;
|
||||
Type *t = elem;
|
||||
@@ -5994,7 +6010,8 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
return CallArgumentError_MultipleVariadicExpand;
|
||||
}
|
||||
}
|
||||
score += eval_param_and_score(c, o, t, err, true, nullptr, show_error);
|
||||
bool allow_array_programming = !(var_entity && (var_entity->flags & EntityFlag_NoBroadcast));
|
||||
score += eval_param_and_score(c, o, t, err, true, nullptr, show_error, allow_array_programming);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11148,6 +11165,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
|
||||
if (f->flags&FieldFlag_any_int) {
|
||||
str = gb_string_appendc(str, "#any_int ");
|
||||
}
|
||||
if (f->flags&FieldFlag_no_broadcast) {
|
||||
str = gb_string_appendc(str, "#no_broadcast ");
|
||||
}
|
||||
if (f->flags&FieldFlag_const) {
|
||||
str = gb_string_appendc(str, "#const ");
|
||||
}
|
||||
|
||||
+15
-1
@@ -1869,6 +1869,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
error(name, "'#any_int' can only be applied to variable fields");
|
||||
p->flags &= ~FieldFlag_any_int;
|
||||
}
|
||||
if (p->flags&FieldFlag_no_broadcast) {
|
||||
error(name, "'#no_broadcast' can only be applied to variable fields");
|
||||
p->flags &= ~FieldFlag_no_broadcast;
|
||||
}
|
||||
if (p->flags&FieldFlag_by_ptr) {
|
||||
error(name, "'#by_ptr' can only be applied to variable fields");
|
||||
p->flags &= ~FieldFlag_by_ptr;
|
||||
@@ -1926,7 +1930,13 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) {
|
||||
|
||||
bool allow_array_programming = true;
|
||||
if (p->flags&FieldFlag_no_broadcast) {
|
||||
allow_array_programming = false;
|
||||
}
|
||||
|
||||
if (type != t_invalid && !check_is_assignable_to(ctx, &op, type, allow_array_programming)) {
|
||||
bool ok = true;
|
||||
if (p->flags&FieldFlag_any_int) {
|
||||
if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
|
||||
@@ -2002,6 +2012,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
if (p->flags&FieldFlag_no_alias) {
|
||||
param->flags |= EntityFlag_NoAlias;
|
||||
}
|
||||
if (p->flags&FieldFlag_no_broadcast) {
|
||||
param->flags |= EntityFlag_NoBroadcast;
|
||||
}
|
||||
|
||||
if (p->flags&FieldFlag_any_int) {
|
||||
if (!is_type_integer(param->type) && !is_type_enum(param->type)) {
|
||||
gbString str = type_to_string(param->type);
|
||||
|
||||
@@ -61,6 +61,7 @@ enum EntityFlag : u64 {
|
||||
|
||||
EntityFlag_CVarArg = 1ull<<22,
|
||||
|
||||
EntityFlag_NoBroadcast = 1ull<<23,
|
||||
EntityFlag_AnyInt = 1ull<<24,
|
||||
|
||||
EntityFlag_Disabled = 1ull<<25,
|
||||
|
||||
+9
-8
@@ -3898,14 +3898,15 @@ struct ParseFieldPrefixMapping {
|
||||
FieldFlag flag;
|
||||
};
|
||||
|
||||
gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = {
|
||||
{str_lit("using"), Token_using, FieldFlag_using},
|
||||
{str_lit("no_alias"), Token_Hash, FieldFlag_no_alias},
|
||||
{str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg},
|
||||
{str_lit("const"), Token_Hash, FieldFlag_const},
|
||||
{str_lit("any_int"), Token_Hash, FieldFlag_any_int},
|
||||
{str_lit("subtype"), Token_Hash, FieldFlag_subtype},
|
||||
{str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr},
|
||||
gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = {
|
||||
{str_lit("using"), Token_using, FieldFlag_using},
|
||||
{str_lit("no_alias"), Token_Hash, FieldFlag_no_alias},
|
||||
{str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg},
|
||||
{str_lit("const"), Token_Hash, FieldFlag_const},
|
||||
{str_lit("any_int"), Token_Hash, FieldFlag_any_int},
|
||||
{str_lit("subtype"), Token_Hash, FieldFlag_subtype},
|
||||
{str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr},
|
||||
{str_lit("no_broadcast"), Token_Hash, FieldFlag_no_broadcast},
|
||||
};
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -326,6 +326,7 @@ enum FieldFlag : u32 {
|
||||
FieldFlag_any_int = 1<<6,
|
||||
FieldFlag_subtype = 1<<7,
|
||||
FieldFlag_by_ptr = 1<<8,
|
||||
FieldFlag_no_broadcast = 1<<9, // disallow array programming
|
||||
|
||||
// Internal use by the parser only
|
||||
FieldFlag_Tags = 1<<10,
|
||||
@@ -336,7 +337,7 @@ enum FieldFlag : u32 {
|
||||
FieldFlag_Invalid = 1u<<31,
|
||||
|
||||
// Parameter List Restrictions
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr,
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast,
|
||||
FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user