aboutsummaryrefslogtreecommitdiff
path: root/src/check_builtin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/check_builtin.cpp')
-rw-r--r--src/check_builtin.cpp413
1 files changed, 384 insertions, 29 deletions
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index a08382c9a..1b3e6912c 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -13,6 +13,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
nullptr, // BuiltinProc__type_simple_boolean_begin
is_type_boolean,
+ is_type_bit_field,
is_type_integer,
is_type_rune,
is_type_float,
@@ -20,8 +21,11 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
is_type_quaternion,
is_type_string,
is_type_string16,
+ is_type_cstring,
+ is_type_cstring16,
is_type_typeid,
is_type_any,
+
is_type_endian_platform,
is_type_endian_little,
is_type_endian_big,
@@ -32,8 +36,8 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
is_type_indexable,
is_type_sliceable,
is_type_comparable,
- is_type_simple_compare,
- is_type_nearly_simple_compare,
+ is_type_simple_compare, // easily compared using memcmp
+ is_type_nearly_simple_compare, // easily compared using memcmp (including floats)
is_type_dereferenceable,
is_type_valid_for_keys,
is_type_valid_for_matrix_elems,
@@ -45,16 +49,15 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
is_type_enumerated_array,
is_type_slice,
is_type_dynamic_array,
-
is_type_map,
is_type_struct,
is_type_union,
is_type_enum,
is_type_proc,
is_type_bit_set,
- is_type_bit_field,
is_type_simd_vector,
is_type_matrix,
+ is_type_raw_union,
is_type_polymorphic_record_specialized,
is_type_polymorphic_record_unspecialized,
@@ -210,7 +213,7 @@ gb_internal ObjcMsgKind get_objc_proc_kind(Type *return_type) {
return ObjcMsg_normal;
}
-gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
+void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
ObjcMsgKind kind = get_objc_proc_kind(return_type);
Scope *scope = create_scope(c->info, nullptr);
@@ -248,6 +251,12 @@ gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_t
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
+
+ Slice<Ast *> args = call->CallExpr.args;
+ if (args.count > 0 && args[0]->tav.objc_super_target) {
+ try_to_add_package_dependency(c, "runtime", "objc_msgSendSuper2");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSendSuper2_stret");
+ }
}
gb_internal bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) {
@@ -354,7 +363,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
}
isize const arg_offset = 1;
- auto param_types = slice_make<Type *>(permanent_allocator(), ce->args.count-arg_offset);
+ auto param_types = permanent_slice_make<Type *>(ce->args.count-arg_offset);
param_types[0] = t_objc_id;
param_types[1] = sel_type;
@@ -462,12 +471,12 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
{
// NOTE(harold): The last argument specified in the call is the handler proc,
// any other arguments before it are capture by-copy arguments.
- auto param_operands = slice_make<Operand>(permanent_allocator(), ce->args.count);
+ auto param_operands = permanent_slice_make<Operand>(ce->args.count);
isize capture_arg_count = ce->args.count - 1;
- // NOTE(harold): The first parameter is already checked at check_builtin_procedure().
- // Checking again would invalidate the Entity -> Value map for direct parameters if it's the handler proc.
+ // NOTE(harold): The first argument is already checked at check_builtin_procedure().
+ // Checking again would invalidate the Entity -> Value map for direct arguments if it's the handler proc.
param_operands[0] = *operand;
for (isize i = 0; i < ce->args.count-1; i++) {
@@ -680,6 +689,52 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
operand->mode = Addressing_Value;
return true;
} break;
+
+ case BuiltinProc_objc_super:
+ {
+ // Must be a pointer to an Objective-C object.
+ Type *objc_obj = operand->type;
+ if (!is_type_objc_ptr_to_object(objc_obj)) {
+ gbString e = expr_to_string(operand->expr);
+ gbString t = type_to_string(objc_obj);
+ error(operand->expr, "'%.*s' expected a pointer to an Objective-C object, but got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ if (operand->mode != Addressing_Value && operand->mode != Addressing_Variable) {
+ gbString e = expr_to_string(operand->expr);
+ gbString t = type_to_string(operand->type);
+ error(operand->expr, "'%.*s' expression '%s', of type %s, must be a value or variable.", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ Type *obj_type = type_deref(objc_obj);
+ GB_ASSERT(obj_type->kind == Type_Named);
+
+ // NOTE(harold) Track original type before transforming it to the superclass.
+ // This is needed because objc_msgSendSuper2 must start its search on the subclass, not the superclass.
+ call->tav.objc_super_target = obj_type;
+
+ // The superclass type must be known at compile time. We require this so that the selector method expressions
+ // methods are resolved to the superclass's methods instead of the subclass's.
+ Type *superclass = obj_type->Named.type_name->TypeName.objc_superclass;
+ if (superclass == nullptr) {
+ gbString t = type_to_string(obj_type);
+ error(operand->expr, "'%.*s' target object '%.*s' does not have an Objective-C superclass. One must be set via the @(objc_superclass) attribute", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ GB_ASSERT(superclass->Named.type_name->TypeName.objc_class_name.len > 0);
+
+ operand->type = alloc_type_pointer(superclass);
+ return true;
+
+ } break;
}
}
@@ -2430,6 +2485,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_min:
case BuiltinProc_max:
case BuiltinProc_type_is_subtype_of:
+ case BuiltinProc_type_is_superset_of:
case BuiltinProc_objc_send:
case BuiltinProc_objc_find_selector:
case BuiltinProc_objc_find_class:
@@ -2515,6 +2571,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_objc_register_class:
case BuiltinProc_objc_ivar_get:
case BuiltinProc_objc_block:
+ case BuiltinProc_objc_super:
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
case BuiltinProc___entry_point:
@@ -3554,7 +3611,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
case BuiltinProc_compress_values: {
- Operand *ops = gb_alloc_array(temporary_allocator(), Operand, ce->args.count);
+ Operand *ops = temporary_alloc_array<Operand>(ce->args.count);
isize value_count = 0;
@@ -3685,9 +3742,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
operand->mode = Addressing_Value;
} else {
Type *st = alloc_type_struct_complete();
- st->Struct.fields = slice_make<Entity *>(permanent_allocator(), value_count);
- st->Struct.tags = gb_alloc_array(permanent_allocator(), String, value_count);
- st->Struct.offsets = gb_alloc_array(permanent_allocator(), i64, value_count);
+ st->Struct.fields = permanent_slice_make<Entity *>(value_count);
+ st->Struct.tags = permanent_alloc_array<String>(value_count);
+ st->Struct.offsets = permanent_alloc_array<i64>(value_count);
Scope *scope = create_scope(c->info, nullptr);
@@ -4387,7 +4444,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
elem = alloc_type_struct();
elem->Struct.scope = s;
elem->Struct.fields = slice_from_array(fields);
- elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count);
+ elem->Struct.tags = permanent_alloc_array<String>(fields.count);
elem->Struct.node = dummy_node_struct;
type_set_offsets(elem);
wait_signal_set(&elem->Struct.fields_wait_signal);
@@ -4419,7 +4476,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
gb_string_free(s);
return false;
}
- auto types = slice_make<Type *>(permanent_allocator(), t->Struct.fields.count-1);
+ auto types = permanent_slice_make<Type *>(t->Struct.fields.count-1);
for_array(i, types) {
Entity *f = t->Struct.fields[i];
GB_ASSERT(f->type->kind == Type_MultiPointer);
@@ -4712,6 +4769,42 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
+ case BuiltinProc_constant_floor:
+ case BuiltinProc_constant_trunc:
+ case BuiltinProc_constant_ceil:
+ case BuiltinProc_constant_round:
+ {
+ Operand o = {};
+ check_expr(c, &o, ce->args[0]);
+
+ if (!is_type_integer_or_float(o.type) && (o.mode != Addressing_Constant)) {
+ error(ce->args[0], "Expected a constant number for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ operand->mode = Addressing_Constant;
+ operand->type = o.type;
+
+ ExactValue value = o.value;
+ if (value.kind == ExactValue_Integer) {
+ // do nothing
+ } else if (value.kind == ExactValue_Float) {
+ f64 f = value.value_float;
+ switch (id) {
+ case BuiltinProc_constant_floor: f = floor(f); break;
+ case BuiltinProc_constant_trunc: f = trunc(f); break;
+ case BuiltinProc_constant_ceil: f = ceil(f); break;
+ case BuiltinProc_constant_round: f = round(f); break;
+ default:
+ GB_PANIC("Unhandled built-in: %.*s", LIT(builtin_name));
+ break;
+ }
+ value = exact_value_float(f);
+ }
+
+ operand->value = value;
+ break;
+ }
+
case BuiltinProc_soa_struct: {
Operand x = {};
Operand y = {};
@@ -4755,8 +4848,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (is_type_array(elem)) {
Type *old_array = base_type(elem);
soa_struct = alloc_type_struct();
- soa_struct->Struct.fields = slice_make<Entity *>(heap_allocator(), cast(isize)old_array->Array.count);
- soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, cast(isize)old_array->Array.count);
+ soa_struct->Struct.fields = permanent_slice_make<Entity *>(cast(isize)old_array->Array.count);
+ soa_struct->Struct.tags = permanent_alloc_array<String>(cast(isize)old_array->Array.count);
soa_struct->Struct.node = operand->expr;
soa_struct->Struct.soa_kind = StructSoa_Fixed;
soa_struct->Struct.soa_elem = elem;
@@ -4788,8 +4881,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
Type *old_struct = base_type(elem);
soa_struct = alloc_type_struct();
- soa_struct->Struct.fields = slice_make<Entity *>(heap_allocator(), old_struct->Struct.fields.count);
- soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, old_struct->Struct.fields.count);
+ soa_struct->Struct.fields = permanent_slice_make<Entity *>(old_struct->Struct.fields.count);
+ soa_struct->Struct.tags = permanent_alloc_array<String>(old_struct->Struct.fields.count);
soa_struct->Struct.node = operand->expr;
soa_struct->Struct.soa_kind = StructSoa_Fixed;
soa_struct->Struct.soa_elem = elem;
@@ -4829,6 +4922,138 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
+ case BuiltinProc_concatenate: {
+ Operand lhs = {};
+
+ check_expr_with_type_hint(c, &lhs, ce->args[0], type_hint);
+ if (lhs.mode == Addressing_Invalid) {
+ return false;
+ }
+ if (lhs.mode != Addressing_Constant) {
+ error(lhs.expr, "'%.*s' expects a constant array or slice", LIT(builtin_name));
+ return false;
+ }
+ operand->type = lhs.type;
+ operand->mode = Addressing_Value;
+
+ if (!is_type_slice(lhs.type) && !is_type_array(lhs.type)) {
+ gbString a = type_to_string(lhs.type);
+ error(lhs.expr, "'%.*s' expects a constant array or slice, got %s", LIT(builtin_name), a);
+ gb_string_free(a);
+ return false;
+ }
+ if (lhs.value.kind != ExactValue_Compound) {
+ gbString a = exact_value_to_string(lhs.value);
+ error(lhs.expr, "Expected a compound literal value for '%.*s', got '%s'", LIT(builtin_name), a);
+ gb_string_free(a);
+ return false;
+ }
+
+ ast_node(lhs_cl, CompoundLit, lhs.value.value_compound);
+
+ for (Ast *elem : lhs_cl->elems) {
+ if (elem->kind == Ast_FieldValue) {
+ error(elem, "'%.*s' does not allow the use of 'field = value' to be concatenated together", LIT(builtin_name));
+ return false;
+ }
+ }
+
+ Type *elem_type = base_any_array_type(lhs.type);
+
+ for (isize i = 1; i < ce->args.count; i++) {
+ Operand extra = {};
+ if (is_type_slice(lhs.type)) {
+ check_expr_with_type_hint(c, &extra, ce->args[i], lhs.type);
+ } else {
+ check_expr(c, &extra, ce->args[i]);
+ }
+ if (extra.mode == Addressing_Invalid) {
+ return false;
+ }
+ if (extra.mode != Addressing_Constant) {
+ error(extra.expr, "'%.*s' expects a constant array or slice", LIT(builtin_name));
+ return false;
+ }
+
+ if (is_type_slice(lhs.type)) {
+ if (!are_types_identical(lhs.type, extra.type)) {
+ gbString a = type_to_string(lhs.type);
+ gbString b = type_to_string(extra.type);
+ error(extra.expr, "'%.*s' expects constant values of the same slice type, got '%s' vs '%s'", LIT(builtin_name), a, b);
+ gb_string_free(b);
+ gb_string_free(a);
+ return false;
+ }
+ } else if (is_type_array(lhs.type)) {
+ if (!is_type_array(extra.type)) {
+ gbString a = type_to_string(extra.type);
+ error(extra.expr, "'%.*s' expects a constant array or slice, got %s", LIT(builtin_name), a);
+ gb_string_free(a);
+ return false;
+ }
+ Type *extra_elem_type = base_array_type(extra.type);
+ if (!are_types_identical(elem_type, extra_elem_type)) {
+ gbString a = type_to_string(elem_type);
+ gbString b = type_to_string(extra_elem_type);
+ error(extra.expr, "'%.*s' expects constant values of the same element-type, got '%s' vs '%s'", LIT(builtin_name), a, b);
+ gb_string_free(b);
+ gb_string_free(a);
+ return false;
+ }
+ } else {
+ GB_PANIC("Unhandled type: %s", type_to_string(lhs.type));
+ }
+
+ if (extra.value.kind != ExactValue_Compound) {
+ gbString a = exact_value_to_string(extra.value);
+ error(extra.expr, "Expected a compound literal value for '%.*s', got '%s'", LIT(builtin_name), a);
+ gb_string_free(a);
+ return false;
+ }
+
+ ast_node(extra_cl, CompoundLit, extra.value.value_compound);
+
+
+ for (Ast *elem : extra_cl->elems) {
+ if (elem->kind == Ast_FieldValue) {
+ error(elem, "'%.*s' does not allow the use of 'field = value' to be concatenated together", LIT(builtin_name));
+ return false;
+ }
+ }
+ }
+
+ isize count_needed = 0;
+
+ for (Ast *arg : ce->args) {
+ ExactValue value = arg->tav.value;
+ GB_ASSERT(value.kind == ExactValue_Compound);
+ ast_node(cl, CompoundLit, value.value_compound);
+ count_needed += cl->elems.count;
+ }
+
+ Array<Ast *> new_elems = {};
+ array_init(&new_elems, permanent_allocator(), 0, count_needed);
+
+ for (Ast *arg : ce->args) {
+ ExactValue value = arg->tav.value;
+ GB_ASSERT(value.kind == ExactValue_Compound);
+ ast_node(cl, CompoundLit, value.value_compound);
+ array_add_elems(&new_elems, cl->elems.data, cl->elems.count);
+ }
+
+ Ast *new_compound_lit = ast_compound_lit(lhs.expr->file(), nullptr, new_elems, ast_token(lhs.expr), ast_end_token(ce->args[ce->args.count-1]));
+
+ operand->mode = Addressing_Constant;
+ operand->value = exact_value_compound(new_compound_lit);
+
+ if (is_type_slice(lhs.type)) {
+ operand->type = lhs.type;
+ } else {
+ operand->type = alloc_type_array(elem_type, new_elems.count);
+ }
+ break;
+ }
+
case BuiltinProc_alloca:
{
Operand sz = {};
@@ -6181,7 +6406,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
Type *new_type = alloc_type_union();
- auto variants = slice_make<Type *>(permanent_allocator(), bt->Union.variants.count);
+ auto variants = permanent_slice_make<Type *>(bt->Union.variants.count);
for_array(i, bt->Union.variants) {
variants[i] = alloc_type_pointer(bt->Union.variants[i]);
}
@@ -6364,6 +6589,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_type_is_boolean:
+ case BuiltinProc_type_is_bit_field:
case BuiltinProc_type_is_integer:
case BuiltinProc_type_is_rune:
case BuiltinProc_type_is_float:
@@ -6371,6 +6597,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_type_is_quaternion:
case BuiltinProc_type_is_string:
case BuiltinProc_type_is_string16:
+ case BuiltinProc_type_is_cstring:
+ case BuiltinProc_type_is_cstring16:
case BuiltinProc_type_is_typeid:
case BuiltinProc_type_is_any:
case BuiltinProc_type_is_endian_platform:
@@ -6383,8 +6611,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_type_is_indexable:
case BuiltinProc_type_is_sliceable:
case BuiltinProc_type_is_comparable:
- case BuiltinProc_type_is_simple_compare:
- case BuiltinProc_type_is_nearly_simple_compare:
+ case BuiltinProc_type_is_simple_compare: // easily compared using memcmp
+ case BuiltinProc_type_is_nearly_simple_compare: // easily compared using memcmp (including floats)
case BuiltinProc_type_is_dereferenceable:
case BuiltinProc_type_is_valid_map_key:
case BuiltinProc_type_is_valid_matrix_elements:
@@ -6401,9 +6629,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_type_is_enum:
case BuiltinProc_type_is_proc:
case BuiltinProc_type_is_bit_set:
- case BuiltinProc_type_is_bit_field:
case BuiltinProc_type_is_simd_vector:
case BuiltinProc_type_is_matrix:
+ case BuiltinProc_type_is_raw_union:
case BuiltinProc_type_is_specialized_polymorphic_record:
case BuiltinProc_type_is_unspecialized_polymorphic_record:
case BuiltinProc_type_has_nil:
@@ -6466,7 +6694,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_expr(c, &x, ce->args[1]);
if (!is_type_string(x.type) || x.mode != Addressing_Constant || x.value.kind != ExactValue_String) {
- error(ce->args[1], "Expected a const string for field argument");
+ error(ce->args[1], "Expected a constant string for field argument");
return false;
}
@@ -6546,7 +6774,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_expr(c, &x, ce->args[1]);
if (!is_type_string(x.type) || x.mode != Addressing_Constant || x.value.kind != ExactValue_String) {
- error(ce->args[1], "Expected a const string for field argument");
+ error(ce->args[1], "Expected a constant string for field argument");
return false;
}
@@ -6683,9 +6911,13 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// NOTE(jakubtomsu): forces calculation of variant_block_size
type_size_of(u);
- // NOTE(Jeroen): A tag offset of zero is perfectly fine if all members of the union are empty structs.
- // What matters is that the tag size is > 0.
- GB_ASSERT(u->Union.tag_size > 0);
+ if (u->Union.tag_size == 0) {
+ GB_ASSERT(is_type_union_maybe_pointer(u));
+ } else {
+ // NOTE(Jeroen): A tag offset of zero is perfectly fine if all members of the union are empty structs.
+ // What matters is that the tag size is > 0.
+ GB_ASSERT(u->Union.tag_size > 0);
+ }
operand->mode = Addressing_Constant;
operand->type = t_untyped_integer;
@@ -7166,6 +7398,129 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
operand->type = t_untyped_bool;
} break;
+ case BuiltinProc_type_is_superset_of:
+ {
+ Operand op_super = {};
+ Operand op_sub = {};
+
+ check_expr_or_type(c, &op_super, ce->args[0]);
+ if (op_super.mode != Addressing_Type) {
+ gbString e = expr_to_string(op_super.expr);
+ error(op_super.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
+ gb_string_free(e);
+ return false;
+ }
+ check_expr_or_type(c, &op_sub, ce->args[1]);
+ if (op_sub.mode != Addressing_Type) {
+ gbString e = expr_to_string(op_sub.expr);
+ error(op_sub.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_bool;
+
+ Type *super = op_super.type;
+ Type *sub = op_sub.type;
+ if (are_types_identical(super, sub)) {
+ operand->value = exact_value_bool(true);
+ return true;
+ }
+
+ super = base_type(super);
+ sub = base_type(sub);
+ if (are_types_identical(super, sub)) {
+ operand->value = exact_value_bool(true);
+ return true;
+ }
+
+ if (super->kind != sub->kind) {
+ gbString a = type_to_string(op_super.type);
+ gbString b = type_to_string(op_sub.type);
+ error(op_super.expr, "'%.*s' expects types of the same kind, got %s vs %s", LIT(builtin_name), a, b);
+ gb_string_free(b);
+ gb_string_free(a);
+ return false;
+ }
+
+ if (super->kind == Type_Enum) {
+ if (sub->Enum.fields.count > super->Enum.fields.count) {
+ operand->value = exact_value_bool(false);
+ return true;
+ }
+
+
+ Type *base_super = base_enum_type(super);
+ Type *base_sub = base_enum_type(sub);
+ if (base_super == base_sub && base_super == nullptr) {
+ // okay
+ } else if (!are_types_identical(base_type(base_super), base_type(base_sub))) {
+ operand->value = exact_value_bool(false);
+ return true;
+ }
+
+ for (Entity *f_sub : sub->Enum.fields) {
+ bool found = false;
+
+ if (f_sub->kind != Entity_Constant) {
+ continue;
+ }
+
+ for (Entity *f_super : super->Enum.fields) {
+ if (f_super->kind != Entity_Constant) {
+ continue;
+ }
+
+ if (f_sub->token.string == f_super->token.string) {
+ if (compare_exact_values(Token_CmpEq, f_sub->Constant.value, f_super->Constant.value)) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ operand->value = exact_value_bool(false);
+ return true;
+ }
+ }
+
+ operand->value = exact_value_bool(true);
+ return true;
+
+ } else if (super->kind == Type_Union) {
+ if (sub->Union.variants.count > super->Union.variants.count) {
+ operand->value = exact_value_bool(false);
+ return true;
+ }
+ if (sub->Union.kind != super->Union.kind) {
+ operand->value = exact_value_bool(false);
+ return true;
+ }
+
+ for_array(i, sub->Union.variants) {
+ Type *t_sub = sub->Union.variants[i];
+ Type *t_super = super->Union.variants[i];
+ if (!are_types_identical(t_sub, t_super)) {
+ operand->value = exact_value_bool(false);
+ return true;
+ }
+ }
+
+ operand->value = exact_value_bool(true);
+ return true;
+
+ }
+ gbString a = type_to_string(op_super.type);
+ gbString b = type_to_string(op_sub.type);
+ error(op_super.expr, "'%.*s' expects types of the same kind and either an enum or union, got %s vs %s", LIT(builtin_name), a, b);
+ gb_string_free(b);
+ gb_string_free(a);
+ return false;
+ }
+
+
case BuiltinProc_type_field_index_of:
{
Operand op = {};
@@ -7179,7 +7534,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_expr(c, &x, ce->args[1]);
if (!is_type_string(x.type) || x.mode != Addressing_Constant || x.value.kind != ExactValue_String) {
- error(ce->args[1], "Expected a const string for field argument");
+ error(ce->args[1], "Expected a constant string for field argument");
return false;
}