diff options
| author | gingerBill <bill@gingerbill.org> | 2021-04-23 10:01:52 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2021-04-23 10:01:52 +0100 |
| commit | c9b82a21e9a52122a226ecc05c46a29dc8f57dac (patch) | |
| tree | da4e6ada4d3706843e487d0f42592e4d9a276e0b /src/check_builtin.cpp | |
| parent | bd31a99bf7ae06fdc6f2eda1cbfd2d99e0841c31 (diff) | |
Move `check_builtin_procedure` to check_builtin.cpp
Diffstat (limited to 'src/check_builtin.cpp')
| -rw-r--r-- | src/check_builtin.cpp | 2802 |
1 files changed, 2802 insertions, 0 deletions
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp new file mode 100644 index 000000000..5f4411b90 --- /dev/null +++ b/src/check_builtin.cpp @@ -0,0 +1,2802 @@ +typedef bool (BuiltinTypeIsProc)(Type *t); + +BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end - BuiltinProc__type_simple_boolean_begin] = { + nullptr, // BuiltinProc__type_simple_boolean_begin + + is_type_boolean, + is_type_integer, + is_type_rune, + is_type_float, + is_type_complex, + is_type_quaternion, + is_type_string, + is_type_typeid, + is_type_any, + is_type_endian_little, + is_type_endian_big, + is_type_unsigned, + is_type_numeric, + is_type_ordered, + is_type_ordered_numeric, + is_type_indexable, + is_type_sliceable, + is_type_comparable, + is_type_simple_compare, + is_type_dereferenceable, + is_type_valid_for_keys, + + is_type_named, + is_type_pointer, + is_type_array, + 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_simd_vector, + + is_type_polymorphic_record_specialized, + is_type_polymorphic_record_unspecialized, + + type_has_nil, +}; + + + +bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { + ast_node(ce, CallExpr, call); + if (ce->inlining != ProcInlining_none) { + error(call, "Inlining operators are not allowed on built-in procedures"); + } + + BuiltinProc *bp = &builtin_procs[id]; + { + char const *err = nullptr; + if (ce->args.count < bp->arg_count) { + err = "Too few"; + } else if (ce->args.count > bp->arg_count && !bp->variadic) { + err = "Too many"; + } + + if (err != nullptr) { + gbString expr = expr_to_string(ce->proc); + error(ce->close, "%s arguments for '%s', expected %td, got %td", + err, expr, + bp->arg_count, ce->args.count); + gb_string_free(expr); + return false; + } + } + + switch (id) { + case BuiltinProc_size_of: + case BuiltinProc_align_of: + case BuiltinProc_offset_of: + case BuiltinProc_type_info_of: + case BuiltinProc_typeid_of: + case BuiltinProc_len: + case BuiltinProc_min: + case BuiltinProc_max: + // NOTE(bill): The first arg may be a Type, this will be checked case by case + break; + + case BuiltinProc_DIRECTIVE: { + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name; + if (name == "defined") { + break; + } + if (name == "config") { + break; + } + /*fallthrough*/ + } + default: + if (BuiltinProc__type_begin < id && id < BuiltinProc__type_end) { + check_expr_or_type(c, operand, ce->args[0]); + } else if (ce->args.count > 0) { + check_multi_expr(c, operand, ce->args[0]); + } + break; + } + + String builtin_name = builtin_procs[id].name; + + + if (ce->args.count > 0) { + if (ce->args[0]->kind == Ast_FieldValue) { + if (id != BuiltinProc_soa_zip) { + error(call, "'field = value' calling is not allowed on built-in procedures"); + return false; + } + } + } + + switch (id) { + default: + GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name)); + break; + + case BuiltinProc_DIRECTIVE: { + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name; + if (name == "location") { + if (ce->args.count > 1) { + error(ce->args[0], "'#location' expects either 0 or 1 arguments, got %td", ce->args.count); + } + if (ce->args.count > 0) { + Ast *arg = ce->args[0]; + Entity *e = nullptr; + Operand o = {}; + if (arg->kind == Ast_Ident) { + e = check_ident(c, &o, arg, nullptr, nullptr, true); + } else if (arg->kind == Ast_SelectorExpr) { + e = check_selector(c, &o, arg, nullptr); + } + if (e == nullptr) { + error(ce->args[0], "'#location' expected a valid entity name"); + } + } + + operand->type = t_source_code_location; + operand->mode = Addressing_Value; + } else if (name == "load") { + if (ce->args.count != 1) { + error(ce->args[0], "'#load' expects 1 argument, got %td", ce->args.count); + return false; + } + + Ast *arg = ce->args[0]; + Operand o = {}; + check_expr(c, &o, arg); + if (o.mode != Addressing_Constant) { + error(arg, "'#load' expected a constant string argument"); + return false; + } + + if (!is_type_string(o.type)) { + gbString str = type_to_string(o.type); + error(arg, "'#load' expected a constant string, got %s", str); + gb_string_free(str); + return false; + } + + gbAllocator a = heap_allocator(); + + GB_ASSERT(o.value.kind == ExactValue_String); + String base_dir = dir_from_path(get_file_path_string(bd->token.pos.file_id)); + String original_string = o.value.value_string; + + + gbMutex *ignore_mutex = nullptr; + String path = {}; + bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path); + + char *c_str = alloc_cstring(a, path); + defer (gb_free(a, c_str)); + + + gbFile f = {}; + gbFileError file_err = gb_file_open(&f, c_str); + defer (gb_file_close(&f)); + + switch (file_err) { + default: + case gbFileError_Invalid: + error(ce->proc, "Failed to `#load` file: %s; invalid file or cannot be found", c_str); + return false; + case gbFileError_NotExists: + error(ce->proc, "Failed to `#load` file: %s; file cannot be found", c_str); + return false; + case gbFileError_Permission: + error(ce->proc, "Failed to `#load` file: %s; file permissions problem", c_str); + return false; + case gbFileError_None: + // Okay + break; + } + + String result = {}; + isize file_size = cast(isize)gb_file_size(&f); + if (file_size > 0) { + u8 *data = cast(u8 *)gb_alloc(a, file_size+1); + gb_file_read_at(&f, data, file_size, 0); + data[file_size] = '\0'; + result.text = data; + result.len = file_size; + } + + operand->type = t_u8_slice; + operand->mode = Addressing_Constant; + operand->value = exact_value_string(result); + + } else if (name == "assert") { + if (ce->args.count != 1) { + error(call, "'#assert' expects 1 argument, got %td", ce->args.count); + return false; + } + if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) { + gbString str = expr_to_string(ce->args[0]); + error(call, "'%s' is not a constant boolean", str); + gb_string_free(str); + return false; + } + if (!operand->value.value_bool) { + gbString arg = expr_to_string(ce->args[0]); + error(call, "Compile time assertion: %s", arg); + if (c->proc_name != "") { + gbString str = type_to_string(c->curr_proc_sig); + error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); + gb_string_free(str); + } + gb_string_free(arg); + } + + operand->type = t_untyped_bool; + operand->mode = Addressing_Constant; + } else if (name == "panic") { + if (ce->args.count != 1) { + error(call, "'#panic' expects 1 argument, got %td", ce->args.count); + return false; + } + if (!is_type_string(operand->type) && operand->mode != Addressing_Constant) { + gbString str = expr_to_string(ce->args[0]); + error(call, "'%s' is not a constant string", str); + gb_string_free(str); + return false; + } + error(call, "Compile time panic: %.*s", LIT(operand->value.value_string)); + if (c->proc_name != "") { + gbString str = type_to_string(c->curr_proc_sig); + error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); + gb_string_free(str); + } + operand->type = t_invalid; + operand->mode = Addressing_NoValue; + } else if (name == "defined") { + if (ce->args.count != 1) { + error(call, "'#defined' expects 1 argument, got %td", ce->args.count); + return false; + } + Ast *arg = unparen_expr(ce->args[0]); + if (arg == nullptr || (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr)) { + error(call, "'#defined' expects an identifier or selector expression, got %.*s", LIT(ast_strings[arg->kind])); + return false; + } + + if (c->curr_proc_decl == nullptr) { + error(call, "'#defined' is only allowed within a procedure, prefer the replacement '#config(NAME, default_value)'"); + return false; + } + + bool is_defined = check_identifier_exists(c->scope, arg); + operand->type = t_untyped_bool; + operand->mode = Addressing_Constant; + operand->value = exact_value_bool(false); + + } else if (name == "config") { + if (ce->args.count != 2) { + error(call, "'#config' expects 2 argument, got %td", ce->args.count); + return false; + } + Ast *arg = unparen_expr(ce->args[0]); + if (arg == nullptr || arg->kind != Ast_Ident) { + error(call, "'#config' expects an identifier, got %.*s", LIT(ast_strings[arg->kind])); + return false; + } + + Ast *def_arg = unparen_expr(ce->args[1]); + + Operand def = {}; + check_expr(c, &def, def_arg); + if (def.mode != Addressing_Constant) { + error(def_arg, "'#config' default value must be a constant"); + return false; + } + + String name = arg->Ident.token.string; + + + operand->type = def.type; + operand->mode = def.mode; + operand->value = def.value; + + Entity *found = scope_lookup_current(config_pkg->scope, name); + if (found != nullptr) { + if (found->kind != Entity_Constant) { + error(arg, "'#config' entity '%.*s' found but expected a constant", LIT(name)); + } else { + operand->type = found->type; + operand->mode = Addressing_Constant; + operand->value = found->Constant.value; + } + } + } else { + GB_PANIC("Unhandled #%.*s", LIT(name)); + } + + break; + } + + case BuiltinProc_len: + check_expr_or_type(c, operand, ce->args[0]); + if (operand->mode == Addressing_Invalid) { + return false; + } + /* fallthrough */ + + case BuiltinProc_cap: + { + // len :: proc(Type) -> int + // cap :: proc(Type) -> int + + Type *op_type = type_deref(operand->type); + Type *type = t_int; + AddressingMode mode = Addressing_Invalid; + ExactValue value = {}; + if (is_type_string(op_type) && id == BuiltinProc_len) { + if (operand->mode == Addressing_Constant) { + mode = Addressing_Constant; + String str = operand->value.value_string; + value = exact_value_i64(str.len); + type = t_untyped_integer; + } else { + mode = Addressing_Value; + if (is_type_cstring(op_type)) { + add_package_dependency(c, "runtime", "cstring_len"); + } + } + } else if (is_type_array(op_type)) { + Type *at = core_type(op_type); + mode = Addressing_Constant; + value = exact_value_i64(at->Array.count); + type = t_untyped_integer; + } else if (is_type_enumerated_array(op_type) && id == BuiltinProc_len) { + Type *at = core_type(op_type); + mode = Addressing_Constant; + value = exact_value_i64(at->EnumeratedArray.count); + type = t_untyped_integer; + } else if (is_type_slice(op_type) && id == BuiltinProc_len) { + mode = Addressing_Value; + } else if (is_type_dynamic_array(op_type)) { + mode = Addressing_Value; + } else if (is_type_map(op_type)) { + mode = Addressing_Value; + } else if (operand->mode == Addressing_Type && is_type_enum(op_type) && id == BuiltinProc_len) { + Type *bt = base_type(op_type); + mode = Addressing_Constant; + value = exact_value_i64(bt->Enum.fields.count); + type = t_untyped_integer; + } else if (is_type_struct(op_type)) { + Type *bt = base_type(op_type); + if (bt->Struct.soa_kind == StructSoa_Fixed) { + mode = Addressing_Constant; + value = exact_value_i64(bt->Struct.soa_count); + type = t_untyped_integer; + } else if ((bt->Struct.soa_kind == StructSoa_Slice && id == BuiltinProc_len) || + bt->Struct.soa_kind == StructSoa_Dynamic) { + mode = Addressing_Value; + } + } + if (operand->mode == Addressing_Type && mode != Addressing_Constant) { + mode = Addressing_Invalid; + } + + if (mode == Addressing_Invalid) { + gbString t = type_to_string(operand->type); + error(call, "'%.*s' is not supported for '%s'", LIT(builtin_name), t); + return false; + } + + operand->mode = mode; + operand->value = value; + operand->type = type; + + break; + } + + case BuiltinProc_size_of: { + // size_of :: proc(Type or expr) -> untyped int + Operand o = {}; + check_expr_or_type(c, &o, ce->args[0]); + if (o.mode == Addressing_Invalid) { + return false; + } + Type *t = o.type; + if (t == nullptr || t == t_invalid) { + error(ce->args[0], "Invalid argument for 'size_of'"); + return false; + } + t = default_type(t); + + operand->mode = Addressing_Constant; + operand->value = exact_value_i64(type_size_of(t)); + operand->type = t_untyped_integer; + + break; + } + + case BuiltinProc_align_of: { + // align_of :: proc(Type or expr) -> untyped int + Operand o = {}; + check_expr_or_type(c, &o, ce->args[0]); + if (o.mode == Addressing_Invalid) { + return false; + } + Type *t = o.type; + if (t == nullptr || t == t_invalid) { + error(ce->args[0], "Invalid argument for 'align_of'"); + return false; + } + t = default_type(t); + + operand->mode = Addressing_Constant; + operand->value = exact_value_i64(type_align_of(t)); + operand->type = t_untyped_integer; + + break; + } + + + case BuiltinProc_offset_of: { + // offset_of :: proc(Type, field) -> uintptr + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for 'offset_of'"); + return false; + } + + Ast *field_arg = unparen_expr(ce->args[1]); + if (field_arg == nullptr || + field_arg->kind != Ast_Ident) { + error(field_arg, "Expected an identifier for field argument"); + return false; + } + if (is_type_array(type)) { + error(field_arg, "Invalid type for 'offset_of'"); + return false; + } + + + ast_node(arg, Ident, field_arg); + Selection sel = lookup_field(type, arg->token.string, operand->mode == Addressing_Type); + if (sel.entity == nullptr) { + gbString type_str = type_to_string(bt); + error(ce->args[0], + "'%s' has no field named '%.*s'", type_str, LIT(arg->token.string)); + gb_string_free(type_str); + return false; + } + if (sel.indirect) { + gbString type_str = type_to_string(bt); + error(ce->args[0], + "Field '%.*s' is embedded via a pointer in '%s'", LIT(arg->token.string), type_str); + gb_string_free(type_str); + return false; + } + + operand->mode = Addressing_Constant; + operand->value = exact_value_i64(type_offset_of_from_selection(type, sel)); + operand->type = t_uintptr; + + break; + } + + + case BuiltinProc_type_of: { + // type_of :: proc(val: Type) -> type(Type) + Ast *expr = ce->args[0]; + Operand o = {}; + check_expr_or_type(c, &o, expr); + + // check_assignment(c, operand, nullptr, str_lit("argument of 'type_of'")); + if (o.mode == Addressing_Invalid || o.mode == Addressing_Builtin) { + return false; + } + if (o.type == nullptr || o.type == t_invalid || is_type_asm_proc(o.type)) { + error(o.expr, "Invalid argument to 'type_of'"); + return false; + } + // NOTE(bill): Prevent type cycles for procedure declarations + if (c->curr_proc_sig == o.type) { + gbString s = expr_to_string(o.expr); + error(o.expr, "Invalid cyclic type usage from 'type_of', got '%s'", s); + gb_string_free(s); + return false; + } + + if (is_type_polymorphic(o.type)) { + error(o.expr, "'type_of' of polymorphic type cannot be determined"); + return false; + } + operand->mode = Addressing_Type; + operand->type = o.type; + break; + } + + case BuiltinProc_type_info_of: { + // type_info_of :: proc(Type) -> ^Type_Info + if (c->scope->flags&ScopeFlag_Global) { + compiler_error("'type_info_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); + } + + // NOTE(bill): The type information may not be setup yet + init_core_type_info(c->checker); + Ast *expr = ce->args[0]; + Operand o = {}; + check_expr_or_type(c, &o, expr); + if (o.mode == Addressing_Invalid) { + return false; + } + Type *t = o.type; + if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(t)) { + if (is_type_polymorphic(t)) { + error(ce->args[0], "Invalid argument for 'type_info_of', unspecialized polymorphic type"); + } else { + error(ce->args[0], "Invalid argument for 'type_info_of'"); + } + return false; + } + t = default_type(t); + + add_type_info_type(c, t); + + if (is_operand_value(o) && is_type_typeid(t)) { + add_package_dependency(c, "runtime", "__type_info_of"); + } else if (o.mode != Addressing_Type) { + error(expr, "Expected a type or typeid for 'type_info_of'"); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_type_info_ptr; + break; + } + + case BuiltinProc_typeid_of: { + // typeid_of :: proc(Type) -> typeid + if (c->scope->flags&ScopeFlag_Global) { + compiler_error("'typeid_of' Cannot be declared within the runtime package due to how the internals of the compiler works"); + } + + // NOTE(bill): The type information may not be setup yet + init_core_type_info(c->checker); + Ast *expr = ce->args[0]; + Operand o = {}; + check_expr_or_type(c, &o, expr); + if (o.mode == Addressing_Invalid) { + return false; + } + Type *t = o.type; + if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(operand->type)) { + error(ce->args[0], "Invalid argument for 'typeid_of'"); + return false; + } + t = default_type(t); + + add_type_info_type(c, t); + + if (o.mode != Addressing_Type) { + error(expr, "Expected a type for 'typeid_of'"); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_typeid; + operand->value = exact_value_typeid(t); + break; + } + + case BuiltinProc_swizzle: { + // swizzle :: proc(v: [N]T, ..int) -> [M]T + Type *type = base_type(operand->type); + i64 max_count = 0; + Type *elem_type = nullptr; + + if (!is_type_array(type) && !is_type_simd_vector(type)) { + gbString type_str = type_to_string(operand->type); + error(call, + "'swizzle' is only allowed on an array or #simd vector, got '%s'", + type_str); + gb_string_free(type_str); + return false; + } + if (type->kind == Type_Array) { + max_count = type->Array.count; + elem_type = type->Array.elem; + } else if (type->kind == Type_SimdVector) { + max_count = type->SimdVector.count; + elem_type = type->SimdVector.elem; + if (!build_context.use_llvm_api) { + error(call, "'swizzle' with #simd vector is not supported on this backend"); + } + } + + i64 arg_count = 0; + for_array(i, ce->args) { + if (i == 0) { + continue; + } + Ast *arg = ce->args[i]; + Operand op = {}; + check_expr(c, &op, arg); + if (op.mode == Addressing_Invalid) { + return false; + } + Type *arg_type = base_type(op.type); + if (!is_type_integer(arg_type) || op.mode != Addressing_Constant) { + error(op.expr, "Indices to 'swizzle' must be constant integers"); + return false; + } + + if (op.value.value_integer.neg) { + error(op.expr, "Negative 'swizzle' index"); + return false; + } + + BigInt mc = {}; + big_int_from_i64(&mc, max_count); + if (big_int_cmp(&mc, &op.value.value_integer) <= 0) { + error(op.expr, "'swizzle' index exceeds length"); + return false; + } + + arg_count++; + } + + if (arg_count > max_count) { + error(call, "Too many 'swizzle' indices, %td > %td", arg_count, max_count); + return false; + } + + if (arg_count < max_count) { + operand->type = alloc_type_array(elem_type, arg_count); + } + operand->mode = Addressing_Value; + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_complex: { + // complex :: proc(real, imag: float_type) -> complex_type + Operand x = *operand; + Operand y = {}; + + // NOTE(bill): Invalid will be the default till fixed + operand->type = t_invalid; + operand->mode = Addressing_Invalid; + + check_expr(c, &y, ce->args[1]); + if (y.mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant) { + if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) { + x.type = t_untyped_float; + } + if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) { + y.type = t_untyped_float; + } + } + + if (!are_types_identical(x.type, y.type)) { + gbString tx = type_to_string(x.type); + gbString ty = type_to_string(y.type); + error(call, "Mismatched types to 'complex', '%s' vs '%s'", tx, ty); + gb_string_free(ty); + gb_string_free(tx); + return false; + } + + if (!is_type_float(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments have type '%s', expected a floating point", s); + gb_string_free(s); + return false; + } + if (is_type_endian_specific(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments with a specified endian are not allow, expected a normal floating point, got '%s'", s); + gb_string_free(s); + return false; + } + + if (x.mode == Addressing_Constant && y.mode == Addressing_Constant) { + f64 r = exact_value_to_float(x.value).value_float; + f64 i = exact_value_to_float(y.value).value_float; + operand->value = exact_value_complex(r, i); + operand->mode = Addressing_Constant; + } else { + operand->mode = Addressing_Value; + } + + BasicKind kind = core_type(x.type)->Basic.kind; + switch (kind) { + case Basic_f16: operand->type = t_complex32; break; + case Basic_f32: operand->type = t_complex64; break; + case Basic_f64: operand->type = t_complex128; break; + case Basic_UntypedFloat: operand->type = t_untyped_complex; break; + default: GB_PANIC("Invalid type"); break; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_quaternion: { + // quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type + Operand x = *operand; + Operand y = {}; + Operand z = {}; + Operand w = {}; + + // NOTE(bill): Invalid will be the default till fixed + operand->type = t_invalid; + operand->mode = Addressing_Invalid; + + check_expr(c, &y, ce->args[1]); + if (y.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &z, ce->args[2]); + if (y.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &w, ce->args[3]); + if (y.mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false; + convert_to_typed(c, &w, x.type); if (w.mode == Addressing_Invalid) return false; + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant && + z.mode == Addressing_Constant && + w.mode == Addressing_Constant) { + if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) { + x.type = t_untyped_float; + } + if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) { + y.type = t_untyped_float; + } + if (is_type_numeric(z.type) && exact_value_imag(z.value).value_float == 0) { + z.type = t_untyped_float; + } + if (is_type_numeric(w.type) && exact_value_imag(w.value).value_float == 0) { + w.type = t_untyped_float; + } + } + + if (!(are_types_identical(x.type, y.type) && are_types_identical(x.type, z.type) && are_types_identical(x.type, w.type))) { + gbString tx = type_to_string(x.type); + gbString ty = type_to_string(y.type); + gbString tz = type_to_string(z.type); + gbString tw = type_to_string(w.type); + error(call, "Mismatched types to 'quaternion', '%s' vs '%s' vs '%s' vs '%s'", tx, ty, tz, tw); + gb_string_free(tw); + gb_string_free(tz); + gb_string_free(ty); + gb_string_free(tx); + return false; + } + + if (!is_type_float(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments have type '%s', expected a floating point", s); + gb_string_free(s); + return false; + } + if (is_type_endian_specific(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments with a specified endian are not allow, expected a normal floating point, got '%s'", s); + gb_string_free(s); + return false; + } + + if (x.mode == Addressing_Constant && y.mode == Addressing_Constant && z.mode == Addressing_Constant && w.mode == Addressing_Constant) { + f64 r = exact_value_to_float(x.value).value_float; + f64 i = exact_value_to_float(y.value).value_float; + f64 j = exact_value_to_float(z.value).value_float; + f64 k = exact_value_to_float(w.value).value_float; + operand->value = exact_value_quaternion(r, i, j, k); + operand->mode = Addressing_Constant; + } else { + operand->mode = Addressing_Value; + } + + BasicKind kind = core_type(x.type)->Basic.kind; + switch (kind) { + case Basic_f16: operand->type = t_quaternion64; break; + case Basic_f32: operand->type = t_quaternion128; break; + case Basic_f64: operand->type = t_quaternion256; break; + case Basic_UntypedFloat: operand->type = t_untyped_quaternion; break; + default: GB_PANIC("Invalid type"); break; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_real: + case BuiltinProc_imag: { + // real :: proc(x: type) -> float_type + // imag :: proc(x: type) -> float_type + + Operand *x = operand; + if (is_type_untyped(x->type)) { + if (x->mode == Addressing_Constant) { + if (is_type_numeric(x->type)) { + x->type = t_untyped_complex; + } + } else if (is_type_quaternion(x->type)) { + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } else{ + convert_to_typed(c, x, t_complex128); + if (x->mode == Addressing_Invalid) { + return false; + } + } + } + + if (!is_type_complex(x->type) && !is_type_quaternion(x->type)) { + gbString s = type_to_string(x->type); + error(call, "Argument has type '%s', expected a complex or quaternion type", s); + gb_string_free(s); + return false; + } + + if (x->mode == Addressing_Constant) { + switch (id) { + case BuiltinProc_real: x->value = exact_value_real(x->value); break; + case BuiltinProc_imag: x->value = exact_value_imag(x->value); break; + } + } else { + x->mode = Addressing_Value; + } + + BasicKind kind = core_type(x->type)->Basic.kind; + switch (kind) { + case Basic_complex32: x->type = t_f16; break; + case Basic_complex64: x->type = t_f32; break; + case Basic_complex128: x->type = t_f64; break; + case Basic_quaternion64: x->type = t_f16; break; + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; + case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; + default: GB_PANIC("Invalid type"); break; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_jmag: + case BuiltinProc_kmag: { + // jmag :: proc(x: type) -> float_type + // kmag :: proc(x: type) -> float_type + + Operand *x = operand; + if (is_type_untyped(x->type)) { + if (x->mode == Addressing_Constant) { + if (is_type_numeric(x->type)) { + x->type = t_untyped_complex; + } + } else{ + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } + } + + if (!is_type_quaternion(x->type)) { + gbString s = type_to_string(x->type); + error(call, "Argument has type '%s', expected a quaternion type", s); + gb_string_free(s); + return false; + } + + if (x->mode == Addressing_Constant) { + switch (id) { + case BuiltinProc_jmag: x->value = exact_value_jmag(x->value); break; + case BuiltinProc_kmag: x->value = exact_value_kmag(x->value); break; + } + } else { + x->mode = Addressing_Value; + } + + BasicKind kind = core_type(x->type)->Basic.kind; + switch (kind) { + case Basic_quaternion64: x->type = t_f16; break; + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; + case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; + default: GB_PANIC("Invalid type"); break; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_conj: { + // conj :: proc(x: type) -> type + Operand *x = operand; + if (is_type_complex(x->type)) { + if (x->mode == Addressing_Constant) { + ExactValue v = exact_value_to_complex(x->value); + f64 r = v.value_complex->real; + f64 i = -v.value_complex->imag; + x->value = exact_value_complex(r, i); + x->mode = Addressing_Constant; + } else { + x->mode = Addressing_Value; + } + } else if (is_type_quaternion(x->type)) { + if (x->mode == Addressing_Constant) { + ExactValue v = exact_value_to_quaternion(x->value); + f64 r = +v.value_quaternion->real; + f64 i = -v.value_quaternion->imag; + f64 j = -v.value_quaternion->jmag; + f64 k = -v.value_quaternion->kmag; + x->value = exact_value_quaternion(r, i, j, k); + x->mode = Addressing_Constant; + } else { + x->mode = Addressing_Value; + } + } else { + gbString s = type_to_string(x->type); + error(call, "Expected a complex or quaternion, got '%s'", s); + gb_string_free(s); + return false; + } + + break; + } + + case BuiltinProc_expand_to_tuple: { + Type *type = base_type(operand->type); + if (!is_type_struct(type) && !is_type_array(type)) { + gbString type_str = type_to_string(operand->type); + error(call, "Expected a struct or array type, got '%s'", type_str); + gb_string_free(type_str); + return false; + } + gbAllocator a = permanent_allocator(); + + Type *tuple = alloc_type_tuple(); + + if (is_type_struct(type)) { + isize variable_count = type->Struct.fields.count; + array_init(&tuple->Tuple.variables, a, variable_count); + // TODO(bill): Should I copy each of the entities or is this good enough? + gb_memmove_array(tuple->Tuple.variables.data, type->Struct.fields.data, variable_count); + } else if (is_type_array(type)) { + isize variable_count = type->Array.count; + array_init(&tuple->Tuple.variables, a, variable_count); + for (isize i = 0; i < variable_count; i++) { + tuple->Tuple.variables[i] = alloc_entity_array_elem(nullptr, blank_token, type->Array.elem, cast(i32)i); + } + } + operand->type = tuple; + operand->mode = Addressing_Value; + + if (tuple->Tuple.variables.count == 1) { + operand->type = tuple->Tuple.variables[0]->type; + } + + break; + } + + case BuiltinProc_min: { + // min :: proc($T: typeid) -> ordered + // min :: proc(a: ..ordered) -> ordered + + check_multi_expr_or_type(c, operand, ce->args[0]); + + Type *original_type = operand->type; + Type *type = base_type(operand->type); + if (operand->mode == Addressing_Type && is_type_enumerated_array(type)) { + // Okay + } else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) { + gbString type_str = type_to_string(original_type); + error(call, "Expected a ordered numeric type to 'min', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + if (operand->mode == Addressing_Type) { + if (ce->args.count != 1) { + error(call, "If 'min' gets a type, only 1 arguments is allowed, got %td", ce->args.count); + return false; + } + + if (is_type_boolean(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + operand->value = exact_value_bool(false); + return true; + } else if (is_type_integer(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + if (is_type_unsigned(type)) { + operand->value = exact_value_u64(0); + return true; + } else { + i64 sz = 8*type_size_of(type); + ExactValue a = exact_value_i64(1); + ExactValue b = exact_value_i64(sz-1); + ExactValue v = exact_binary_operator_value(Token_Shl, a, b); + v = exact_unary_operator_value(Token_Sub, v, cast(i32)sz, false); + operand->value = v; + return true; + } + } else if (is_type_float(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + switch (type_size_of(type)) { + case 2: + operand->value = exact_value_float(-65504.0f); + break; + case 4: + operand->value = exact_value_float(-3.402823466e+38f); + break; + case 8: + operand->value = exact_value_float(-1.7976931348623158e+308); + break; + default: + GB_PANIC("Unhandled float type"); + break; + } + return true; + } else if (is_type_enum(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + operand->value = type->Enum.min_value; + return true; + } else if (is_type_enumerated_array(type)) { + Type *bt = base_type(type); + GB_ASSERT(bt->kind == Type_EnumeratedArray); + operand->mode = Addressing_Constant; + operand->type = bt->EnumeratedArray.index; + operand->value = bt->EnumeratedArray.min_value; + return true; + } + gbString type_str = type_to_string(original_type); + error(call, "Invalid type for 'min', got %s", type_str); + gb_string_free(type_str); + return false; + } + + + bool all_constant = operand->mode == Addressing_Constant; + + auto operands = array_make<Operand>(heap_allocator(), 0, ce->args.count); + defer (array_free(&operands)); + + array_add(&operands, *operand); + + for (isize i = 1; i < ce->args.count; i++) { + Ast *other_arg = ce->args[i]; + Operand b = {}; + check_expr(c, &b, other_arg); + if (b.mode == Addressing_Invalid) { + return false; + } + if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) { + gbString type_str = type_to_string(b.type); + error(call, + "Expected a ordered numeric type to 'min', got '%s'", + type_str); + gb_string_free(type_str); + return false; + } + array_add(&operands, b); + + if (all_constant) { + all_constant = b.mode == Addressing_Constant; + } + } + + if (all_constant) { + ExactValue value = operands[0].value; + Type *type = operands[0].type; + for (isize i = 1; i < operands.count; i++) { + Operand y = operands[i]; + if (compare_exact_values(Token_Lt, value, y.value)) { + // okay + } else { + value = y.value; + type = y.type; + } + } + operand->value = value; + operand->type = type; + } else { + operand->mode = Addressing_Value; + operand->type = original_type; + + for_array(i, operands) { + Operand *a = &operands[i]; + for_array(j, operands) { + if (i == j) { + continue; + } + Operand *b = &operands[j]; + + convert_to_typed(c, a, b->type); + if (a->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, b, a->type); + if (b->mode == Addressing_Invalid) { + return false; + } + } + } + + for (isize i = 0; i < operands.count-1; i++) { + Operand *a = &operands[i]; + Operand *b = &operands[i+1]; + + if (!are_types_identical(a->type, b->type)) { + gbString type_a = type_to_string(a->type); + gbString type_b = type_to_string(b->type); + error(a->expr, + "Mismatched types to 'min', '%s' vs '%s'", + type_a, type_b); + gb_string_free(type_b); + gb_string_free(type_a); + return false; + } + } + + + { + Type *bt = base_type(operands[0].type); + if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "min_f32"); + if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "min_f64"); + + operand->type = operands[0].type; + } + } + break; + } + + case BuiltinProc_max: { + // max :: proc($T: typeid) -> ordered + // max :: proc(a: ..ordered) -> ordered + + check_multi_expr_or_type(c, operand, ce->args[0]); + + Type *original_type = operand->type; + Type *type = base_type(operand->type); + + if (operand->mode == Addressing_Type && is_type_enumerated_array(type)) { + // Okay + } else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) { + gbString type_str = type_to_string(original_type); + error(call, "Expected a ordered numeric type to 'max', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + if (operand->mode == Addressing_Type) { + if (ce->args.count != 1) { + error(call, "If 'max' gets a type, only 1 arguments is allowed, got %td", ce->args.count); + return false; + } + + if (is_type_boolean(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + operand->value = exact_value_bool(true); + return true; + } else if (is_type_integer(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + if (is_type_unsigned(type)) { + i64 sz = 8*type_size_of(type); + ExactValue a = exact_value_i64(1); + ExactValue b = exact_value_i64(sz); + ExactValue v = exact_binary_operator_value(Token_Shl, a, b); + v = exact_binary_operator_value(Token_Sub, v, a); + operand->value = v; + return true; + } else { + i64 sz = 8*type_size_of(type); + ExactValue a = exact_value_i64(1); + ExactValue b = exact_value_i64(sz-1); + ExactValue v = exact_binary_operator_value(Token_Shl, a, b); + v = exact_binary_operator_value(Token_Sub, v, a); + operand->value = v; + return true; + } + } else if (is_type_float(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + switch (type_size_of(type)) { + case 2: + operand->value = exact_value_float(65504.0f); + break; + case 4: + operand->value = exact_value_float(3.402823466e+38f); + break; + case 8: + operand->value = exact_value_float(1.7976931348623158e+308); + break; + default: + GB_PANIC("Unhandled float type"); + break; + } + return true; + } else if (is_type_enum(type)) { + operand->mode = Addressing_Constant; + operand->type = original_type; + operand->value = type->Enum.max_value; + return true; + } else if (is_type_enumerated_array(type)) { + Type *bt = base_type(type); + GB_ASSERT(bt->kind == Type_EnumeratedArray); + operand->mode = Addressing_Constant; + operand->type = bt->EnumeratedArray.index; + operand->value = bt->EnumeratedArray.max_value; + return true; + } + gbString type_str = type_to_string(original_type); + error(call, "Invalid type for 'max', got %s", type_str); + gb_string_free(type_str); + return false; + } + + bool all_constant = operand->mode == Addressing_Constant; + + auto operands = array_make<Operand>(heap_allocator(), 0, ce->args.count); + defer (array_free(&operands)); + + array_add(&operands, *operand); + + + for (isize i = 1; i < ce->args.count; i++) { + Ast *arg = ce->args[i]; + Operand b = {}; + check_expr(c, &b, arg); + if (b.mode == Addressing_Invalid) { + return false; + } + if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) { + gbString type_str = type_to_string(b.type); + error(arg, + "Expected a ordered numeric type to 'max', got '%s'", + type_str); + gb_string_free(type_str); + return false; + } + array_add(&operands, b); + + if (all_constant) { + all_constant = b.mode == Addressing_Constant; + } + } + + if (all_constant) { + ExactValue value = operands[0].value; + Type *type = operands[0].type; + for (isize i = 1; i < operands.count; i++) { + Operand y = operands[i]; + if (compare_exact_values(Token_Gt, value, y.value)) { + // okay + } else { + type = y.type; + value = y.value; + } + } + operand->value = value; + operand->type = type; + } else { + operand->mode = Addressing_Value; + operand->type = original_type; + + for_array(i, operands) { + Operand *a = &operands[i]; + for_array(j, operands) { + if (i == j) { + continue; + } + Operand *b = &operands[j]; + + convert_to_typed(c, a, b->type); + if (a->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, b, a->type); + if (b->mode == Addressing_Invalid) { + return false; + } + } + } + + for (isize i = 0; i < operands.count-1; i++) { + Operand *a = &operands[i]; + Operand *b = &operands[i+1]; + + if (!are_types_identical(a->type, b->type)) { + gbString type_a = type_to_string(a->type); + gbString type_b = type_to_string(b->type); + error(a->expr, + "Mismatched types to 'max', '%s' vs '%s'", + type_a, type_b); + gb_string_free(type_b); + gb_string_free(type_a); + return false; + } + } + + { + Type *bt = base_type(operands[0].type); + if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "max_f32"); + if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "max_f64"); + + operand->type = operands[0].type; + } + } + break; + } + + case BuiltinProc_abs: { + // abs :: proc(n: numeric) -> numeric + if (!(is_type_numeric(operand->type) && !is_type_array(operand->type))) { + gbString type_str = type_to_string(operand->type); + error(call, "Expected a numeric type to 'abs', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + if (operand->mode == Addressing_Constant) { + switch (operand->value.kind) { + case ExactValue_Integer: + operand->value.value_integer.neg = false; + break; + case ExactValue_Float: + operand->value.value_float = gb_abs(operand->value.value_float); + break; + case ExactValue_Complex: { + f64 r = operand->value.value_complex->real; + f64 i = operand->value.value_complex->imag; + operand->value = exact_value_float(gb_sqrt(r*r + i*i)); + + break; + } + default: + GB_PANIC("Invalid numeric constant"); + break; + } + } else { + operand->mode = Addressing_Value; + + { + Type *bt = base_type(operand->type); + if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "abs_f32"); + if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "abs_f64"); + if (are_types_identical(bt, t_complex64)) add_package_dependency(c, "runtime", "abs_complex64"); + if (are_types_identical(bt, t_complex128)) add_package_dependency(c, "runtime", "abs_complex128"); + if (are_types_identical(bt, t_quaternion128)) add_package_dependency(c, "runtime", "abs_quaternion128"); + if (are_types_identical(bt, t_quaternion256)) add_package_dependency(c, "runtime", "abs_quaternion256"); + } + } + + if (is_type_complex(operand->type)) { + operand->type = base_complex_elem_type(operand->type); + } + GB_ASSERT(!is_type_complex(operand->type)); + + break; + } + + case BuiltinProc_clamp: { + // clamp :: proc(a, min, max: ordered) -> ordered + Type *type = operand->type; + if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) { + gbString type_str = type_to_string(operand->type); + error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + Ast *min_arg = ce->args[1]; + Ast *max_arg = ce->args[2]; + Operand x = *operand; + Operand y = {}; + Operand z = {}; + + check_expr(c, &y, min_arg); + if (y.mode == Addressing_Invalid) { + return false; + } + if (!is_type_ordered(y.type) || !(is_type_numeric(y.type) || is_type_string(y.type))) { + gbString type_str = type_to_string(y.type); + error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + check_expr(c, &z, max_arg); + if (z.mode == Addressing_Invalid) { + return false; + } + if (!is_type_ordered(z.type) || !(is_type_numeric(z.type) || is_type_string(z.type))) { + gbString type_str = type_to_string(z.type); + error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str); + gb_string_free(type_str); + return false; + } + + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant && + z.mode == Addressing_Constant) { + ExactValue a = x.value; + ExactValue b = y.value; + ExactValue c = z.value; + + operand->mode = Addressing_Constant; + if (compare_exact_values(Token_Lt, a, b)) { + operand->value = b; + operand->type = y.type; + } else if (compare_exact_values(Token_Gt, a, c)) { + operand->value = c; + operand->type = z.type; + } else { + operand->value = a; + operand->type = x.type; + } + } else { + operand->mode = Addressing_Value; + operand->type = type; + + Operand *ops[3] = {&x, &y, &z}; + for (isize i = 0; i < 3; i++) { + Operand *a = ops[i]; + for (isize j = 0; j < 3; j++) { + if (i == j) continue; + Operand *b = ops[j]; + convert_to_typed(c, a, b->type); + if (a->mode == Addressing_Invalid) { return false; } + } + } + + if (!are_types_identical(x.type, y.type) || !are_types_identical(x.type, z.type)) { + gbString type_x = type_to_string(x.type); + gbString type_y = type_to_string(y.type); + gbString type_z = type_to_string(z.type); + error(call, + "Mismatched types to 'clamp', '%s', '%s', '%s'", + type_x, type_y, type_z); + gb_string_free(type_z); + gb_string_free(type_y); + gb_string_free(type_x); + return false; + } + + { + Type *bt = base_type(x.type); + if (are_types_identical(bt, t_f32)) { + add_package_dependency(c, "runtime", "min_f32"); + add_package_dependency(c, "runtime", "max_f32"); + } + if (are_types_identical(bt, t_f64)) { + add_package_dependency(c, "runtime", "min_f64"); + add_package_dependency(c, "runtime", "max_f64"); + } + + operand->type = ops[0]->type; + } + } + + break; + } + + case BuiltinProc_soa_zip: { + if (!build_context.use_llvm_api) { + error(call, "'soa_zip' is not supported with this backend"); + return false; + } + + auto types = array_make<Type *>(temporary_allocator(), 0, ce->args.count); + auto names = array_make<String>(temporary_allocator(), 0, ce->args.count); + + bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); + + bool fail = false; + for_array(i, ce->args) { + Ast *arg = ce->args[i]; + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + error(arg, "Mixture of 'field = value' and value elements in the procedure call 'soa_zip' is not allowed"); + fail = true; + break; + } + } + StringSet name_set = {}; + string_set_init(&name_set, temporary_allocator(), 2*ce->args.count); + + for_array(i, ce->args) { + String name = {}; + Ast *arg = ce->args[i]; + if (arg->kind == Ast_FieldValue) { + Ast *ename = arg->FieldValue.field; + if (!fail && ename->kind != Ast_Ident) { + error(ename, "Expected an identifier for field argument"); + } else if (ename->kind == Ast_Ident) { + name = ename->Ident.token.string; + } + arg = arg->FieldValue.value; + } + + Operand op = {}; + check_expr(c, &op, arg); + if (op.mode == Addressing_Invalid) { + return false; + } + Type *arg_type = base_type(op.type); + if (!is_type_slice(arg_type)) { + gbString s = type_to_string(op.type); + error(op.expr, "Indices to 'soa_zip' must be slices, got %s", s); + gb_string_free(s); + return false; + } + GB_ASSERT(arg_type->kind == Type_Slice); + if (name == "_") { + error(op.expr, "Field argument name '%.*s' is not allowed", LIT(name)); + name = {}; + } + if (name.len == 0) { + gbString field_name = gb_string_make(permanent_allocator(), "_"); + field_name = gb_string_append_fmt(field_name, "%td", types.count); + name = make_string_c(field_name); + } + + + if (string_set_exists(&name_set, name)) { + error(op.expr, "Field argument name '%.*s' already exists", LIT(name)); + } else { + array_add(&types, arg_type->Slice.elem); + array_add(&names, name); + + string_set_add(&name_set, name); + } + } + + + + + Ast *dummy_node_struct = alloc_ast_node(nullptr, Ast_Invalid); + Ast *dummy_node_soa = alloc_ast_node(nullptr, Ast_Invalid); + Scope *s = create_scope(builtin_pkg->scope); + + auto fields = array_make<Entity *>(permanent_allocator(), 0, types.count); + for_array(i, types) { + Type *type = types[i]; + String name = names[i]; + GB_ASSERT(name != ""); + Entity *e = alloc_entity_field(s, make_token_ident(name), type, false, cast(i32)i, EntityState_Resolved); + array_add(&fields, e); + scope_insert(s, e); + } + + Type *elem = nullptr; + if (type_hint != nullptr && is_type_struct(type_hint)) { + Type *soa_type = base_type(type_hint); + if (soa_type->Struct.soa_kind != StructSoa_Slice) { + goto soa_zip_end; + } + Type *soa_elem_type = soa_type->Struct.soa_elem; + Type *et = base_type(soa_elem_type); + if (et->kind != Type_Struct) { + goto soa_zip_end; + } + + if (et->Struct.fields.count != fields.count) { + goto soa_zip_end; + } + if (!fail && first_is_field_value) { + for_array(i, names) { + Selection sel = lookup_field(et, names[i], false); + if (sel.entity == nullptr) { + goto soa_zip_end; + } + if (sel.index.count != 1) { + goto soa_zip_end; + } + if (!are_types_identical(sel.entity->type, types[i])) { + goto soa_zip_end; + } + } + } else { + for_array(i, et->Struct.fields) { + if (!are_types_identical(et->Struct.fields[i]->type, types[i])) { + goto soa_zip_end; + } + } + } + + elem = soa_elem_type; + } + + soa_zip_end:; + + if (elem == nullptr) { + elem = alloc_type_struct(); + elem->Struct.scope = s; + elem->Struct.fields = fields; + elem->Struct.tags = array_make<String>(permanent_allocator(), fields.count); + elem->Struct.node = dummy_node_struct; + type_set_offsets(elem); + } + + Type *soa_type = make_soa_struct_slice(c, dummy_node_soa, nullptr, elem); + type_set_offsets(soa_type); + + operand->type = soa_type; + operand->mode = Addressing_Value; + + break; + } + + case BuiltinProc_soa_unzip: { + if (!build_context.use_llvm_api) { + error(call, "'soa_unzip' is not supported with this backend"); + return false; + } + + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (!is_operand_value(x)) { + error(call, "'soa_unzip' expects an #soa slice"); + return false; + } + Type *t = base_type(x.type); + if (!is_type_soa_struct(t) || t->Struct.soa_kind != StructSoa_Slice) { + gbString s = type_to_string(x.type); + error(call, "'soa_unzip' expects an #soa slice, got %s", s); + gb_string_free(s); + return false; + } + auto types = slice_make<Type *>(permanent_allocator(), t->Struct.fields.count-1); + for_array(i, types) { + Entity *f = t->Struct.fields[i]; + GB_ASSERT(f->type->kind == Type_Pointer); + types[i] = alloc_type_slice(f->type->Pointer.elem); + } + + operand->type = alloc_type_tuple_from_field_types(types.data, types.count, false, false); + operand->mode = Addressing_Value; + break; + } + + + case BuiltinProc_simd_vector: { + Operand x = {}; + Operand y = {}; + x = *operand; + if (!is_type_integer(x.type) || x.mode != Addressing_Constant) { + error(call, "Expected a constant integer for 'intrinsics.simd_vector'"); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + if (x.value.value_integer.neg) { + error(call, "Negative vector element length"); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + i64 count = big_int_to_i64(&x.value.value_integer); + + check_expr_or_type(c, &y, ce->args[1]); + if (y.mode != Addressing_Type) { + error(call, "Expected a type 'intrinsics.simd_vector'"); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + Type *elem = y.type; + if (!is_type_valid_vector_elem(elem)) { + gbString str = type_to_string(elem); + error(call, "Invalid element type for 'intrinsics.simd_vector', expected an integer or float with no specific endianness, got '%s'", str); + gb_string_free(str); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + + operand->mode = Addressing_Type; + operand->type = alloc_type_simd_vector(count, elem); + break; + } + + case BuiltinProc_soa_struct: { + Operand x = {}; + Operand y = {}; + x = *operand; + if (!is_type_integer(x.type) || x.mode != Addressing_Constant) { + error(call, "Expected a constant integer for 'intrinsics.soa_struct'"); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + if (x.value.value_integer.neg) { + error(call, "Negative array element length"); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + i64 count = big_int_to_i64(&x.value.value_integer); + + check_expr_or_type(c, &y, ce->args[1]); + if (y.mode != Addressing_Type) { + error(call, "Expected a type 'intrinsics.soa_struct'"); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + Type *elem = y.type; + Type *bt_elem = base_type(elem); + if (!is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) { + gbString str = type_to_string(elem); + error(call, "Invalid type for 'intrinsics.soa_struct', expected a struct or array of length 4 or below, got '%s'", str); + gb_string_free(str); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + + operand->mode = Addressing_Type; + Type *soa_struct = nullptr; + Scope *scope = nullptr; + + if (is_type_array(elem)) { + Type *old_array = base_type(elem); + soa_struct = alloc_type_struct(); + soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), old_array->Array.count); + soa_struct->Struct.tags = array_make<String>(heap_allocator(), old_array->Array.count); + soa_struct->Struct.node = operand->expr; + soa_struct->Struct.soa_kind = StructSoa_Fixed; + soa_struct->Struct.soa_elem = elem; + soa_struct->Struct.soa_count = count; + + scope = create_scope(c->scope); + soa_struct->Struct.scope = scope; + + String params_xyzw[4] = { + str_lit("x"), + str_lit("y"), + str_lit("z"), + str_lit("w") + }; + + for (i64 i = 0; i < old_array->Array.count; i++) { + Type *array_type = alloc_type_array(old_array->Array.elem, count); + Token token = {}; + token.string = params_xyzw[i]; + + Entity *new_field = alloc_entity_field(scope, token, array_type, false, cast(i32)i); + soa_struct->Struct.fields[i] = new_field; + add_entity(c->checker, scope, nullptr, new_field); + add_entity_use(c, nullptr, new_field); + } + + } else { + GB_ASSERT(is_type_struct(elem)); + + Type *old_struct = base_type(elem); + soa_struct = alloc_type_struct(); + soa_struct->Struct.fields = array_make<Entity *>(heap_allocator(), old_struct->Struct.fields.count); + soa_struct->Struct.tags = array_make<String>(heap_allocator(), old_struct->Struct.tags.count); + soa_struct->Struct.node = operand->expr; + soa_struct->Struct.soa_kind = StructSoa_Fixed; + soa_struct->Struct.soa_elem = elem; + soa_struct->Struct.soa_count = count; + + scope = create_scope(old_struct->Struct.scope->parent); + soa_struct->Struct.scope = scope; + + for_array(i, old_struct->Struct.fields) { + Entity *old_field = old_struct->Struct.fields[i]; + if (old_field->kind == Entity_Variable) { + Type *array_type = alloc_type_array(old_field->type, count); + Entity *new_field = alloc_entity_field(scope, old_field->token, array_type, false, old_field->Variable.field_src_index); + soa_struct->Struct.fields[i] = new_field; + add_entity(c->checker, scope, nullptr, new_field); + } else { + soa_struct->Struct.fields[i] = old_field; + } + + soa_struct->Struct.tags[i] = old_struct->Struct.tags[i]; + } + } + + Token token = {}; + token.string = str_lit("Base_Type"); + Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved); + add_entity(c->checker, scope, nullptr, base_type_entity); + + add_type_info_type(c, soa_struct); + + operand->type = soa_struct; + break; + } + + case BuiltinProc_alloca: + { + Operand sz = {}; + Operand al = {}; + + check_expr(c, &sz, ce->args[0]); + if (sz.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &al, ce->args[1]); + if (al.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &sz, t_int); if (sz.mode == Addressing_Invalid) return false; + convert_to_typed(c, &al, t_int); if (al.mode == Addressing_Invalid) return false; + + if (!is_type_integer(sz.type) || !is_type_integer(al.type)) { + error(operand->expr, "Both parameters to '%.*s' must integers", LIT(builtin_name)); + return false; + } + + if (sz.mode == Addressing_Constant) { + i64 i_sz = exact_value_to_i64(sz.value); + if (i_sz < 0) { + error(sz.expr, "Size parameter to '%.*s' must be non-negative, got %lld", LIT(builtin_name), cast(long long)i_sz); + return false; + } + } + if (al.mode == Addressing_Constant) { + i64 i_al = exact_value_to_i64(al.value); + if (i_al < 0) { + error(al.expr, "Alignment parameter to '%.*s' must be non-negative, got %lld", LIT(builtin_name), cast(long long)i_al); + return false; + } + + if (i_al > 1<<29) { + error(al.expr, "Alignment parameter to '%.*s' must not exceed '1<<29', got %lld", LIT(builtin_name), cast(long long)i_al); + return false; + } + + if (!gb_is_power_of_two(cast(isize)i_al) && i_al != 0) { + error(al.expr, "Alignment parameter to '%.*s' must be a power of 2 or 0, got %lld", LIT(builtin_name), cast(long long)i_al); + return false; + } + } else { + error(al.expr, "Alignment parameter to '%.*s' must be constant", LIT(builtin_name)); + } + + operand->type = t_u8_ptr; + operand->mode = Addressing_Value; + break; + } + + + case BuiltinProc_cpu_relax: + operand->mode = Addressing_NoValue; + break; + + case BuiltinProc_trap: + case BuiltinProc_debug_trap: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + } + operand->mode = Addressing_NoValue; + break; + + case BuiltinProc_read_cycle_counter: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + } + operand->mode = Addressing_Value; + operand->type = t_i64; + break; + + case BuiltinProc_count_ones: + case BuiltinProc_trailing_zeros: + case BuiltinProc_reverse_bits: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + + if (!is_type_integer_like(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + } else if (x.type == t_llvm_bool) { + gbString xts = type_to_string(x.type); + error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + } + + operand->mode = Addressing_Value; + operand->type = default_type(x.type); + } + break; + + case BuiltinProc_byte_swap: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + + if (!is_type_integer_like(x.type) && !is_type_float(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set) or float, got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + } else if (x.type == t_llvm_bool) { + gbString xts = type_to_string(x.type); + error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + } + i64 sz = type_size_of(x.type); + if (sz < 2) { + gbString xts = type_to_string(x.type); + error(x.expr, "Type passed to '%.*s' must be at least 2 bytes, got %s with size of %lld", LIT(builtin_procs[id].name), xts, sz); + gb_string_free(xts); + } + + operand->mode = Addressing_Value; + operand->type = default_type(x.type); + } + break; + + case BuiltinProc_overflow_add: + case BuiltinProc_overflow_sub: + case BuiltinProc_overflow_mul: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[0]); + check_expr(c, &y, ce->args[1]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (y.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &y, x.type); + convert_to_typed(c, &x, y.type); + if (is_type_untyped(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a typed integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + if (!is_type_integer(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected an integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + Type *ct = core_type(x.type); + if (is_type_different_to_arch_endianness(ct)) { + GB_ASSERT(ct->kind == Type_Basic); + if (ct->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected an integer which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + } + + operand->mode = Addressing_Value; + operand->type = make_optional_ok_type(default_type(x.type), false); // Just reusing this procedure, it's not optional + } + break; + + case BuiltinProc_atomic_fence: + case BuiltinProc_atomic_fence_acq: + case BuiltinProc_atomic_fence_rel: + case BuiltinProc_atomic_fence_acqrel: + operand->mode = Addressing_NoValue; + break; + + case BuiltinProc_volatile_store: + /*fallthrough*/ + case BuiltinProc_atomic_store: + case BuiltinProc_atomic_store_rel: + case BuiltinProc_atomic_store_relaxed: + case BuiltinProc_atomic_store_unordered: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_assignment(c, &x, elem, builtin_name); + + operand->type = nullptr; + operand->mode = Addressing_NoValue; + break; + } + + case BuiltinProc_volatile_load: + /*fallthrough*/ + case BuiltinProc_atomic_load: + case BuiltinProc_atomic_load_acq: + case BuiltinProc_atomic_load_relaxed: + case BuiltinProc_atomic_load_unordered: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + operand->type = elem; + operand->mode = Addressing_Value; + break; + } + + case BuiltinProc_atomic_add: + case BuiltinProc_atomic_add_acq: + case BuiltinProc_atomic_add_rel: + case BuiltinProc_atomic_add_acqrel: + case BuiltinProc_atomic_add_relaxed: + case BuiltinProc_atomic_sub: + case BuiltinProc_atomic_sub_acq: + case BuiltinProc_atomic_sub_rel: + case BuiltinProc_atomic_sub_acqrel: + case BuiltinProc_atomic_sub_relaxed: + case BuiltinProc_atomic_and: + case BuiltinProc_atomic_and_acq: + case BuiltinProc_atomic_and_rel: + case BuiltinProc_atomic_and_acqrel: + case BuiltinProc_atomic_and_relaxed: + case BuiltinProc_atomic_nand: + case BuiltinProc_atomic_nand_acq: + case BuiltinProc_atomic_nand_rel: + case BuiltinProc_atomic_nand_acqrel: + case BuiltinProc_atomic_nand_relaxed: + case BuiltinProc_atomic_or: + case BuiltinProc_atomic_or_acq: + case BuiltinProc_atomic_or_rel: + case BuiltinProc_atomic_or_acqrel: + case BuiltinProc_atomic_or_relaxed: + case BuiltinProc_atomic_xor: + case BuiltinProc_atomic_xor_acq: + case BuiltinProc_atomic_xor_rel: + case BuiltinProc_atomic_xor_acqrel: + case BuiltinProc_atomic_xor_relaxed: + case BuiltinProc_atomic_xchg: + case BuiltinProc_atomic_xchg_acq: + case BuiltinProc_atomic_xchg_rel: + case BuiltinProc_atomic_xchg_acqrel: + case BuiltinProc_atomic_xchg_relaxed: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_assignment(c, &x, elem, builtin_name); + + operand->type = elem; + operand->mode = Addressing_Value; + break; + } + + case BuiltinProc_atomic_cxchg: + case BuiltinProc_atomic_cxchg_acq: + case BuiltinProc_atomic_cxchg_rel: + case BuiltinProc_atomic_cxchg_acqrel: + case BuiltinProc_atomic_cxchg_relaxed: + case BuiltinProc_atomic_cxchg_failrelaxed: + case BuiltinProc_atomic_cxchg_failacq: + case BuiltinProc_atomic_cxchg_acq_failrelaxed: + case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: + + case BuiltinProc_atomic_cxchgweak: + case BuiltinProc_atomic_cxchgweak_acq: + case BuiltinProc_atomic_cxchgweak_rel: + case BuiltinProc_atomic_cxchgweak_acqrel: + case BuiltinProc_atomic_cxchgweak_relaxed: + case BuiltinProc_atomic_cxchgweak_failrelaxed: + case BuiltinProc_atomic_cxchgweak_failacq: + case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: + case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + Operand y = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_expr_with_type_hint(c, &y, ce->args[2], elem); + check_assignment(c, &x, elem, builtin_name); + check_assignment(c, &y, elem, builtin_name); + + operand->mode = Addressing_Value; + operand->type = make_optional_ok_type(elem, /*typed*/false); + break; + } + break; + + case BuiltinProc_fixed_point_mul: + case BuiltinProc_fixed_point_div: + case BuiltinProc_fixed_point_mul_sat: + case BuiltinProc_fixed_point_div_sat: + { + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + + Operand x = {}; + Operand y = {}; + Operand z = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &y, ce->args[1]); + if (y.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &x, y.type); + if (x.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &y, x.type); + if (x.mode == Addressing_Invalid) { + return false; + } + if (!are_types_identical(x.type, y.type)) { + gbString xts = type_to_string(x.type); + gbString yts = type_to_string(y.type); + error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts); + gb_string_free(yts); + gb_string_free(xts); + return false; + } + + if (!is_type_integer(x.type) || is_type_untyped(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + + check_expr(c, &z, ce->args[2]); + if (z.mode == Addressing_Invalid) { + return false; + } + if (z.mode != Addressing_Constant || !is_type_integer(z.type)) { + error(z.expr, "Expected a constant integer for the scale in '%.*s'", LIT(builtin_procs[id].name)); + return false; + } + i64 n = exact_value_to_i64(z.value); + if (n <= 0) { + error(z.expr, "Scale parameter in '%.*s' must be positive, got %lld", LIT(builtin_procs[id].name), n); + return false; + } + i64 sz = 8*type_size_of(x.type); + if (n > sz) { + error(z.expr, "Scale parameter in '%.*s' is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_procs[id].name), n, sz); + return false; + } + + operand->type = x.type; + operand->mode = Addressing_Value; + } + break; + + + case BuiltinProc_expect: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[0]); + check_expr(c, &y, ce->args[1]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (y.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &y, x.type); + convert_to_typed(c, &x, y.type); + if (!are_types_identical(x.type, y.type)) { + gbString xts = type_to_string(x.type); + gbString yts = type_to_string(y.type); + error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts); + gb_string_free(yts); + gb_string_free(xts); + *operand = x; // minimize error propagation + return true; + } + + if (!is_type_integer_like(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + *operand = x; + return true; + } + + if (y.mode != Addressing_Constant) { + error(y.expr, "Second argument to '%.*s' must be constant as it is the expected value", LIT(builtin_procs[id].name)); + } + + if (x.mode == Addressing_Constant) { + // NOTE(bill): just completely ignore this intrinsic entirely + *operand = x; + return true; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + } + break; + + + + + case BuiltinProc_type_base_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + operand->type = base_type(operand->type); + } + operand->mode = Addressing_Type; + break; + case BuiltinProc_type_core_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + operand->type = core_type(operand->type); + } + operand->mode = Addressing_Type; + break; + case BuiltinProc_type_elem_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + switch (bt->kind) { + case Type_Basic: + switch (bt->Basic.kind) { + case Basic_complex64: operand->type = t_f32; break; + case Basic_complex128: operand->type = t_f64; break; + case Basic_quaternion128: operand->type = t_f32; break; + case Basic_quaternion256: operand->type = t_f64; break; + } + break; + case Type_Pointer: operand->type = bt->Pointer.elem; break; + case Type_Array: operand->type = bt->Array.elem; break; + case Type_EnumeratedArray: operand->type = bt->EnumeratedArray.elem; break; + case Type_Slice: operand->type = bt->Slice.elem; break; + case Type_DynamicArray: operand->type = bt->DynamicArray.elem; break; + } + } + operand->mode = Addressing_Type; + break; + + + case BuiltinProc_type_is_boolean: + case BuiltinProc_type_is_integer: + case BuiltinProc_type_is_rune: + case BuiltinProc_type_is_float: + case BuiltinProc_type_is_complex: + case BuiltinProc_type_is_quaternion: + case BuiltinProc_type_is_string: + case BuiltinProc_type_is_typeid: + case BuiltinProc_type_is_any: + case BuiltinProc_type_is_endian_little: + case BuiltinProc_type_is_endian_big: + case BuiltinProc_type_is_unsigned: + case BuiltinProc_type_is_numeric: + case BuiltinProc_type_is_ordered: + case BuiltinProc_type_is_ordered_numeric: + 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_dereferenceable: + case BuiltinProc_type_is_valid_map_key: + case BuiltinProc_type_is_named: + case BuiltinProc_type_is_pointer: + case BuiltinProc_type_is_array: + case BuiltinProc_type_is_slice: + case BuiltinProc_type_is_dynamic_array: + case BuiltinProc_type_is_map: + case BuiltinProc_type_is_struct: + case BuiltinProc_type_is_union: + case BuiltinProc_type_is_enum: + case BuiltinProc_type_is_proc: + case BuiltinProc_type_is_bit_field: + case BuiltinProc_type_is_bit_field_value: + case BuiltinProc_type_is_bit_set: + case BuiltinProc_type_is_simd_vector: + case BuiltinProc_type_is_specialized_polymorphic_record: + case BuiltinProc_type_is_unspecialized_polymorphic_record: + case BuiltinProc_type_has_nil: + GB_ASSERT(BuiltinProc__type_simple_boolean_begin < id && id < BuiltinProc__type_simple_boolean_end); + + operand->value = exact_value_bool(false); + if (operand->mode != Addressing_Type) { + gbString str = expr_to_string(ce->args[0]); + error(operand->expr, "Expected a type for '%.*s', got '%s'", LIT(builtin_name), str); + gb_string_free(str); + } else { + i32 i = id - cast(i32)BuiltinProc__type_simple_boolean_begin; + auto procedure = builtin_type_is_procs[i]; + GB_ASSERT_MSG(procedure != nullptr, "%.*s", LIT(builtin_name)); + bool ok = procedure(operand->type); + operand->value = exact_value_bool(ok); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + + case BuiltinProc_type_has_field: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + 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"); + return false; + } + + String field_name = x.value.value_string; + + Selection sel = lookup_field(type, field_name, false); + operand->mode = Addressing_Constant; + operand->value = exact_value_bool(sel.index.count != 0); + operand->type = t_untyped_bool; + + break; + } + break; + + case BuiltinProc_type_is_specialization_of: + { + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + Type *t = operand->type; + Type *s = nullptr; + + bool prev_ips = c->in_polymorphic_specialization; + c->in_polymorphic_specialization = true; + s = check_type(c, ce->args[1]); + c->in_polymorphic_specialization = prev_ips; + + if (s == t_invalid) { + error(ce->args[1], "Invalid specialization type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + operand->value = exact_value_bool(check_type_specialization_to(c, s, t, false, false)); + + } + break; + + case BuiltinProc_type_struct_field_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a struct type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_struct(operand->type)) { + error(operand->expr, "Expected a struct type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Struct.fields.count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; + + case BuiltinProc_type_proc_parameter_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Proc.param_count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; + case BuiltinProc_type_proc_return_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Proc.result_count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; + + case BuiltinProc_type_proc_parameter_type: + if (operand->mode != Addressing_Type || !is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + return false; + } else { + if (is_type_polymorphic(operand->type)) { + error(operand->expr, "Expected a non-polymorphic procedure type for '%.*s'", LIT(builtin_name)); + return false; + } + + Operand op = {}; + check_expr(c, &op, ce->args[1]); + if (op.mode != Addressing_Constant && !is_type_integer(op.type)) { + error(op.expr, "Expected a constant integer for the index of procedure parameter value"); + return false; + } + + i64 index = exact_value_to_i64(op.value); + if (index < 0) { + error(op.expr, "Expected a non-negative integer for the index of procedure parameter value, got %lld", cast(long long)index); + return false; + } + + Entity *param = nullptr; + i64 count = 0; + + Type *bt = base_type(operand->type); + if (bt->kind == Type_Proc) { + count = bt->Proc.param_count; + if (index < count) { + param = bt->Proc.params->Tuple.variables[index]; + } + } + + if (index >= count) { + error(op.expr, "Index of procedure parameter value out of bounds, expected 0..<%lld, got %lld", cast(long long)count, cast(long long)index); + return false; + } + GB_ASSERT(param != nullptr); + switch (param->kind) { + case Entity_Constant: + operand->mode = Addressing_Constant; + operand->type = param->type; + operand->value = param->Constant.value; + break; + case Entity_TypeName: + case Entity_Variable: + operand->mode = Addressing_Type; + operand->type = param->type; + break; + default: + GB_PANIC("Unhandled procedure entity type %d", param->kind); + break; + } + + } + + break; + + case BuiltinProc_type_proc_return_type: + if (operand->mode != Addressing_Type || !is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + return false; + } else { + if (is_type_polymorphic(operand->type)) { + error(operand->expr, "Expected a non-polymorphic procedure type for '%.*s'", LIT(builtin_name)); + return false; + } + + Operand op = {}; + check_expr(c, &op, ce->args[1]); + if (op.mode != Addressing_Constant && !is_type_integer(op.type)) { + error(op.expr, "Expected a constant integer for the index of procedure parameter value"); + return false; + } + + i64 index = exact_value_to_i64(op.value); + if (index < 0) { + error(op.expr, "Expected a non-negative integer for the index of procedure parameter value, got %lld", cast(long long)index); + return false; + } + + Entity *param = nullptr; + i64 count = 0; + + Type *bt = base_type(operand->type); + if (bt->kind == Type_Proc) { + count = bt->Proc.result_count; + if (index < count) { + param = bt->Proc.results->Tuple.variables[index]; + } + } + + if (index >= count) { + error(op.expr, "Index of procedure parameter value out of bounds, expected 0..<%lld, got %lld", cast(long long)count, cast(long long)index); + return false; + } + GB_ASSERT(param != nullptr); + switch (param->kind) { + case Entity_Constant: + operand->mode = Addressing_Constant; + operand->type = param->type; + operand->value = param->Constant.value; + break; + case Entity_TypeName: + case Entity_Variable: + operand->mode = Addressing_Type; + operand->type = param->type; + break; + default: + GB_PANIC("Unhandled procedure entity type %d", param->kind); + break; + } + + } + + break; + + case BuiltinProc_type_polymorphic_record_parameter_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + if (bt->kind == Type_Struct) { + if (bt->Struct.polymorphic_params != nullptr) { + operand->value = exact_value_i64(bt->Struct.polymorphic_params->Tuple.variables.count); + } + } else if (bt->kind == Type_Union) { + if (bt->Union.polymorphic_params != nullptr) { + operand->value = exact_value_i64(bt->Union.polymorphic_params->Tuple.variables.count); + } + } else { + error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name)); + } + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; + case BuiltinProc_type_polymorphic_record_parameter_value: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name)); + return false; + } else if (!is_type_polymorphic_record_specialized(operand->type)) { + error(operand->expr, "Expected a specialized polymorphic record type for '%.*s'", LIT(builtin_name)); + return false; + } else { + Operand op = {}; + check_expr(c, &op, ce->args[1]); + if (op.mode != Addressing_Constant && !is_type_integer(op.type)) { + error(op.expr, "Expected a constant integer for the index of record parameter value"); + return false; + } + + i64 index = exact_value_to_i64(op.value); + if (index < 0) { + error(op.expr, "Expected a non-negative integer for the index of record parameter value, got %lld", cast(long long)index); + return false; + } + + Entity *param = nullptr; + i64 count = 0; + + Type *bt = base_type(operand->type); + if (bt->kind == Type_Struct) { + if (bt->Struct.polymorphic_params != nullptr) { + count = bt->Struct.polymorphic_params->Tuple.variables.count; + if (index < count) { + param = bt->Struct.polymorphic_params->Tuple.variables[cast(isize)index]; + } + } + } else if (bt->kind == Type_Union) { + if (bt->Union.polymorphic_params != nullptr) { + count = bt->Union.polymorphic_params->Tuple.variables.count; + if (index < count) { + param = bt->Union.polymorphic_params->Tuple.variables[cast(isize)index]; + } + } + } else { + error(operand->expr, "Expected a specialized polymorphic record type for '%.*s'", LIT(builtin_name)); + return false; + } + + if (index >= count) { + error(op.expr, "Index of record parameter value out of bounds, expected 0..<%lld, got %lld", cast(long long)count, cast(long long)index); + return false; + } + GB_ASSERT(param != nullptr); + switch (param->kind) { + case Entity_Constant: + operand->mode = Addressing_Constant; + operand->type = param->type; + operand->value = param->Constant.value; + break; + case Entity_TypeName: + operand->mode = Addressing_Type; + operand->type = param->type; + break; + default: + GB_PANIC("Unhandled polymorphic record type"); + break; + } + + } + + break; + + case BuiltinProc_type_field_index_of: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + 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"); + return false; + } + + String field_name = x.value.value_string; + + Selection sel = lookup_field(type, field_name, false); + if (sel.entity == nullptr) { + gbString type_str = type_to_string(bt); + error(ce->args[0], + "'%s' has no field named '%.*s'", type_str, LIT(field_name)); + gb_string_free(type_str); + return false; + } + if (sel.indirect) { + gbString type_str = type_to_string(bt); + error(ce->args[0], + "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str); + gb_string_free(type_str); + return false; + } + + operand->mode = Addressing_Constant; + operand->value = exact_value_u64(sel.index[0]); + operand->type = t_uintptr; + break; + } + break; + + case BuiltinProc_type_equal_proc: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + if (!is_type_comparable(type)) { + gbString t = type_to_string(type); + error(ce->args[0], "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_equal_proc; + break; + } + + case BuiltinProc_type_hasher_proc: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + if (!is_type_valid_for_keys(type)) { + gbString t = type_to_string(type); + error(ce->args[0], "Expected a valid type for map keys for '%.*s', got %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + add_map_key_type_dependencies(c, type); + + operand->mode = Addressing_Value; + operand->type = t_hasher_proc; + break; + } + } + + return true; +} |