mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-30 13:00:07 +00:00
Merge tag 'dev-2025-09'
This commit is contained in:
+44
-4
@@ -352,12 +352,43 @@ u64 get_vet_flag_from_name(String const &name) {
|
||||
enum OptInFeatureFlags : u64 {
|
||||
OptInFeatureFlag_NONE = 0,
|
||||
OptInFeatureFlag_DynamicLiterals = 1u<<0,
|
||||
|
||||
OptInFeatureFlag_GlobalContext = 1u<<1,
|
||||
|
||||
OptInFeatureFlag_IntegerDivisionByZero_Trap = 1u<<2,
|
||||
OptInFeatureFlag_IntegerDivisionByZero_Zero = 1u<<3,
|
||||
OptInFeatureFlag_IntegerDivisionByZero_Self = 1u<<4,
|
||||
OptInFeatureFlag_IntegerDivisionByZero_AllBits = 1u<<5,
|
||||
|
||||
|
||||
OptInFeatureFlag_IntegerDivisionByZero_ALL = OptInFeatureFlag_IntegerDivisionByZero_Trap|
|
||||
OptInFeatureFlag_IntegerDivisionByZero_Zero|
|
||||
OptInFeatureFlag_IntegerDivisionByZero_Self|
|
||||
OptInFeatureFlag_IntegerDivisionByZero_AllBits,
|
||||
|
||||
};
|
||||
|
||||
u64 get_feature_flag_from_name(String const &name) {
|
||||
if (name == "dynamic-literals") {
|
||||
return OptInFeatureFlag_DynamicLiterals;
|
||||
}
|
||||
if (name == "integer-division-by-zero:trap") {
|
||||
return OptInFeatureFlag_IntegerDivisionByZero_Trap;
|
||||
}
|
||||
if (name == "integer-division-by-zero:zero") {
|
||||
return OptInFeatureFlag_IntegerDivisionByZero_Zero;
|
||||
}
|
||||
if (name == "integer-division-by-zero:self") {
|
||||
return OptInFeatureFlag_IntegerDivisionByZero_Self;
|
||||
}
|
||||
if (name == "integer-division-by-zero:all-bits") {
|
||||
return OptInFeatureFlag_IntegerDivisionByZero_AllBits;
|
||||
}
|
||||
|
||||
|
||||
if (name == "global-context") {
|
||||
return OptInFeatureFlag_GlobalContext;
|
||||
}
|
||||
return OptInFeatureFlag_NONE;
|
||||
}
|
||||
|
||||
@@ -404,6 +435,13 @@ String linker_choices[Linker_COUNT] = {
|
||||
str_lit("radlink"),
|
||||
};
|
||||
|
||||
enum IntegerDivisionByZeroKind : u8 {
|
||||
IntegerDivisionByZero_Trap,
|
||||
IntegerDivisionByZero_Zero,
|
||||
IntegerDivisionByZero_Self,
|
||||
IntegerDivisionByZero_AllBits,
|
||||
};
|
||||
|
||||
// This stores the information for the specify architecture of this build
|
||||
struct BuildContext {
|
||||
// Constants
|
||||
@@ -485,6 +523,8 @@ struct BuildContext {
|
||||
bool keep_object_files;
|
||||
bool disallow_do;
|
||||
|
||||
IntegerDivisionByZeroKind integer_division_by_zero_behaviour;
|
||||
|
||||
LinkerChoice linker_choice;
|
||||
|
||||
StringSet custom_attributes;
|
||||
@@ -1089,7 +1129,7 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
text = gb_alloc_array(permanent_allocator(), wchar_t, len+1);
|
||||
|
||||
GetModuleFileNameW(nullptr, text, cast(int)len);
|
||||
path = string16_to_string(heap_allocator(), make_string16(text, len));
|
||||
path = string16_to_string(heap_allocator(), make_string16(cast(u16 *)text, len));
|
||||
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path[i];
|
||||
@@ -1387,14 +1427,14 @@ gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) {
|
||||
|
||||
mutex_lock(&fullpath_mutex);
|
||||
|
||||
len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr);
|
||||
len = GetFullPathNameW(cast(wchar_t *)&string16[0], 0, nullptr, nullptr);
|
||||
if (len != 0) {
|
||||
wchar_t *text = gb_alloc_array(permanent_allocator(), wchar_t, len+1);
|
||||
GetFullPathNameW(&string16[0], len, text, nullptr);
|
||||
GetFullPathNameW(cast(wchar_t *)&string16[0], len, text, nullptr);
|
||||
mutex_unlock(&fullpath_mutex);
|
||||
|
||||
text[len] = 0;
|
||||
result = string16_to_string(a, make_string16(text, len));
|
||||
result = string16_to_string(a, make_string16(cast(u16 *)text, len));
|
||||
result = string_trim_whitespace(result);
|
||||
|
||||
// Replace Windows style separators
|
||||
|
||||
+1
-1
@@ -231,7 +231,7 @@ Array<String> cache_gather_envs() {
|
||||
|
||||
wchar_t *curr_string = strings;
|
||||
while (curr_string && *curr_string) {
|
||||
String16 wstr = make_string16_c(curr_string);
|
||||
String16 wstr = make_string16_c(cast(u16 *)curr_string);
|
||||
curr_string += wstr.len+1;
|
||||
String str = string16_to_string(temporary_allocator(), wstr);
|
||||
if (string_starts_with(str, str_lit("CURR_DATE_TIME="))) {
|
||||
|
||||
+262
-4
@@ -19,6 +19,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
|
||||
is_type_complex,
|
||||
is_type_quaternion,
|
||||
is_type_string,
|
||||
is_type_string16,
|
||||
is_type_typeid,
|
||||
is_type_any,
|
||||
is_type_endian_platform,
|
||||
@@ -456,6 +457,229 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
|
||||
return true;
|
||||
|
||||
} break;
|
||||
|
||||
case BuiltinProc_objc_block:
|
||||
{
|
||||
// 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);
|
||||
|
||||
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.
|
||||
param_operands[0] = *operand;
|
||||
|
||||
for (isize i = 0; i < ce->args.count-1; i++) {
|
||||
Operand x = {};
|
||||
check_expr(c, &x, ce->args[i]);
|
||||
|
||||
switch (x.mode) {
|
||||
case Addressing_Value:
|
||||
case Addressing_Context:
|
||||
case Addressing_Variable:
|
||||
case Addressing_Constant:
|
||||
param_operands[i] = x;
|
||||
break;
|
||||
|
||||
default:
|
||||
gbString e = expr_to_string(x.expr);
|
||||
gbString t = type_to_string(x.type);
|
||||
error(x.expr, "'%.*s' capture arguments must be values, but got %s of type %s", LIT(builtin_name), e, t);
|
||||
gb_string_free(t);
|
||||
gb_string_free(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate handler proc
|
||||
Operand handler = {};
|
||||
|
||||
if (capture_arg_count == 0) {
|
||||
// It's already been checked and assigned
|
||||
handler = param_operands[0];
|
||||
} else {
|
||||
check_expr_or_type(c, &handler, ce->args[capture_arg_count]);
|
||||
param_operands[capture_arg_count] = handler;
|
||||
}
|
||||
|
||||
if (!is_operand_value(handler) || handler.type->kind != Type_Proc) {
|
||||
gbString e = expr_to_string(handler.expr);
|
||||
gbString t = type_to_string(handler.type);
|
||||
error(handler.expr, "'%.*s' expected a procedure, but got '%s' of type %s", LIT(builtin_name), e, t);
|
||||
gb_string_free(t);
|
||||
gb_string_free(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
Ast *handler_node = unparen_expr(handler.expr);
|
||||
|
||||
// Only direct reference to procs are allowed
|
||||
switch (handler_node->kind) {
|
||||
case Ast_ProcLit: break; // ok
|
||||
case Ast_Ident: {
|
||||
auto& ident = handler_node->Ident;
|
||||
|
||||
if (ident.entity == nullptr) {
|
||||
error(handler.expr, "'%.*s' failed to resolve entity from expression", LIT(builtin_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ident.entity->kind != Entity_Procedure) {
|
||||
gbString e = expr_to_string(handler_node);
|
||||
|
||||
ERROR_BLOCK();
|
||||
error(handler.expr, "'%.*s' expected a direct reference to a procedure", LIT(builtin_name));
|
||||
if(ident.entity->kind == Entity_Variable) {
|
||||
error_line("\tSuggestion: Variables referencing a procedure are not allowed, they are not a direct procedure reference.");
|
||||
} else {
|
||||
error_line("\tSuggestion: Ensure '%s' is not a runtime-evaluated expression.", e); // NOTE(harold): Is this case possible to hit?
|
||||
}
|
||||
error_line("\n\t Refer to a procedure directly by its name or declare it anonymously: %.*s(proc(){})", LIT(builtin_name));
|
||||
|
||||
gb_string_free(e);
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
|
||||
default: {
|
||||
gbString e = expr_to_string(handler_node);
|
||||
ERROR_BLOCK();
|
||||
error(handler.expr, "'%.*s' expected a direct reference to a procedure", LIT(builtin_name));
|
||||
if( handler_node->kind == Ast_CallExpr) {
|
||||
error_line("\tSuggestion: Do not use a procedure returned from another procedure.");
|
||||
} else {
|
||||
error_line("\tSuggestion: Ensure '%s' is not a runtime-evaluated expression.", e);
|
||||
}
|
||||
error_line("\n\t Refer to a procedure directly by its name or declare it anonymously: %.*s(proc(){})", LIT(builtin_name));
|
||||
|
||||
gb_string_free(e);
|
||||
} return false;
|
||||
} // End switch
|
||||
|
||||
auto& handler_type_proc = handler.type->Proc;
|
||||
|
||||
if (capture_arg_count > handler_type_proc.param_count) {
|
||||
error(handler.expr, "'%.*s' captured arguments exceeded the handler's parameter count", LIT(builtin_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the handler proc is odin calling convention, but there must be a context defined in this scope.
|
||||
if (handler_type_proc.calling_convention == ProcCC_Odin) {
|
||||
if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
|
||||
ERROR_BLOCK();
|
||||
error(handler.expr, "The handler procedure for '%.*s' requires a context, but no context is defined in the current scope", LIT(builtin_name));
|
||||
error_line("\tSuggestion: 'context = runtime.default_context()', or use the \"c\" calling convention for the handler procedure");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// At most a single return value is supported
|
||||
if (handler_type_proc.result_count > 1) {
|
||||
error(handler_type_proc.node->ProcType.results, "Handler procedures for '%.*s' cannot have multiple return values", LIT(builtin_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that captured args are assignable to the handler's corresponding capture params
|
||||
if (handler_type_proc.param_count > 0) {
|
||||
auto& handler_param_types = handler.type->Proc.params->Tuple.variables;
|
||||
Slice<Entity *> handler_capture_param_types = slice(handler_param_types, handler_param_types.count - capture_arg_count, handler_param_types.count);
|
||||
|
||||
for (isize i = 0; i < capture_arg_count; i++) {
|
||||
Operand op = param_operands[i];
|
||||
if (!check_is_assignable_to(c, &op, handler_capture_param_types[i]->type)) {
|
||||
gbString e = expr_to_string(op.expr);
|
||||
gbString src = type_to_string(op.type);
|
||||
gbString dst = type_to_string(handler_capture_param_types[i]->type);
|
||||
error(op.expr, "'%.*s' captured value '%s' of type '%s' is not assignable to type '%s'", LIT(builtin_name), e, src, dst);
|
||||
gb_string_free(e);
|
||||
gb_string_free(src);
|
||||
gb_string_free(dst);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProcCallingConvention cc = handler_type_proc.calling_convention;
|
||||
switch (cc) {
|
||||
case ProcCC_Odin:
|
||||
case ProcCC_Contextless:
|
||||
case ProcCC_CDecl:
|
||||
break; // ok
|
||||
default:
|
||||
ERROR_BLOCK();
|
||||
|
||||
error(handler.expr, "'%.*s' Invalid calling convention for block procedure.", LIT(builtin_name));
|
||||
error_line("\tSuggestion: Do not specify a calling convention ot else use \"c\" or \"cotextless\"");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (handler_type_proc.is_polymorphic) {
|
||||
error(handler.expr, "'%.*s' Unspecialized polymorphic procedures are not allowed.", LIT(builtin_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the specialized Objc_Block type that this intrinsic will return
|
||||
Token ident = {};
|
||||
ident.kind = Token_Ident;
|
||||
ident.string = str_lit("Objc_Block");
|
||||
ident.pos = ast_token(call).pos;
|
||||
|
||||
Token l_paren = {};
|
||||
l_paren.kind = Token_OpenParen;
|
||||
l_paren.string = str_lit("(");
|
||||
l_paren.pos = ident.pos;
|
||||
|
||||
Token r_paren = {};
|
||||
r_paren.kind = Token_CloseParen;
|
||||
l_paren.string = str_lit(")");
|
||||
r_paren.pos = ident.pos;
|
||||
|
||||
// Remove the capture args from the resulting Objc_Block type signature
|
||||
Ast* handler_proc_type_copy = clone_ast(handler_type_proc.node);
|
||||
handler_proc_type_copy->ProcType.params->FieldList.list.count -= capture_arg_count;
|
||||
|
||||
// Make sure the Objc_Block's specialized proc is always "c" calling conv,
|
||||
// even if we have a context, as the invoker is always "c".
|
||||
// This allows us to have compatibility with the target block types with either calling convention used.
|
||||
handler_proc_type_copy->ProcType.calling_convention = ProcCC_CDecl;
|
||||
|
||||
Array<Ast *> poly_args = {};
|
||||
array_init(&poly_args, permanent_allocator(), 1, 1);
|
||||
poly_args[0] = handler_proc_type_copy;
|
||||
|
||||
|
||||
Type *t_Objc_Block = find_core_type(c->checker, str_lit("Objc_Block"));
|
||||
Operand poly_op = {};
|
||||
poly_op.type = t_Objc_Block;
|
||||
poly_op.mode = Addressing_Type;
|
||||
|
||||
Ast *poly_call = ast_call_expr(nullptr, ast_ident(nullptr, ident), poly_args, l_paren, r_paren, {});
|
||||
|
||||
auto err = check_polymorphic_record_type(c, &poly_op, poly_call);
|
||||
|
||||
if (err != 0) {
|
||||
operand->mode = Addressing_Invalid;
|
||||
operand->type = t_invalid;
|
||||
error(handler.expr, "'%.*s' failed to determine resulting Objc_Block handler procedure", LIT(builtin_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
GB_ASSERT(poly_op.type != t_Objc_Block);
|
||||
GB_ASSERT(poly_op.mode == Addressing_Type);
|
||||
|
||||
bool is_global_block = capture_arg_count == 0 && handler_type_proc.calling_convention != ProcCC_Odin;
|
||||
if (is_global_block) {
|
||||
try_to_add_package_dependency(c, "runtime", "_NSConcreteGlobalBlock");
|
||||
} else {
|
||||
try_to_add_package_dependency(c, "runtime", "_NSConcreteStackBlock");
|
||||
}
|
||||
|
||||
*operand = poly_op;
|
||||
operand->type = alloc_type_pointer(operand->type);
|
||||
operand->mode = Addressing_Value;
|
||||
return true;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2291,6 +2515,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
case BuiltinProc_objc_register_selector:
|
||||
case BuiltinProc_objc_register_class:
|
||||
case BuiltinProc_objc_ivar_get:
|
||||
case BuiltinProc_objc_block:
|
||||
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
|
||||
|
||||
case BuiltinProc___entry_point:
|
||||
@@ -2329,13 +2554,23 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
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);
|
||||
|
||||
if (operand->value.kind == ExactValue_String) {
|
||||
String str = operand->value.value_string;
|
||||
value = exact_value_i64(str.len);
|
||||
} else if (operand->value.kind == ExactValue_String16) {
|
||||
String16 str = operand->value.value_string16;
|
||||
value = exact_value_i64(str.len);
|
||||
} else {
|
||||
GB_PANIC("Unhandled value kind: %d", operand->value.kind);
|
||||
}
|
||||
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_cstring16(op_type)) {
|
||||
add_package_dependency(c, "runtime", "cstring16_len");
|
||||
}
|
||||
}
|
||||
} else if (is_type_array(op_type)) {
|
||||
@@ -4685,7 +4920,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
break;
|
||||
case Type_Basic:
|
||||
if (t->Basic.kind == Basic_string) {
|
||||
operand->type = alloc_type_multi_pointer(t_u8);
|
||||
operand->type = t_u8_multi_ptr;
|
||||
} else if (t->Basic.kind == Basic_string16) {
|
||||
operand->type = t_u16_multi_ptr;
|
||||
}
|
||||
break;
|
||||
case Type_Pointer:
|
||||
@@ -6134,6 +6371,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
case BuiltinProc_type_is_complex:
|
||||
case BuiltinProc_type_is_quaternion:
|
||||
case BuiltinProc_type_is_string:
|
||||
case BuiltinProc_type_is_string16:
|
||||
case BuiltinProc_type_is_typeid:
|
||||
case BuiltinProc_type_is_any:
|
||||
case BuiltinProc_type_is_endian_platform:
|
||||
@@ -7121,6 +7359,22 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
break;
|
||||
}
|
||||
|
||||
case BuiltinProc_type_canonical_name:
|
||||
{
|
||||
Operand op = {};
|
||||
Type *type = check_type(c, ce->args[0]);
|
||||
Type *bt = base_type(type);
|
||||
if (bt == nullptr || bt == t_invalid) {
|
||||
error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->type = t_untyped_string;
|
||||
operand->value = exact_value_string(type_to_canonical_string(permanent_allocator(), type));
|
||||
break;
|
||||
}
|
||||
|
||||
case BuiltinProc_procedure_of:
|
||||
{
|
||||
Ast *call_expr = unparen_expr(ce->args[0]);
|
||||
@@ -7173,7 +7427,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
return false;
|
||||
}
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = alloc_type_multi_pointer(t_u16);
|
||||
if (type_hint != nullptr && is_type_cstring16(type_hint)) {
|
||||
operand->type = type_hint;
|
||||
} else {
|
||||
operand->type = alloc_type_multi_pointer(t_u16);
|
||||
}
|
||||
operand->value = {};
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -602,6 +602,13 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
|
||||
} else if (ac.objc_is_implementation) {
|
||||
error(e->token, "@(objc_implement) may only be applied when the @(objc_class) attribute is also applied");
|
||||
}
|
||||
|
||||
if (ac.raddbg_type_view) {
|
||||
RaddbgTypeView type_view = {};
|
||||
type_view.type = e->type;
|
||||
type_view.view = ac.raddbg_type_view_string;
|
||||
mpsc_enqueue(&ctx->info->raddbg_type_views_queue, type_view);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -815,6 +822,12 @@ gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) {
|
||||
if (sig_compare(is_type_cstring, is_type_u8_multi_ptr, x, y)) {
|
||||
return true;
|
||||
}
|
||||
if (sig_compare(is_type_cstring16, is_type_u16_ptr, x, y)) {
|
||||
return true;
|
||||
}
|
||||
if (sig_compare(is_type_cstring16, is_type_u16_multi_ptr, x, y)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sig_compare(is_type_uintptr, is_type_rawptr, x, y)) {
|
||||
return true;
|
||||
@@ -1845,6 +1858,17 @@ gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d,
|
||||
c.scope = d->scope;
|
||||
c.decl = d;
|
||||
c.type_level = 0;
|
||||
c.curr_proc_calling_convention = ProcCC_Contextless;
|
||||
|
||||
auto prev_flags = c.scope->flags;
|
||||
defer (c.scope->flags = prev_flags);
|
||||
|
||||
if (check_feature_flags(ctx, d->decl_node) & OptInFeatureFlag_GlobalContext) {
|
||||
c.scope->flags |= ScopeFlag_ContextDefined;
|
||||
} else {
|
||||
c.scope->flags &= ~ScopeFlag_ContextDefined;
|
||||
}
|
||||
|
||||
|
||||
e->parent_proc_decl = c.curr_proc_decl;
|
||||
e->state = EntityState_InProgress;
|
||||
|
||||
+244
-21
@@ -129,6 +129,8 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
|
||||
|
||||
gb_internal bool is_exact_value_zero(ExactValue const &v);
|
||||
|
||||
gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node);
|
||||
|
||||
enum LoadDirectiveResult {
|
||||
LoadDirective_Success = 0,
|
||||
LoadDirective_Error = 1,
|
||||
@@ -2106,6 +2108,9 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
|
||||
} else if (is_type_boolean(type)) {
|
||||
return in_value.kind == ExactValue_Bool;
|
||||
} else if (is_type_string(type)) {
|
||||
if (in_value.kind == ExactValue_String16) {
|
||||
return is_type_string16(type) || is_type_cstring16(type);
|
||||
}
|
||||
return in_value.kind == ExactValue_String;
|
||||
} else if (is_type_integer(type) || is_type_rune(type)) {
|
||||
if (in_value.kind == ExactValue_Bool) {
|
||||
@@ -2320,6 +2325,9 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
|
||||
if (in_value.kind == ExactValue_String) {
|
||||
return false;
|
||||
}
|
||||
if (in_value.kind == ExactValue_String16) {
|
||||
return false;
|
||||
}
|
||||
if (out_value) *out_value = in_value;
|
||||
} else if (is_type_bit_set(type)) {
|
||||
if (in_value.kind == ExactValue_Integer) {
|
||||
@@ -2455,7 +2463,8 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o
|
||||
} else if (is_type_pointer(o->type) &&
|
||||
are_types_identical(type_deref(o->type), type)) {
|
||||
gbString s = expr_to_string(o->expr);
|
||||
error_line("\tSuggestion: Did you mean `%s^`\n", s);
|
||||
if (s[0] == '&') error_line("\tSuggestion: Did you mean `%s`\n", &s[1]);
|
||||
else error_line("\tSuggestion: Did you mean `%s^`\n", s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
}
|
||||
@@ -2862,6 +2871,14 @@ gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t
|
||||
add_package_dependency(c, "runtime", "string_eq");
|
||||
add_package_dependency(c, "runtime", "string_ne");
|
||||
break;
|
||||
case Basic_cstring16:
|
||||
add_package_dependency(c, "runtime", "cstring16_eq");
|
||||
add_package_dependency(c, "runtime", "cstring16_ne");
|
||||
break;
|
||||
case Basic_string16:
|
||||
add_package_dependency(c, "runtime", "string16_eq");
|
||||
add_package_dependency(c, "runtime", "string16_ne");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Type_Struct:
|
||||
@@ -3035,6 +3052,24 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper
|
||||
case Token_LtEq: add_package_dependency(c, "runtime", "cstring_le"); break;
|
||||
case Token_GtEq: add_package_dependency(c, "runtime", "cstring_gt"); break;
|
||||
}
|
||||
} else if (is_type_cstring16(x->type) && is_type_cstring16(y->type)) {
|
||||
switch (op) {
|
||||
case Token_CmpEq: add_package_dependency(c, "runtime", "cstring16_eq"); break;
|
||||
case Token_NotEq: add_package_dependency(c, "runtime", "cstring16_ne"); break;
|
||||
case Token_Lt: add_package_dependency(c, "runtime", "cstring16_lt"); break;
|
||||
case Token_Gt: add_package_dependency(c, "runtime", "cstring16_gt"); break;
|
||||
case Token_LtEq: add_package_dependency(c, "runtime", "cstring16_le"); break;
|
||||
case Token_GtEq: add_package_dependency(c, "runtime", "cstring16_gt"); break;
|
||||
}
|
||||
} else if (is_type_string16(x->type) || is_type_string16(y->type)) {
|
||||
switch (op) {
|
||||
case Token_CmpEq: add_package_dependency(c, "runtime", "string16_eq"); break;
|
||||
case Token_NotEq: add_package_dependency(c, "runtime", "string16_ne"); break;
|
||||
case Token_Lt: add_package_dependency(c, "runtime", "string16_lt"); break;
|
||||
case Token_Gt: add_package_dependency(c, "runtime", "string16_gt"); break;
|
||||
case Token_LtEq: add_package_dependency(c, "runtime", "string16_le"); break;
|
||||
case Token_GtEq: add_package_dependency(c, "runtime", "string16_gt"); break;
|
||||
}
|
||||
} else if (is_type_string(x->type) || is_type_string(y->type)) {
|
||||
switch (op) {
|
||||
case Token_CmpEq: add_package_dependency(c, "runtime", "string_eq"); break;
|
||||
@@ -3340,6 +3375,11 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
|
||||
return true;
|
||||
}
|
||||
|
||||
// []u16 <-> string16 (not cstring16)
|
||||
if (is_type_u16_slice(src) && (is_type_string16(dst) && !is_type_cstring16(dst))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// cstring -> string
|
||||
if (are_types_identical(src, t_cstring) && are_types_identical(dst, t_string)) {
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
@@ -3347,6 +3387,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// cstring16 -> string16
|
||||
if (are_types_identical(src, t_cstring16) && are_types_identical(dst, t_string16)) {
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
add_package_dependency(c, "runtime", "cstring16_to_string16");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// cstring -> ^u8
|
||||
if (are_types_identical(src, t_cstring) && is_type_u8_ptr(dst)) {
|
||||
return !is_constant;
|
||||
@@ -3372,6 +3420,34 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
|
||||
if (is_type_rawptr(src) && are_types_identical(dst, t_cstring)) {
|
||||
return !is_constant;
|
||||
}
|
||||
|
||||
// cstring -> ^u16
|
||||
if (are_types_identical(src, t_cstring16) && is_type_u16_ptr(dst)) {
|
||||
return !is_constant;
|
||||
}
|
||||
// cstring -> [^]u16
|
||||
if (are_types_identical(src, t_cstring16) && is_type_u16_multi_ptr(dst)) {
|
||||
return !is_constant;
|
||||
}
|
||||
// cstring16 -> rawptr
|
||||
if (are_types_identical(src, t_cstring16) && is_type_rawptr(dst)) {
|
||||
return !is_constant;
|
||||
}
|
||||
|
||||
|
||||
// ^u16 -> cstring16
|
||||
if (is_type_u16_ptr(src) && are_types_identical(dst, t_cstring16)) {
|
||||
return !is_constant;
|
||||
}
|
||||
// [^]u16 -> cstring
|
||||
if (is_type_u16_multi_ptr(src) && are_types_identical(dst, t_cstring16)) {
|
||||
return !is_constant;
|
||||
}
|
||||
// rawptr -> cstring16
|
||||
if (is_type_rawptr(src) && are_types_identical(dst, t_cstring16)) {
|
||||
return !is_constant;
|
||||
}
|
||||
|
||||
// proc <-> proc
|
||||
if (is_type_proc(src) && is_type_proc(dst)) {
|
||||
if (is_type_polymorphic(dst)) {
|
||||
@@ -4235,7 +4311,25 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
error(y->expr, "Division by zero not allowed");
|
||||
if (is_type_integer(x->type) || (x->mode == Addressing_Constant && x->value.kind == ExactValue_Integer)) {
|
||||
if (check_for_integer_division_by_zero(c, node) != IntegerDivisionByZero_Trap) {
|
||||
// Okay
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (op.kind) {
|
||||
case Token_Mod:
|
||||
case Token_ModMod:
|
||||
case Token_ModEq:
|
||||
case Token_ModModEq:
|
||||
error(y->expr, "Division by zero through '%.*s' not allowed", LIT(token_strings[op.kind]));
|
||||
break;
|
||||
case Token_Quo:
|
||||
case Token_QuoEq:
|
||||
error(y->expr, "Division by zero not allowed");
|
||||
break;
|
||||
}
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
@@ -4275,7 +4369,59 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
|
||||
}
|
||||
}
|
||||
|
||||
x->value = exact_binary_operator_value(op.kind, a, b);
|
||||
match_exact_values(&a, &b);
|
||||
|
||||
|
||||
IntegerDivisionByZeroKind zero_behaviour = check_for_integer_division_by_zero(c, node);
|
||||
if (zero_behaviour != IntegerDivisionByZero_Trap &&
|
||||
b.kind == ExactValue_Integer && big_int_is_zero(&b.value_integer) &&
|
||||
(op.kind == Token_QuoEq || op.kind == Token_Mod || op.kind == Token_ModMod)) {
|
||||
if (op.kind == Token_QuoEq) {
|
||||
switch (zero_behaviour) {
|
||||
case IntegerDivisionByZero_Zero:
|
||||
// x/0 == 0
|
||||
x->value = b;
|
||||
break;
|
||||
case IntegerDivisionByZero_Self:
|
||||
// x/0 == x
|
||||
x->value = a;
|
||||
break;
|
||||
case IntegerDivisionByZero_AllBits:
|
||||
// x/0 == 0b111...111
|
||||
if (is_type_untyped(x->type)) {
|
||||
x->value = exact_value_i64(-1);
|
||||
} else {
|
||||
x->value = exact_unary_operator_value(Token_Xor, b, cast(i32)(8*type_size_of(x->type)), is_type_unsigned(x->type));
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
NOTE(bill): @integer division by zero rules
|
||||
|
||||
truncated: r = a - b*trunc(a/b)
|
||||
floored: r = a - b*floor(a/b)
|
||||
|
||||
IFF a/0 == 0, then (a%0 == a) or (a%%0 == a)
|
||||
IFF a/0 == a, then (a%0 == 0) or (a%%0 == 0)
|
||||
IFF a/0 == 0b111..., then (a%0 == a) or (a%%0 == a)
|
||||
*/
|
||||
|
||||
switch (zero_behaviour) {
|
||||
case IntegerDivisionByZero_Zero:
|
||||
case IntegerDivisionByZero_AllBits:
|
||||
// x%0 == x
|
||||
x->value = a;
|
||||
break;
|
||||
case IntegerDivisionByZero_Self:
|
||||
// x%0 == 0
|
||||
x->value = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
x->value = exact_binary_operator_value(op.kind, a, b);
|
||||
}
|
||||
|
||||
if (is_type_typed(x->type)) {
|
||||
if (node != nullptr) {
|
||||
@@ -4558,6 +4704,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
|
||||
// target_type = t_untyped_nil;
|
||||
} else if (is_type_cstring(target_type)) {
|
||||
// target_type = t_untyped_nil;
|
||||
} else if (is_type_cstring16(target_type)) {
|
||||
// target_type = t_untyped_nil;
|
||||
} else if (!type_has_nil(target_type)) {
|
||||
operand->mode = Addressing_Invalid;
|
||||
convert_untyped_error(c, operand, target_type);
|
||||
@@ -4585,6 +4733,13 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (operand->value.kind == ExactValue_String16) {
|
||||
String16 s = operand->value.value_string16;
|
||||
if (is_type_u16_array(t)) {
|
||||
if (s.len == t->Array.count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
operand->mode = Addressing_Invalid;
|
||||
convert_untyped_error(c, operand, target_type);
|
||||
@@ -4914,6 +5069,12 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
|
||||
if (success_) *success_ = true;
|
||||
if (finish_) *finish_ = true;
|
||||
return exact_value_u64(val);
|
||||
} else if (value.kind == ExactValue_String16) {
|
||||
GB_ASSERT(0 <= index && index < value.value_string.len);
|
||||
u16 val = value.value_string16[index];
|
||||
if (success_) *success_ = true;
|
||||
if (finish_) *finish_ = true;
|
||||
return exact_value_u64(val);
|
||||
}
|
||||
if (value.kind != ExactValue_Compound) {
|
||||
if (success_) *success_ = true;
|
||||
@@ -6058,7 +6219,8 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
Entity *entity, Type *proc_type,
|
||||
Array<Operand> positional_operands, Array<Operand> const &named_operands,
|
||||
CallArgumentErrorMode show_error_mode,
|
||||
CallArgumentData *data) {
|
||||
CallArgumentData *data,
|
||||
bool checking_proc_group) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
CallArgumentError err = CallArgumentError_None;
|
||||
@@ -6225,7 +6387,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
bool context_allocator_error = false;
|
||||
if (e->kind == Entity_Variable) {
|
||||
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
|
||||
if (ast_file_vet_explicit_allocators(c->file)) {
|
||||
if (ast_file_vet_explicit_allocators(c->file) && !checking_proc_group) {
|
||||
// NOTE(lucas): check if we are trying to default to context.allocator or context.temp_allocator
|
||||
if (e->Variable.param_value.original_ast_expr->kind == Ast_SelectorExpr) {
|
||||
auto& expr = e->Variable.param_value.original_ast_expr->SelectorExpr.expr;
|
||||
@@ -6310,6 +6472,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
}
|
||||
}
|
||||
|
||||
if (e && e->kind == Entity_Constant && is_type_proc(e->type)) {
|
||||
if (o->mode != Addressing_Constant) {
|
||||
if (show_error) {
|
||||
error(o->expr, "Expected a constant procedure value for the argument '%.*s'", LIT(e->token.string));
|
||||
}
|
||||
err = CallArgumentError_NoneConstantParameter;
|
||||
}
|
||||
}
|
||||
|
||||
if (!err && is_type_any(param_type)) {
|
||||
add_type_info_type(c, o->type);
|
||||
@@ -6652,7 +6822,8 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera
|
||||
Entity *e, Type *proc_type,
|
||||
Array<Operand> const &positional_operands, Array<Operand> const &named_operands,
|
||||
CallArgumentErrorMode show_error_mode,
|
||||
CallArgumentData *data) {
|
||||
CallArgumentData *data,
|
||||
bool checking_proc_group) {
|
||||
|
||||
bool return_on_failure = show_error_mode == CallArgumentErrorMode::NoErrors;
|
||||
|
||||
@@ -6676,7 +6847,7 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera
|
||||
}
|
||||
GB_ASSERT(proc_type->kind == Type_Proc);
|
||||
|
||||
CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data);
|
||||
CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data, checking_proc_group);
|
||||
if (return_on_failure && err != CallArgumentError_None) {
|
||||
return false;
|
||||
}
|
||||
@@ -6830,7 +7001,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
e, e->type,
|
||||
positional_operands, named_operands,
|
||||
CallArgumentErrorMode::ShowErrors,
|
||||
&data);
|
||||
&data, false);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@@ -6955,6 +7126,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
gbString expr_name = expr_to_string(operand->expr);
|
||||
defer (gb_string_free(expr_name));
|
||||
|
||||
c->in_proc_group = true;
|
||||
for_array(i, procs) {
|
||||
Entity *p = procs[i];
|
||||
if (p->flags & EntityFlag_Disabled) {
|
||||
@@ -6974,7 +7146,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
p, pt,
|
||||
positional_operands, named_operands,
|
||||
CallArgumentErrorMode::NoErrors,
|
||||
&data);
|
||||
&data, true);
|
||||
if (!is_a_candidate) {
|
||||
continue;
|
||||
}
|
||||
@@ -6997,6 +7169,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
array_add(&valids, item);
|
||||
}
|
||||
}
|
||||
c->in_proc_group = false;
|
||||
|
||||
if (max_matched_features > 0) {
|
||||
for_array(i, valids) {
|
||||
@@ -7283,7 +7456,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
e, e->type,
|
||||
positional_operands, named_operands,
|
||||
CallArgumentErrorMode::ShowErrors,
|
||||
&data);
|
||||
&data, false);
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -7396,7 +7569,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
|
||||
nullptr, proc_type,
|
||||
positional_operands, named_operands,
|
||||
CallArgumentErrorMode::ShowErrors,
|
||||
&data);
|
||||
&data, false);
|
||||
} else if (pt) {
|
||||
data.result_type = pt->results;
|
||||
}
|
||||
@@ -7774,7 +7947,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
|
||||
s = gb_string_append_fmt(s, "$%.*s", LIT(name));
|
||||
|
||||
if (v->kind == Entity_TypeName) {
|
||||
if (v->type->kind != Type_Generic) {
|
||||
if (v->type != nullptr && v->type->kind != Type_Generic) {
|
||||
s = gb_string_append_fmt(s, "=");
|
||||
s = write_type_to_string(s, v->type, false);
|
||||
}
|
||||
@@ -8078,8 +8251,12 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
|
||||
if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) {
|
||||
if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
|
||||
ERROR_BLOCK();
|
||||
error(call, "'context' has not been defined within this scope, but is required for this procedure call");
|
||||
error_line("\tSuggestion: 'context = runtime.default_context()'");
|
||||
if (c->scope->flags & ScopeFlag_File) {
|
||||
error(call, "Procedures requiring a 'context' cannot be called at the global scope");
|
||||
} else {
|
||||
error(call, "'context' has not been defined within this scope, but is required for this procedure call");
|
||||
error_line("\tSuggestion: 'context = runtime.default_context()'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8226,6 +8403,7 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
|
||||
case Type_Basic:
|
||||
if (t->Basic.kind == Basic_string) {
|
||||
if (o->mode == Addressing_Constant) {
|
||||
GB_ASSERT(o->value.kind == ExactValue_String);
|
||||
*max_count = o->value.value_string.len;
|
||||
}
|
||||
if (o->mode != Addressing_Constant) {
|
||||
@@ -8233,6 +8411,16 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
|
||||
}
|
||||
o->type = t_u8;
|
||||
return true;
|
||||
} else if (t->Basic.kind == Basic_string16) {
|
||||
if (o->mode == Addressing_Constant) {
|
||||
GB_ASSERT(o->value.kind == ExactValue_String16);
|
||||
*max_count = o->value.value_string16.len;
|
||||
}
|
||||
if (o->mode != Addressing_Constant) {
|
||||
o->mode = Addressing_Value;
|
||||
}
|
||||
o->type = t_u16;
|
||||
return true;
|
||||
} else if (t->Basic.kind == Basic_UntypedString) {
|
||||
if (o->mode == Addressing_Constant) {
|
||||
*max_count = o->value.value_string.len;
|
||||
@@ -9496,6 +9684,24 @@ gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCom
|
||||
return cl->elems.count > 0;
|
||||
}
|
||||
|
||||
gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node) {
|
||||
// TODO(bill): per file `#+feature` flags
|
||||
u64 flags = check_feature_flags(c, node);
|
||||
if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) != 0) {
|
||||
return IntegerDivisionByZero_Trap;
|
||||
}
|
||||
if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) != 0) {
|
||||
return IntegerDivisionByZero_Zero;
|
||||
}
|
||||
if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Self) != 0) {
|
||||
return IntegerDivisionByZero_Self;
|
||||
}
|
||||
if ((flags & OptInFeatureFlag_IntegerDivisionByZero_AllBits) != 0) {
|
||||
return IntegerDivisionByZero_AllBits;
|
||||
}
|
||||
return build_context.integer_division_by_zero_behaviour;
|
||||
}
|
||||
|
||||
gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
|
||||
ExprKind kind = Expr_Expr;
|
||||
ast_node(cl, CompoundLit, node);
|
||||
@@ -10879,9 +11085,17 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
|
||||
if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
|
||||
valid = true;
|
||||
if (o->mode == Addressing_Constant) {
|
||||
GB_ASSERT(o->value.kind == ExactValue_String);
|
||||
max_count = o->value.value_string.len;
|
||||
}
|
||||
o->type = type_deref(o->type);
|
||||
} else if (t->Basic.kind == Basic_string16) {
|
||||
valid = true;
|
||||
if (o->mode == Addressing_Constant) {
|
||||
GB_ASSERT(o->value.kind == ExactValue_String16);
|
||||
max_count = o->value.value_string16.len;
|
||||
}
|
||||
o->type = type_deref(o->type);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -11036,15 +11250,21 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
|
||||
o->expr = node;
|
||||
return kind;
|
||||
}
|
||||
|
||||
String s = {};
|
||||
if (o->value.kind == ExactValue_String) {
|
||||
s = o->value.value_string;
|
||||
}
|
||||
|
||||
o->mode = Addressing_Constant;
|
||||
o->type = t;
|
||||
o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
|
||||
|
||||
if (o->value.kind == ExactValue_String16) {
|
||||
String16 s = o->value.value_string16;
|
||||
|
||||
o->value = exact_value_string16(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
|
||||
} else {
|
||||
String s = {};
|
||||
if (o->value.kind == ExactValue_String) {
|
||||
s = o->value.value_string;
|
||||
}
|
||||
|
||||
o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
|
||||
}
|
||||
}
|
||||
return kind;
|
||||
}
|
||||
@@ -11133,6 +11353,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
|
||||
Type *t = t_invalid;
|
||||
switch (node->tav.value.kind) {
|
||||
case ExactValue_String: t = t_untyped_string; break;
|
||||
case ExactValue_String16: t = t_string16; break; // TODO(bill): determine this correctly
|
||||
case ExactValue_Float: t = t_untyped_float; break;
|
||||
case ExactValue_Complex: t = t_untyped_complex; break;
|
||||
case ExactValue_Quaternion: t = t_untyped_quaternion; break;
|
||||
@@ -11569,6 +11790,8 @@ gb_internal bool is_exact_value_zero(ExactValue const &v) {
|
||||
return !v.value_bool;
|
||||
case ExactValue_String:
|
||||
return v.value_string.len == 0;
|
||||
case ExactValue_String16:
|
||||
return v.value_string16.len == 0;
|
||||
case ExactValue_Integer:
|
||||
return big_int_is_zero(&v.value_integer);
|
||||
case ExactValue_Float:
|
||||
|
||||
+23
-3
@@ -974,7 +974,14 @@ gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
|
||||
Type *t = base_type(operand.type);
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
if (is_type_string(t) && t->Basic.kind != Basic_cstring) {
|
||||
if (is_type_string16(t) && t->Basic.kind != Basic_cstring) {
|
||||
val0 = t_rune;
|
||||
val1 = t_int;
|
||||
inline_for_depth = exact_value_i64(operand.value.value_string.len);
|
||||
if (unroll_count > 0) {
|
||||
error(node, "#unroll(%lld) does not support strings", cast(long long)unroll_count);
|
||||
}
|
||||
} else if (is_type_string(t) && t->Basic.kind != Basic_cstring) {
|
||||
val0 = t_rune;
|
||||
val1 = t_int;
|
||||
inline_for_depth = exact_value_i64(operand.value.value_string.len);
|
||||
@@ -1236,7 +1243,11 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
|
||||
|
||||
add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
|
||||
|
||||
if (is_type_string(x.type)) {
|
||||
if (is_type_string16(x.type)) {
|
||||
// NOTE(bill): Force dependency for strings here
|
||||
add_package_dependency(ctx, "runtime", "string16_le");
|
||||
add_package_dependency(ctx, "runtime", "string16_lt");
|
||||
} else if (is_type_string(x.type)) {
|
||||
// NOTE(bill): Force dependency for strings here
|
||||
add_package_dependency(ctx, "runtime", "string_le");
|
||||
add_package_dependency(ctx, "runtime", "string_lt");
|
||||
@@ -1770,7 +1781,16 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
|
||||
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
|
||||
if (t->Basic.kind == Basic_string16) {
|
||||
is_possibly_addressable = false;
|
||||
array_add(&vals, t_rune);
|
||||
array_add(&vals, t_int);
|
||||
if (is_reverse) {
|
||||
add_package_dependency(ctx, "runtime", "string16_decode_last_rune");
|
||||
} else {
|
||||
add_package_dependency(ctx, "runtime", "string16_decode_rune");
|
||||
}
|
||||
} else if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
|
||||
is_possibly_addressable = false;
|
||||
array_add(&vals, t_rune);
|
||||
array_add(&vals, t_int);
|
||||
|
||||
+15
-3
@@ -286,9 +286,20 @@ gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(Checker
|
||||
|
||||
gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_type, Type *original_type) {
|
||||
GB_ASSERT(is_type_named(named_type));
|
||||
GB_ASSERT(original_type->kind == Type_Named);
|
||||
gbAllocator a = heap_allocator();
|
||||
Scope *s = ctx->scope->parent;
|
||||
|
||||
AstPackage *pkg = nullptr;
|
||||
if (original_type->Named.type_name && original_type->Named.type_name->pkg) {
|
||||
pkg = original_type->Named.type_name->pkg;
|
||||
}
|
||||
|
||||
if (pkg == nullptr) {
|
||||
// NOTE(bill): if the `pkg` cannot be determined, default to the current context's pkg instead
|
||||
pkg = ctx->pkg;
|
||||
}
|
||||
|
||||
Entity *e = nullptr;
|
||||
{
|
||||
Token token = ast_token(node);
|
||||
@@ -300,12 +311,11 @@ gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, T
|
||||
e = alloc_entity_type_name(s, token, named_type);
|
||||
e->state = EntityState_Resolved;
|
||||
e->file = ctx->file;
|
||||
e->pkg = ctx->pkg;
|
||||
e->pkg = pkg;
|
||||
add_entity_use(ctx, node, e);
|
||||
}
|
||||
|
||||
named_type->Named.type_name = e;
|
||||
GB_ASSERT(original_type->kind == Type_Named);
|
||||
e->TypeName.objc_class_name = original_type->Named.type_name->TypeName.objc_class_name;
|
||||
// TODO(bill): Is this even correct? Or should the metadata be copied?
|
||||
e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata;
|
||||
@@ -2075,7 +2085,9 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
if (op.mode == Addressing_Constant) {
|
||||
poly_const = op.value;
|
||||
} else {
|
||||
error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr));
|
||||
if (!ctx->in_proc_group) {
|
||||
error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr));
|
||||
}
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
+349
-7
@@ -565,6 +565,26 @@ gb_internal u64 check_feature_flags(CheckerContext *c, Ast *node) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
gb_internal u64 check_feature_flags(Entity *e) {
|
||||
if (e == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
AstFile *file = nullptr;
|
||||
if (e->file == nullptr) {
|
||||
file = e->file;
|
||||
}
|
||||
if (file == nullptr) {
|
||||
if (e->decl_info && e->decl_info->decl_node) {
|
||||
file = e->decl_info->decl_node->file();
|
||||
}
|
||||
}
|
||||
if (file != nullptr && file->feature_flags_set) {
|
||||
return file->feature_flags;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum VettedEntityKind {
|
||||
VettedEntity_Invalid,
|
||||
@@ -1363,13 +1383,15 @@ gb_internal void init_universal(void) {
|
||||
}
|
||||
|
||||
|
||||
t_u8_ptr = alloc_type_pointer(t_u8);
|
||||
t_u8_multi_ptr = alloc_type_multi_pointer(t_u8);
|
||||
t_int_ptr = alloc_type_pointer(t_int);
|
||||
t_i64_ptr = alloc_type_pointer(t_i64);
|
||||
t_f64_ptr = alloc_type_pointer(t_f64);
|
||||
t_u8_slice = alloc_type_slice(t_u8);
|
||||
t_string_slice = alloc_type_slice(t_string);
|
||||
t_u8_ptr = alloc_type_pointer(t_u8);
|
||||
t_u8_multi_ptr = alloc_type_multi_pointer(t_u8);
|
||||
t_u16_ptr = alloc_type_pointer(t_u16);
|
||||
t_u16_multi_ptr = alloc_type_multi_pointer(t_u16);
|
||||
t_int_ptr = alloc_type_pointer(t_int);
|
||||
t_i64_ptr = alloc_type_pointer(t_i64);
|
||||
t_f64_ptr = alloc_type_pointer(t_f64);
|
||||
t_u8_slice = alloc_type_slice(t_u8);
|
||||
t_string_slice = alloc_type_slice(t_string);
|
||||
|
||||
// intrinsics types for objective-c stuff
|
||||
{
|
||||
@@ -1429,6 +1451,9 @@ gb_internal void init_checker_info(CheckerInfo *i) {
|
||||
mpsc_init(&i->foreign_decls_to_check, a); // 1<<10);
|
||||
mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used
|
||||
|
||||
mpsc_init(&i->raddbg_type_views_queue, a);
|
||||
array_init(&i->raddbg_type_views, a);
|
||||
|
||||
string_map_init(&i->load_directory_cache);
|
||||
map_init(&i->load_directory_map);
|
||||
}
|
||||
@@ -1457,7 +1482,14 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
|
||||
mpsc_destroy(&i->foreign_imports_to_check_fullpaths);
|
||||
mpsc_destroy(&i->foreign_decls_to_check);
|
||||
|
||||
mpsc_destroy(&i->raddbg_type_views_queue);
|
||||
array_free(&i->raddbg_type_views);
|
||||
|
||||
map_destroy(&i->objc_msgSend_types);
|
||||
string_set_destroy(&i->obcj_class_name_set);
|
||||
mpsc_destroy(&i->objc_class_implementations);
|
||||
map_destroy(&i->objc_method_implementations);
|
||||
|
||||
string_map_destroy(&i->load_file_cache);
|
||||
string_map_destroy(&i->load_directory_cache);
|
||||
map_destroy(&i->load_directory_map);
|
||||
@@ -1765,6 +1797,9 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo
|
||||
}
|
||||
|
||||
expr = unparen_expr(expr);
|
||||
if (expr == nullptr) {
|
||||
break;
|
||||
};
|
||||
}
|
||||
mutex_unlock(mutex);
|
||||
}
|
||||
@@ -2669,6 +2704,15 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
|
||||
is_init = false;
|
||||
}
|
||||
|
||||
u64 feature_flags = check_feature_flags(e);
|
||||
if ((feature_flags & OptInFeatureFlag_GlobalContext) == 0) {
|
||||
if (t->Proc.calling_convention != ProcCC_Contextless) {
|
||||
ERROR_BLOCK();
|
||||
error(e->token, "@(init) procedures must be declared as \"contextless\"");
|
||||
error_line("\tSuggestion: this can be bypassed, for the time being, with '#+feature global-context'");
|
||||
}
|
||||
}
|
||||
|
||||
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
|
||||
error(e->token, "@(init) procedures must be declared at the file scope");
|
||||
is_init = false;
|
||||
@@ -2683,6 +2727,7 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
|
||||
error(e->token, "An @(init) procedure must not use a blank identifier as its name");
|
||||
}
|
||||
|
||||
|
||||
if (is_init) {
|
||||
add_dependency_to_set(c, e);
|
||||
array_add(&c->info.init_procedures, e);
|
||||
@@ -2700,6 +2745,15 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
|
||||
is_fini = false;
|
||||
}
|
||||
|
||||
u64 feature_flags = check_feature_flags(e);
|
||||
if ((feature_flags & OptInFeatureFlag_GlobalContext) == 0) {
|
||||
if (t->Proc.calling_convention != ProcCC_Contextless) {
|
||||
ERROR_BLOCK();
|
||||
error(e->token, "@(fini) procedures must be declared as \"contextless\"");
|
||||
error_line("\tSuggestion: this can be bypassed, for the time being, with '#+feature global-context'");
|
||||
}
|
||||
}
|
||||
|
||||
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
|
||||
error(e->token, "@(fini) procedures must be declared at the file scope");
|
||||
is_fini = false;
|
||||
@@ -3099,6 +3153,9 @@ gb_internal void init_core_type_info(Checker *c) {
|
||||
|
||||
GB_ASSERT(tis->fields.count == 5);
|
||||
|
||||
Entity *type_info_string_encoding_kind = find_core_entity(c, str_lit("Type_Info_String_Encoding_Kind"));
|
||||
t_type_info_string_encoding_kind = type_info_string_encoding_kind->type;
|
||||
|
||||
Entity *type_info_variant = tis->fields[4];
|
||||
Type *tiv_type = type_info_variant->type;
|
||||
GB_ASSERT(is_type_union(tiv_type));
|
||||
@@ -4018,6 +4075,21 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
|
||||
|
||||
return true;
|
||||
}
|
||||
} else if (name == "raddbg_type_view") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind == ExactValue_Invalid) {
|
||||
ac->raddbg_type_view = true;
|
||||
} else if (ev.kind == ExactValue_String) {
|
||||
ac->raddbg_type_view = true;
|
||||
ac->raddbg_type_view_string = ev.value_string;
|
||||
|
||||
if (ev.value_string.len == 0) {
|
||||
error(elem, "Expected a non-empty string for '%.*s'", LIT(name));
|
||||
}
|
||||
} else {
|
||||
error(elem, "Expected a string or no value for '%.*s'", LIT(name));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -6512,6 +6584,271 @@ gb_internal void check_deferred_procedures(Checker *c) {
|
||||
|
||||
}
|
||||
|
||||
gb_internal void handle_raddbg_type_view(Checker *c, RaddbgTypeView const &type_view) {
|
||||
auto const struct_tag_lookup = [](String tag, char const *key_c, String *value_) -> bool {
|
||||
String t = tag;
|
||||
String key = make_string_c(key_c);
|
||||
while (t.len != 0) {
|
||||
isize i = 0;
|
||||
while (i < t.len && t[i] == ' ') { // Skip whitespace
|
||||
i += 1;
|
||||
}
|
||||
t.text += i;
|
||||
t.len -= i;
|
||||
if (t.len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
while (i < t.len) {
|
||||
u8 c = t[i];
|
||||
if (c == ':' || c == '"') {
|
||||
break;
|
||||
} else if ((0 <= c && c < ' ') || (0x7f <= c && c <= 0x9f)) {
|
||||
// break if control character is found
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
break;
|
||||
}
|
||||
if (i+1 >= t.len) {
|
||||
break;
|
||||
}
|
||||
if (t[i] != ':' || t[i+1] != '"') {
|
||||
break;
|
||||
}
|
||||
String name = {t.text, i};
|
||||
t = {t.text+i+1, t.len-(i+1)};
|
||||
|
||||
i = 1;
|
||||
while (i < t.len && t[i] != '"') { // find closing quote
|
||||
if (t[i] == '\\') {
|
||||
i += 1; // Skip escaped characters
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if (i >= t.len) {
|
||||
break;
|
||||
}
|
||||
|
||||
String value = {t.text, i+1};
|
||||
t = {t.text+i+1, t.len-(i+1)};
|
||||
|
||||
if (key == name) {
|
||||
value = {value.text+1, i-1};
|
||||
value = string_trim_whitespace(value);
|
||||
if (value_) *value_ = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
auto const parse_int = [](String s, isize *offset_, u64 *result_) -> bool {
|
||||
isize offset = *offset_;
|
||||
isize new_offset = *offset_;
|
||||
|
||||
u64 result = 0;
|
||||
|
||||
while (new_offset < s.len) {
|
||||
u8 c = s[new_offset];
|
||||
if (!('0' <= c && c <= '9')) {
|
||||
break;
|
||||
}
|
||||
|
||||
new_offset += 1;
|
||||
result *= 10;
|
||||
result += u64(c)-'0';
|
||||
}
|
||||
|
||||
*offset_ = new_offset;
|
||||
*result_ = result;
|
||||
return new_offset > offset;
|
||||
};
|
||||
|
||||
Type *type = type_view.type;
|
||||
if (type == nullptr || type == t_invalid) {
|
||||
return;
|
||||
}
|
||||
String view = type_view.view;
|
||||
if (view.len != 0) {
|
||||
array_add(&c->info.raddbg_type_views, RaddbgTypeView{type, view});
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE(bill): Generate one automatically from the struct field tags if they exist
|
||||
// If it cannot be generated, it'll be ignored/err
|
||||
|
||||
Type *bt = base_type(type);
|
||||
if (is_type_struct(type)) {
|
||||
GB_ASSERT(bt->kind == Type_Struct);
|
||||
if (bt->Struct.tags != nullptr) {
|
||||
bool found_any = false;
|
||||
|
||||
for (isize i = 0; i < bt->Struct.fields.count; i++) {
|
||||
String tag = bt->Struct.tags[i];
|
||||
String value = {};
|
||||
if (struct_tag_lookup(tag, "raddbg", &value)) {
|
||||
found_any = true;
|
||||
} else if (struct_tag_lookup(tag, "fmt", &value)) {
|
||||
found_any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_any) {
|
||||
return;
|
||||
}
|
||||
|
||||
gbString s = gb_string_make(heap_allocator(), "");
|
||||
|
||||
s = gb_string_appendc(s, "rows($");
|
||||
|
||||
for (isize i = 0; i < bt->Struct.fields.count; i++) {
|
||||
Entity *field = bt->Struct.fields[i];
|
||||
GB_ASSERT(field != nullptr);
|
||||
String name = field->token.string;
|
||||
String tag = bt->Struct.tags[i];
|
||||
String value = {};
|
||||
bool custom_rule = false;
|
||||
|
||||
bool raddbg_seen = false;
|
||||
if (struct_tag_lookup(tag, "raddbg", &value)) {
|
||||
raddbg_seen = true;
|
||||
if (value == "-") {
|
||||
// Ignore this field entirely;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
s = gb_string_appendc(s, ", ");
|
||||
|
||||
if (raddbg_seen) {
|
||||
if (value == "") {
|
||||
// ignore
|
||||
} else {
|
||||
s = gb_string_append_length(s, value.text, value.len);
|
||||
custom_rule = true;
|
||||
}
|
||||
} else if (struct_tag_lookup(tag, "fmt", &value)) {
|
||||
if (value == "" || value == "-") {
|
||||
// ignore
|
||||
} else {
|
||||
auto p = string_partition(value, make_string_c(","));
|
||||
String head = p.head;
|
||||
String tail = p.tail;
|
||||
|
||||
isize i = 0;
|
||||
|
||||
for (bool ok = true; ok && i < head.len; i += 1) {
|
||||
switch (head[i]) {
|
||||
case '+':
|
||||
case '-':
|
||||
case ' ':
|
||||
case '#':
|
||||
case '0':
|
||||
break;
|
||||
default:
|
||||
i -= 1;
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u64 prec = 0;
|
||||
u64 width = 0;
|
||||
bool width_ok = parse_int(head, &i, &width);
|
||||
bool prec_ok = false;
|
||||
if (i < head.len && head[i] == '.') {
|
||||
i += 1;
|
||||
prec_ok = parse_int(head, &i, &prec);
|
||||
}
|
||||
|
||||
|
||||
Rune verb = 0;
|
||||
if (i >= head.len || head[i] == ' ') {
|
||||
verb = 'v';
|
||||
} else {
|
||||
utf8_decode(head.text+i, head.len-i, &verb);
|
||||
}
|
||||
|
||||
isize paren_count = 0;
|
||||
|
||||
|
||||
if (width_ok) {
|
||||
s = gb_string_appendc(s, "digits(");
|
||||
custom_rule = true;
|
||||
}
|
||||
|
||||
switch (verb) {
|
||||
case 'b':
|
||||
s = gb_string_appendc(s, "bin(");
|
||||
paren_count += 1;
|
||||
break;
|
||||
case 'd':
|
||||
s = gb_string_appendc(s, "dec(");
|
||||
paren_count += 1;
|
||||
break;
|
||||
case 'x':
|
||||
case 'X':
|
||||
s = gb_string_appendc(s, "hex(");
|
||||
paren_count += 1;
|
||||
break;
|
||||
case 'o':
|
||||
s = gb_string_appendc(s, "oct(");
|
||||
paren_count += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (tail.len != 0 && tail != "0") {
|
||||
s = gb_string_appendc(s, "array(");
|
||||
s = gb_string_append_length(s, name.text, name.len);
|
||||
if (is_type_slice(field->type) || is_type_dynamic_array(field->type)) {
|
||||
s = gb_string_appendc(s, ".data");
|
||||
}
|
||||
s = gb_string_appendc(s, ", ");
|
||||
s = gb_string_append_length(s, tail.text, tail.len);
|
||||
s = gb_string_appendc(s, ")");
|
||||
custom_rule = true;
|
||||
} else {
|
||||
s = gb_string_append_length(s, name.text, name.len);
|
||||
custom_rule = true;
|
||||
}
|
||||
|
||||
|
||||
for (isize j = 0; j < paren_count; j++) {
|
||||
s = gb_string_appendc(s, ")");
|
||||
custom_rule = true;
|
||||
}
|
||||
if (width_ok) {
|
||||
s = gb_string_append_fmt(s, ", %llu)", cast(unsigned long long)width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!custom_rule) {
|
||||
s = gb_string_append_length(s, name.text, name.len);
|
||||
}
|
||||
}
|
||||
|
||||
s = gb_string_appendc(s, ")");
|
||||
|
||||
view = make_string((u8 const *)s, gb_string_length(s));
|
||||
}
|
||||
}
|
||||
|
||||
if (view.len == 0) {
|
||||
// Ignore the type, it didn't anything custom
|
||||
return;
|
||||
}
|
||||
|
||||
array_add(&c->info.raddbg_type_views, RaddbgTypeView{type, view});
|
||||
}
|
||||
|
||||
gb_internal void check_objc_context_provider_procedures(Checker *c) {
|
||||
for (Entity *e = nullptr; mpsc_dequeue(&c->procs_with_objc_context_provider_to_check, &e); /**/) {
|
||||
GB_ASSERT(e->kind == Entity_TypeName);
|
||||
@@ -6989,6 +7326,11 @@ gb_internal void check_parsed_files(Checker *c) {
|
||||
}
|
||||
}
|
||||
|
||||
TIME_SECTION("collate type info stuff");
|
||||
for (RaddbgTypeView type_view; mpsc_dequeue(&c->info.raddbg_type_views_queue, &type_view); /**/) {
|
||||
handle_raddbg_type_view(c, type_view);
|
||||
}
|
||||
|
||||
|
||||
TIME_SECTION("type check finish");
|
||||
}
|
||||
|
||||
@@ -161,6 +161,9 @@ struct AttributeContext {
|
||||
|
||||
String require_target_feature; // required by the target micro-architecture
|
||||
String enable_target_feature; // will be enabled for the procedure only
|
||||
|
||||
bool raddbg_type_view;
|
||||
String raddbg_type_view_string;
|
||||
};
|
||||
|
||||
gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix, String link_suffix) {
|
||||
@@ -427,6 +430,11 @@ struct Defineable {
|
||||
String pos_str;
|
||||
};
|
||||
|
||||
struct RaddbgTypeView {
|
||||
Type * type;
|
||||
String view;
|
||||
};
|
||||
|
||||
// CheckerInfo stores all the symbol information for a type-checked program
|
||||
struct CheckerInfo {
|
||||
Checker *checker;
|
||||
@@ -487,6 +495,9 @@ struct CheckerInfo {
|
||||
MPSCQueue<Entity *> foreign_imports_to_check_fullpaths;
|
||||
MPSCQueue<Entity *> foreign_decls_to_check;
|
||||
|
||||
MPSCQueue<RaddbgTypeView> raddbg_type_views_queue;
|
||||
Array<RaddbgTypeView> raddbg_type_views;
|
||||
|
||||
MPSCQueue<Ast *> intrinsics_entry_point_usage;
|
||||
|
||||
BlockingMutex objc_objc_msgSend_mutex;
|
||||
@@ -552,6 +563,7 @@ struct CheckerContext {
|
||||
|
||||
u32 stmt_flags;
|
||||
bool in_enum_type;
|
||||
bool in_proc_group;
|
||||
bool collect_delayed_decls;
|
||||
bool allow_polymorphic_types;
|
||||
bool disallow_polymorphic_return_types; // NOTE(zen3ger): no poly type decl in return types
|
||||
|
||||
@@ -250,6 +250,7 @@ BuiltinProc__type_simple_boolean_begin,
|
||||
BuiltinProc_type_is_complex,
|
||||
BuiltinProc_type_is_quaternion,
|
||||
BuiltinProc_type_is_string,
|
||||
BuiltinProc_type_is_string16,
|
||||
BuiltinProc_type_is_typeid,
|
||||
BuiltinProc_type_is_any,
|
||||
|
||||
@@ -338,6 +339,8 @@ BuiltinProc__type_simple_boolean_end,
|
||||
|
||||
BuiltinProc_type_has_shared_fields,
|
||||
|
||||
BuiltinProc_type_canonical_name,
|
||||
|
||||
BuiltinProc__type_end,
|
||||
|
||||
BuiltinProc_procedure_of,
|
||||
@@ -350,6 +353,7 @@ BuiltinProc__type_end,
|
||||
BuiltinProc_objc_register_selector,
|
||||
BuiltinProc_objc_register_class,
|
||||
BuiltinProc_objc_ivar_get,
|
||||
BuiltinProc_objc_block,
|
||||
|
||||
BuiltinProc_constant_utf16_cstring,
|
||||
|
||||
@@ -608,6 +612,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("type_is_complex"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_is_quaternion"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_is_string"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_is_string16"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_is_typeid"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_is_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
@@ -695,6 +700,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("type_map_cell_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("type_has_shared_fields"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_canonical_name"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||
|
||||
@@ -709,6 +715,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
{STR_LIT("objc_ivar_get"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
{STR_LIT("objc_block"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
|
||||
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
|
||||
+9
-1
@@ -80,6 +80,13 @@ gb_internal gb_inline bool is_power_of_two(i64 x) {
|
||||
return !(x & (x-1));
|
||||
}
|
||||
|
||||
gb_internal gb_inline bool is_power_of_two_u64(u64 x) {
|
||||
if (x == 0) {
|
||||
return false;
|
||||
}
|
||||
return !(x & (x-1));
|
||||
}
|
||||
|
||||
gb_internal int isize_cmp(isize x, isize y) {
|
||||
if (x < y) {
|
||||
return -1;
|
||||
@@ -350,6 +357,7 @@ gb_global bool global_module_path_set = false;
|
||||
#include "ptr_map.cpp"
|
||||
#include "ptr_set.cpp"
|
||||
#include "string_map.cpp"
|
||||
#include "string16_map.cpp"
|
||||
#include "string_set.cpp"
|
||||
#include "priority_queue.cpp"
|
||||
#include "thread_pool.cpp"
|
||||
@@ -669,7 +677,7 @@ gb_internal gb_inline f64 gb_sqrt(f64 x) {
|
||||
gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
|
||||
u32 i, j;
|
||||
|
||||
u32 len = cast(u32)string16_len(cmd_line);
|
||||
u32 len = cast(u32)string16_len(cast(u16 *)cmd_line);
|
||||
i = ((len+2)/2)*gb_size_of(void *) + gb_size_of(void *);
|
||||
|
||||
wchar_t **argv = cast(wchar_t **)GlobalAlloc(GMEM_FIXED, i + (len+2)*gb_size_of(wchar_t));
|
||||
|
||||
+51
-1
@@ -29,6 +29,7 @@ enum ExactValueKind {
|
||||
ExactValue_Compound = 8,
|
||||
ExactValue_Procedure = 9,
|
||||
ExactValue_Typeid = 10,
|
||||
ExactValue_String16 = 11,
|
||||
|
||||
ExactValue_Count,
|
||||
};
|
||||
@@ -46,6 +47,7 @@ struct ExactValue {
|
||||
Ast * value_compound;
|
||||
Ast * value_procedure;
|
||||
Type * value_typeid;
|
||||
String16 value_string16;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -66,6 +68,9 @@ gb_internal uintptr hash_exact_value(ExactValue v) {
|
||||
case ExactValue_String:
|
||||
res = gb_fnv32a(v.value_string.text, v.value_string.len);
|
||||
break;
|
||||
case ExactValue_String16:
|
||||
res = gb_fnv32a(v.value_string.text, v.value_string.len*gb_size_of(u16));
|
||||
break;
|
||||
case ExactValue_Integer:
|
||||
{
|
||||
u32 key = gb_fnv32a(v.value_integer.dp, gb_size_of(*v.value_integer.dp) * v.value_integer.used);
|
||||
@@ -118,6 +123,11 @@ gb_internal ExactValue exact_value_string(String string) {
|
||||
result.value_string = string;
|
||||
return result;
|
||||
}
|
||||
gb_internal ExactValue exact_value_string16(String16 string) {
|
||||
ExactValue result = {ExactValue_String16};
|
||||
result.value_string16 = string;
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_internal ExactValue exact_value_i64(i64 i) {
|
||||
ExactValue result = {ExactValue_Integer};
|
||||
@@ -656,6 +666,7 @@ gb_internal i32 exact_value_order(ExactValue const &v) {
|
||||
return 0;
|
||||
case ExactValue_Bool:
|
||||
case ExactValue_String:
|
||||
case ExactValue_String16:
|
||||
return 1;
|
||||
case ExactValue_Integer:
|
||||
return 2;
|
||||
@@ -689,6 +700,7 @@ gb_internal void match_exact_values(ExactValue *x, ExactValue *y) {
|
||||
|
||||
case ExactValue_Bool:
|
||||
case ExactValue_String:
|
||||
case ExactValue_String16:
|
||||
case ExactValue_Quaternion:
|
||||
case ExactValue_Pointer:
|
||||
case ExactValue_Compound:
|
||||
@@ -891,7 +903,18 @@ gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, E
|
||||
gb_memmove(data, sx.text, sx.len);
|
||||
gb_memmove(data+sx.len, sy.text, sy.len);
|
||||
return exact_value_string(make_string(data, len));
|
||||
break;
|
||||
}
|
||||
case ExactValue_String16: {
|
||||
if (op != Token_Add) goto error;
|
||||
|
||||
// NOTE(bill): How do you minimize this over allocation?
|
||||
String sx = x.value_string;
|
||||
String sy = y.value_string;
|
||||
isize len = sx.len+sy.len;
|
||||
u16 *data = gb_alloc_array(permanent_allocator(), u16, len);
|
||||
gb_memmove(data, sx.text, sx.len*gb_size_of(u16));
|
||||
gb_memmove(data+sx.len, sy.text, sy.len*gb_size_of(u16));
|
||||
return exact_value_string16(make_string16(data, len));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,6 +1017,19 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExactValue_String16: {
|
||||
String16 a = x.value_string16;
|
||||
String16 b = y.value_string16;
|
||||
switch (op) {
|
||||
case Token_CmpEq: return a == b;
|
||||
case Token_NotEq: return a != b;
|
||||
case Token_Lt: return a < b;
|
||||
case Token_LtEq: return a <= b;
|
||||
case Token_Gt: return a > b;
|
||||
case Token_GtEq: return a >= b;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ExactValue_Pointer: {
|
||||
switch (op) {
|
||||
@@ -1050,6 +1086,20 @@ gb_internal gbString write_exact_value_to_string(gbString str, ExactValue const
|
||||
gb_free(heap_allocator(), s.text);
|
||||
return str;
|
||||
}
|
||||
case ExactValue_String16: {
|
||||
String s = quote_to_ascii(heap_allocator(), v.value_string16);
|
||||
string_limit = gb_max(string_limit, 36);
|
||||
if (s.len <= string_limit) {
|
||||
str = gb_string_append_length(str, s.text, s.len);
|
||||
} else {
|
||||
isize n = string_limit/5;
|
||||
str = gb_string_append_length(str, s.text, n);
|
||||
str = gb_string_append_fmt(str, "\"..%lld chars..\"", s.len-(2*n));
|
||||
str = gb_string_append_length(str, s.text+s.len-n, n);
|
||||
}
|
||||
gb_free(heap_allocator(), s.text);
|
||||
return str;
|
||||
}
|
||||
case ExactValue_Integer: {
|
||||
String s = big_int_to_string(heap_allocator(), &v.value_integer);
|
||||
str = gb_string_append_length(str, s.text, s.len);
|
||||
|
||||
+31
-2
@@ -1264,7 +1264,13 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
|
||||
case Basic_string:
|
||||
return build_context.metrics.int_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}");
|
||||
|
||||
case Basic_string16:
|
||||
return build_context.metrics.int_size == 4 ? str_lit("{string16=*i}") : str_lit("{string16=*q}");
|
||||
|
||||
case Basic_cstring: return str_lit("*");
|
||||
case Basic_cstring16: return str_lit("*");
|
||||
|
||||
|
||||
case Basic_any: return str_lit("{any=^v^v}"); // rawptr + ^Type_Info
|
||||
|
||||
case Basic_typeid:
|
||||
@@ -3368,8 +3374,9 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
LLVMModuleRef mod = m->mod;
|
||||
LLVMContextRef ctx = m->ctx;
|
||||
|
||||
lb_add_raddbg_string(m, "type_view: {type: \"[]?\", expr: \"array(data, len)\"}");
|
||||
lb_add_raddbg_string(m, "type_view: {type: \"string\", expr: \"array(data, len)\"}");
|
||||
lb_add_raddbg_string(m, "type_view: {type: \"[]?\", expr: \"array(data, len)\"}");
|
||||
lb_add_raddbg_string(m, "type_view: {type: \"string\", expr: \"array(data, len)\"}");
|
||||
lb_add_raddbg_string(m, "type_view: {type: \"[dynamic]?\", expr: \"rows($, array(data, len), len, cap, allocator)\"}");
|
||||
|
||||
// column major matrices
|
||||
lb_add_raddbg_string(m, "type_view: {type: \"matrix[1, ?]?\", expr: \"columns($.data, $[0])\"}");
|
||||
@@ -3409,7 +3416,29 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
|
||||
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
for (RaddbgTypeView const &type_view : gen->info->raddbg_type_views) {
|
||||
if (type_view.type == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type_view.view.len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String t_str = type_to_canonical_string(temporary_allocator(), type_view.type);
|
||||
|
||||
gbString s = gb_string_make(temporary_allocator(), "");
|
||||
|
||||
s = gb_string_appendc(s, "type_view: {type: \"");
|
||||
s = gb_string_append_length(s, t_str.text, t_str.len);
|
||||
s = gb_string_appendc(s, "\", expr: \"");
|
||||
s = gb_string_append_length(s, type_view.view.text, type_view.view.len);
|
||||
s = gb_string_appendc(s, "\"}");
|
||||
|
||||
lb_add_raddbg_string(m, s);
|
||||
}
|
||||
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
u32 global_name_index = 0;
|
||||
for (String str = {}; mpsc_dequeue(&gen->raddebug_section_strings, &str); /**/) {
|
||||
LLVMValueRef data = LLVMConstStringInContext(ctx, cast(char const *)str.text, cast(unsigned)str.len, false);
|
||||
|
||||
@@ -173,7 +173,8 @@ struct lbModule {
|
||||
PtrMap<LLVMValueRef, Entity *> procedure_values;
|
||||
Array<lbProcedure *> missing_procedures_to_check;
|
||||
|
||||
StringMap<LLVMValueRef> const_strings;
|
||||
StringMap<LLVMValueRef> const_strings;
|
||||
String16Map<LLVMValueRef> const_string16s;
|
||||
|
||||
PtrMap<u64/*type hash*/, struct lbFunctionType *> function_type_map;
|
||||
|
||||
@@ -197,6 +198,7 @@ struct lbModule {
|
||||
StringMap<lbAddr> objc_classes;
|
||||
StringMap<lbAddr> objc_selectors;
|
||||
StringMap<lbAddr> objc_ivars;
|
||||
isize objc_next_block_id; // Used to name objective-c blocks, per module
|
||||
|
||||
PtrMap<u64/*type hash*/, lbAddr> map_cell_info_map; // address of runtime.Map_Info
|
||||
PtrMap<u64/*type hash*/, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
|
||||
@@ -482,7 +484,10 @@ gb_internal void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, l
|
||||
gb_internal void lb_start_block(lbProcedure *p, lbBlock *b);
|
||||
|
||||
gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr);
|
||||
|
||||
gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type);
|
||||
gb_internal void lb_begin_procedure_body(lbProcedure *p);
|
||||
gb_internal void lb_end_procedure_body(lbProcedure *p);
|
||||
gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining);
|
||||
|
||||
gb_internal lbAddr lb_find_or_generate_context_ptr(lbProcedure *p);
|
||||
gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx);
|
||||
|
||||
@@ -122,6 +122,25 @@ gb_internal lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
|
||||
|
||||
|
||||
gb_internal LLVMValueRef llvm_const_string_internal(lbModule *m, Type *t, LLVMValueRef data, LLVMValueRef len) {
|
||||
GB_ASSERT(!is_type_string16(t));
|
||||
if (build_context.metrics.ptr_size < build_context.metrics.int_size) {
|
||||
LLVMValueRef values[3] = {
|
||||
data,
|
||||
LLVMConstNull(lb_type(m, t_i32)),
|
||||
len,
|
||||
};
|
||||
return llvm_const_named_struct_internal(lb_type(m, t), values, 3);
|
||||
} else {
|
||||
LLVMValueRef values[2] = {
|
||||
data,
|
||||
len,
|
||||
};
|
||||
return llvm_const_named_struct_internal(lb_type(m, t), values, 2);
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal LLVMValueRef llvm_const_string16_internal(lbModule *m, Type *t, LLVMValueRef data, LLVMValueRef len) {
|
||||
GB_ASSERT(is_type_string16(t));
|
||||
if (build_context.metrics.ptr_size < build_context.metrics.int_size) {
|
||||
LLVMValueRef values[3] = {
|
||||
data,
|
||||
@@ -238,6 +257,10 @@ gb_internal lbValue lb_const_string(lbModule *m, String const &value) {
|
||||
return lb_const_value(m, t_string, exact_value_string(value));
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_const_string(lbModule *m, String16 const &value) {
|
||||
return lb_const_value(m, t_string16, exact_value_string16(value));
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value) {
|
||||
lbValue res = {};
|
||||
@@ -569,7 +592,11 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
GB_ASSERT(is_type_slice(type));
|
||||
res.value = lb_find_or_add_entity_string_byte_slice_with_type(m, value.value_string, original_type).value;
|
||||
return res;
|
||||
} else {
|
||||
} else if (value.kind == ExactValue_String16) {
|
||||
GB_ASSERT(is_type_slice(type));
|
||||
res.value = lb_find_or_add_entity_string16_slice_with_type(m, value.value_string16, original_type).value;
|
||||
return res;
|
||||
}else {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
|
||||
isize count = cl->elems.count;
|
||||
@@ -751,7 +778,55 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
{
|
||||
bool custom_link_section = cc.link_section.len > 0;
|
||||
|
||||
LLVMValueRef ptr = lb_find_or_add_entity_string_ptr(m, value.value_string, custom_link_section);
|
||||
LLVMValueRef ptr = nullptr;
|
||||
lbValue res = {};
|
||||
res.type = default_type(original_type);
|
||||
|
||||
isize len = value.value_string.len;
|
||||
|
||||
if (is_type_string16(res.type) || is_type_cstring16(res.type)) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
String16 s16 = string_to_string16(temporary_allocator(), value.value_string);
|
||||
len = s16.len;
|
||||
ptr = lb_find_or_add_entity_string16_ptr(m, s16, custom_link_section);
|
||||
} else {
|
||||
ptr = lb_find_or_add_entity_string_ptr(m, value.value_string, custom_link_section);
|
||||
}
|
||||
|
||||
if (custom_link_section) {
|
||||
LLVMSetSection(ptr, alloc_cstring(permanent_allocator(), cc.link_section));
|
||||
}
|
||||
|
||||
if (is_type_cstring(res.type) || is_type_cstring16(res.type)) {
|
||||
res.value = ptr;
|
||||
} else {
|
||||
if (len == 0) {
|
||||
if (is_type_string16(res.type)) {
|
||||
ptr = LLVMConstNull(lb_type(m, t_u16_ptr));
|
||||
} else {
|
||||
ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
|
||||
}
|
||||
}
|
||||
LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), len, true);
|
||||
GB_ASSERT(is_type_string(original_type));
|
||||
|
||||
if (is_type_string16(res.type)) {
|
||||
res.value = llvm_const_string16_internal(m, original_type, ptr, str_len);
|
||||
} else {
|
||||
res.value = llvm_const_string_internal(m, original_type, ptr, str_len);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
case ExactValue_String16:
|
||||
{
|
||||
GB_ASSERT(is_type_string16(res.type) || is_type_cstring16(res.type));
|
||||
|
||||
bool custom_link_section = cc.link_section.len > 0;
|
||||
|
||||
LLVMValueRef ptr = lb_find_or_add_entity_string16_ptr(m, value.value_string16, custom_link_section);
|
||||
lbValue res = {};
|
||||
res.type = default_type(original_type);
|
||||
|
||||
@@ -759,21 +834,22 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
LLVMSetSection(ptr, alloc_cstring(permanent_allocator(), cc.link_section));
|
||||
}
|
||||
|
||||
if (is_type_cstring(res.type)) {
|
||||
if (is_type_cstring16(res.type)) {
|
||||
res.value = ptr;
|
||||
} else {
|
||||
if (value.value_string.len == 0) {
|
||||
if (value.value_string16.len == 0) {
|
||||
ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
|
||||
}
|
||||
LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string.len, true);
|
||||
LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string16.len, true);
|
||||
GB_ASSERT(is_type_string(original_type));
|
||||
|
||||
res.value = llvm_const_string_internal(m, original_type, ptr, str_len);
|
||||
res.value = llvm_const_string16_internal(m, original_type, ptr, str_len);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
case ExactValue_Integer:
|
||||
if (is_type_pointer(type) || is_type_multi_pointer(type) || is_type_proc(type)) {
|
||||
LLVMTypeRef t = lb_type(m, original_type);
|
||||
|
||||
@@ -802,6 +802,20 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
|
||||
LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("char"), 8, LLVMDWARFTypeEncoding_Unsigned);
|
||||
return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, ptr_bits, ptr_bits, 0, "cstring", 7);
|
||||
}
|
||||
|
||||
case Basic_string16:
|
||||
{
|
||||
LLVMMetadataRef elements[2] = {};
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data"), t_u16_ptr, 0);
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, int_bits);
|
||||
return lb_debug_basic_struct(m, str_lit("string16"), 2*int_bits, int_bits, elements, gb_count_of(elements));
|
||||
}
|
||||
case Basic_cstring16:
|
||||
{
|
||||
LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("wchar_t"), 16, LLVMDWARFTypeEncoding_Unsigned);
|
||||
return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, ptr_bits, ptr_bits, 0, "cstring16", 7);
|
||||
}
|
||||
|
||||
case Basic_any:
|
||||
{
|
||||
LLVMMetadataRef elements[2] = {};
|
||||
|
||||
+477
-30
@@ -283,6 +283,36 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
|
||||
return res;
|
||||
}
|
||||
|
||||
gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_behaviour(lbProcedure *p) {
|
||||
AstFile *file = nullptr;
|
||||
|
||||
if (p->body && p->body->file()) {
|
||||
file = p->body->file();
|
||||
} else if (p->type_expr && p->type_expr->file()) {
|
||||
file = p->type_expr->file();
|
||||
} else if (p->entity && p->entity->file) {
|
||||
file = p->entity->file;
|
||||
}
|
||||
|
||||
if (file != nullptr && file->feature_flags_set) {
|
||||
u64 flags = file->feature_flags;
|
||||
if (flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) {
|
||||
return IntegerDivisionByZero_Trap;
|
||||
}
|
||||
if (flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) {
|
||||
return IntegerDivisionByZero_Zero;
|
||||
}
|
||||
if (flags & OptInFeatureFlag_IntegerDivisionByZero_Self) {
|
||||
return IntegerDivisionByZero_Self;
|
||||
}
|
||||
if (flags & OptInFeatureFlag_IntegerDivisionByZero_AllBits) {
|
||||
return IntegerDivisionByZero_AllBits;
|
||||
}
|
||||
}
|
||||
return build_context.integer_division_by_zero_behaviour;
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, lbValue *res_) {
|
||||
GB_ASSERT(is_type_array_like(type));
|
||||
Type *elem_type = base_array_type(type);
|
||||
@@ -354,7 +384,6 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
switch (op) {
|
||||
case Token_Add:
|
||||
z = LLVMBuildAdd(p->builder, x, y, "");
|
||||
@@ -366,17 +395,15 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu
|
||||
z = LLVMBuildMul(p->builder, x, y, "");
|
||||
break;
|
||||
case Token_Quo:
|
||||
if (is_type_unsigned(integral_type)) {
|
||||
z = LLVMBuildUDiv(p->builder, x, y, "");
|
||||
} else {
|
||||
z = LLVMBuildSDiv(p->builder, x, y, "");
|
||||
{
|
||||
auto *call = is_type_unsigned(integral_type) ? LLVMBuildUDiv : LLVMBuildSDiv;
|
||||
z = call(p->builder, x, y, "");
|
||||
}
|
||||
break;
|
||||
case Token_Mod:
|
||||
if (is_type_unsigned(integral_type)) {
|
||||
z = LLVMBuildURem(p->builder, x, y, "");
|
||||
} else {
|
||||
z = LLVMBuildSRem(p->builder, x, y, "");
|
||||
{
|
||||
auto *call = is_type_unsigned(integral_type) ? LLVMBuildURem : LLVMBuildSRem;
|
||||
z = call(p->builder, x, y, "");
|
||||
}
|
||||
break;
|
||||
case Token_ModMod:
|
||||
@@ -1111,6 +1138,303 @@ gb_internal lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue l
|
||||
return {};
|
||||
}
|
||||
|
||||
gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, LLVMValueRef rhs, bool is_signed) {
|
||||
LLVMTypeRef type = LLVMTypeOf(rhs);
|
||||
GB_ASSERT(LLVMTypeOf(lhs) == type);
|
||||
|
||||
LLVMValueRef zero = LLVMConstNull(type);
|
||||
LLVMValueRef all_bits = LLVMConstNot(zero);
|
||||
auto behaviour = lb_check_for_integer_division_by_zero_behaviour(p);
|
||||
|
||||
auto *call = is_signed ? LLVMBuildSDiv : LLVMBuildUDiv;
|
||||
|
||||
if (LLVMIsConstant(rhs)) {
|
||||
if (LLVMIsNull(rhs)) {
|
||||
switch (behaviour) {
|
||||
case IntegerDivisionByZero_Self:
|
||||
return lhs;
|
||||
case IntegerDivisionByZero_Zero:
|
||||
return zero;
|
||||
case IntegerDivisionByZero_AllBits:
|
||||
// return all_bits;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!is_signed && lb_sizeof(type) <= 8) {
|
||||
u64 v = cast(u64)LLVMConstIntGetZExtValue(rhs);
|
||||
if (v == 1) {
|
||||
return lhs;
|
||||
} else if (is_power_of_two_u64(v)) {
|
||||
u64 n = floor_log2(v);
|
||||
LLVMValueRef bits = LLVMConstInt(type, n, false);
|
||||
return LLVMBuildLShr(p->builder, lhs, bits, "");
|
||||
}
|
||||
}
|
||||
|
||||
return call(p->builder, lhs, rhs, "");
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef incoming_values[2] = {};
|
||||
LLVMBasicBlockRef incoming_blocks[2] = {};
|
||||
|
||||
lbBlock *safe_block = lb_create_block(p, "div.safe");
|
||||
lbBlock *edge_case_block = lb_create_block(p, "div.edge");
|
||||
lbBlock *done_block = lb_create_block(p, "div.done");
|
||||
|
||||
LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, "");
|
||||
lbValue cond = {dem_check, t_untyped_bool};
|
||||
|
||||
lb_emit_if(p, cond, safe_block, edge_case_block);
|
||||
|
||||
lb_start_block(p, safe_block);
|
||||
incoming_values[0] = call(p->builder, lhs, rhs, "");
|
||||
lb_emit_jump(p, done_block);
|
||||
|
||||
lb_start_block(p, edge_case_block);
|
||||
|
||||
|
||||
switch (behaviour) {
|
||||
case IntegerDivisionByZero_Trap:
|
||||
lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0);
|
||||
LLVMBuildUnreachable(p->builder);
|
||||
break;
|
||||
case IntegerDivisionByZero_Zero:
|
||||
incoming_values[1] = zero;
|
||||
break;
|
||||
case IntegerDivisionByZero_Self:
|
||||
incoming_values[1] = lhs;
|
||||
break;
|
||||
case IntegerDivisionByZero_AllBits:
|
||||
incoming_values[1] = all_bits;
|
||||
break;
|
||||
}
|
||||
|
||||
lb_emit_jump(p, done_block);
|
||||
lb_start_block(p, done_block);
|
||||
|
||||
LLVMValueRef res = incoming_values[0];
|
||||
|
||||
switch (behaviour) {
|
||||
case IntegerDivisionByZero_Trap:
|
||||
case IntegerDivisionByZero_Self:
|
||||
res = incoming_values[0];
|
||||
break;
|
||||
case IntegerDivisionByZero_Zero:
|
||||
case IntegerDivisionByZero_AllBits:
|
||||
res = LLVMBuildPhi(p->builder, type, "");
|
||||
|
||||
GB_ASSERT(p->curr_block->preds.count >= 2);
|
||||
incoming_blocks[0] = p->curr_block->preds[0]->block;
|
||||
incoming_blocks[1] = p->curr_block->preds[1]->block;
|
||||
|
||||
LLVMAddIncoming(res, incoming_values, incoming_blocks, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
gb_internal LLVMValueRef lb_integer_division_intrinsics(lbProcedure *p, LLVMValueRef lhs, LLVMValueRef rhs, LLVMValueRef scale, Type *platform_type, char const *name) {
|
||||
LLVMTypeRef type = LLVMTypeOf(rhs);
|
||||
GB_ASSERT(LLVMTypeOf(lhs) == type);
|
||||
|
||||
LLVMValueRef zero = LLVMConstNull(type);
|
||||
LLVMValueRef all_bits = LLVMConstNot(zero);
|
||||
auto behaviour = lb_check_for_integer_division_by_zero_behaviour(p);
|
||||
|
||||
auto const do_op = [&]() -> LLVMValueRef {
|
||||
LLVMTypeRef types[1] = {lb_type(p->module, platform_type)};
|
||||
|
||||
LLVMValueRef args[3] = {
|
||||
lhs,
|
||||
rhs,
|
||||
scale };
|
||||
|
||||
return lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
|
||||
};
|
||||
|
||||
if (LLVMIsConstant(rhs)) {
|
||||
if (LLVMIsNull(rhs)) {
|
||||
switch (behaviour) {
|
||||
case IntegerDivisionByZero_Self:
|
||||
return lhs;
|
||||
case IntegerDivisionByZero_Zero:
|
||||
return zero;
|
||||
}
|
||||
} else {
|
||||
return do_op();
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef incoming_values[2] = {};
|
||||
LLVMBasicBlockRef incoming_blocks[2] = {};
|
||||
|
||||
lbBlock *safe_block = lb_create_block(p, "div.safe");
|
||||
lbBlock *edge_case_block = lb_create_block(p, "div.edge");
|
||||
lbBlock *done_block = lb_create_block(p, "div.done");
|
||||
|
||||
LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, "");
|
||||
lbValue cond = {dem_check, t_untyped_bool};
|
||||
|
||||
lb_emit_if(p, cond, safe_block, edge_case_block);
|
||||
|
||||
lb_start_block(p, safe_block);
|
||||
incoming_values[0] = do_op();
|
||||
lb_emit_jump(p, done_block);
|
||||
|
||||
lb_start_block(p, edge_case_block);
|
||||
|
||||
|
||||
switch (behaviour) {
|
||||
case IntegerDivisionByZero_Trap:
|
||||
lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0);
|
||||
LLVMBuildUnreachable(p->builder);
|
||||
break;
|
||||
case IntegerDivisionByZero_Zero:
|
||||
incoming_values[1] = zero;
|
||||
break;
|
||||
case IntegerDivisionByZero_Self:
|
||||
incoming_values[1] = lhs;
|
||||
break;
|
||||
case IntegerDivisionByZero_AllBits:
|
||||
incoming_values[1] = all_bits;
|
||||
break;
|
||||
}
|
||||
|
||||
lb_emit_jump(p, done_block);
|
||||
lb_start_block(p, done_block);
|
||||
|
||||
LLVMValueRef res = incoming_values[0];
|
||||
|
||||
switch (behaviour) {
|
||||
case IntegerDivisionByZero_Trap:
|
||||
case IntegerDivisionByZero_Self:
|
||||
res = incoming_values[0];
|
||||
break;
|
||||
case IntegerDivisionByZero_Zero:
|
||||
case IntegerDivisionByZero_AllBits:
|
||||
res = LLVMBuildPhi(p->builder, type, "");
|
||||
|
||||
GB_ASSERT(p->curr_block->preds.count >= 2);
|
||||
incoming_blocks[0] = p->curr_block->preds[0]->block;
|
||||
incoming_blocks[1] = p->curr_block->preds[1]->block;
|
||||
|
||||
LLVMAddIncoming(res, incoming_values, incoming_blocks, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLVMValueRef rhs, bool is_unsigned, bool is_floored) {
|
||||
LLVMTypeRef type = LLVMTypeOf(rhs);
|
||||
GB_ASSERT(LLVMTypeOf(lhs) == type);
|
||||
|
||||
LLVMValueRef zero = LLVMConstNull(type);
|
||||
auto behaviour = lb_check_for_integer_division_by_zero_behaviour(p);
|
||||
|
||||
auto const do_op = [&]() -> LLVMValueRef {
|
||||
if (is_floored) { // %%
|
||||
if (is_unsigned) {
|
||||
return LLVMBuildURem(p->builder, lhs, rhs, "");
|
||||
} else {
|
||||
LLVMValueRef a = LLVMBuildSRem(p->builder, lhs, rhs, "");
|
||||
LLVMValueRef b = LLVMBuildAdd(p->builder, a, rhs, "");
|
||||
LLVMValueRef c = LLVMBuildSRem(p->builder, b, rhs, "");
|
||||
return c;
|
||||
}
|
||||
} else { // %
|
||||
if (is_unsigned) {
|
||||
return LLVMBuildURem(p->builder, lhs, rhs, "");
|
||||
} else {
|
||||
return LLVMBuildSRem(p->builder, lhs, rhs, "");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (LLVMIsConstant(rhs)) {
|
||||
if (LLVMIsNull(rhs)) {
|
||||
switch (behaviour) {
|
||||
case IntegerDivisionByZero_Self:
|
||||
return zero;
|
||||
case IntegerDivisionByZero_Zero:
|
||||
case IntegerDivisionByZero_AllBits:
|
||||
return lhs;
|
||||
}
|
||||
} else {
|
||||
return do_op();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LLVMValueRef incoming_values[2] = {};
|
||||
LLVMBasicBlockRef incoming_blocks[2] = {};
|
||||
|
||||
lbBlock *safe_block = lb_create_block(p, "mod.safe");
|
||||
lbBlock *edge_case_block = lb_create_block(p, "mod.edge");
|
||||
lbBlock *done_block = lb_create_block(p, "mod.done");
|
||||
|
||||
LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, "");
|
||||
lbValue cond = {dem_check, t_untyped_bool};
|
||||
|
||||
lb_emit_if(p, cond, safe_block, edge_case_block);
|
||||
|
||||
lb_start_block(p, safe_block);
|
||||
incoming_values[0] = do_op();
|
||||
lb_emit_jump(p, done_block);
|
||||
|
||||
lb_start_block(p, edge_case_block);
|
||||
|
||||
/*
|
||||
NOTE(bill): @integer division by zero rules
|
||||
|
||||
truncated: r = a - b*trunc(a/b)
|
||||
floored: r = a - b*floor(a/b)
|
||||
|
||||
IFF a/0 == 0, then (a%0 == a) or (a%%0 == a)
|
||||
IFF a/0 == a, then (a%0 == 0) or (a%%0 == 0)
|
||||
*/
|
||||
|
||||
switch (behaviour) {
|
||||
case IntegerDivisionByZero_Trap:
|
||||
lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0);
|
||||
LLVMBuildUnreachable(p->builder);
|
||||
break;
|
||||
case IntegerDivisionByZero_Zero:
|
||||
case IntegerDivisionByZero_AllBits:
|
||||
incoming_values[1] = lhs;
|
||||
break;
|
||||
case IntegerDivisionByZero_Self:
|
||||
incoming_values[1] = zero;
|
||||
break;
|
||||
}
|
||||
|
||||
lb_emit_jump(p, done_block);
|
||||
lb_start_block(p, done_block);
|
||||
|
||||
LLVMValueRef res = incoming_values[0];
|
||||
|
||||
switch (behaviour) {
|
||||
case IntegerDivisionByZero_Trap:
|
||||
case IntegerDivisionByZero_Self:
|
||||
res = incoming_values[0];
|
||||
break;
|
||||
case IntegerDivisionByZero_Zero:
|
||||
case IntegerDivisionByZero_AllBits:
|
||||
res = LLVMBuildPhi(p->builder, type, "");
|
||||
|
||||
GB_ASSERT(p->curr_block->preds.count >= 2);
|
||||
incoming_blocks[0] = p->curr_block->preds[0]->block;
|
||||
incoming_blocks[1] = p->curr_block->preds[1]->block;
|
||||
|
||||
LLVMAddIncoming(res, incoming_values, incoming_blocks, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type) {
|
||||
@@ -1350,33 +1674,20 @@ handle_op:;
|
||||
if (is_type_float(integral_type)) {
|
||||
res.value = LLVMBuildFDiv(p->builder, lhs.value, rhs.value, "");
|
||||
return res;
|
||||
} else if (is_type_unsigned(integral_type)) {
|
||||
res.value = LLVMBuildUDiv(p->builder, lhs.value, rhs.value, "");
|
||||
} else {
|
||||
res.value = lb_integer_division(p, lhs.value, rhs.value, !is_type_unsigned(integral_type));
|
||||
return res;
|
||||
}
|
||||
res.value = LLVMBuildSDiv(p->builder, lhs.value, rhs.value, "");
|
||||
return res;
|
||||
case Token_Mod:
|
||||
if (is_type_float(integral_type)) {
|
||||
res.value = LLVMBuildFRem(p->builder, lhs.value, rhs.value, "");
|
||||
return res;
|
||||
} else if (is_type_unsigned(integral_type)) {
|
||||
res.value = LLVMBuildURem(p->builder, lhs.value, rhs.value, "");
|
||||
return res;
|
||||
}
|
||||
res.value = LLVMBuildSRem(p->builder, lhs.value, rhs.value, "");
|
||||
res.value = lb_integer_modulo(p, lhs.value, rhs.value, is_type_unsigned(integral_type), /*is_floored*/false);
|
||||
return res;
|
||||
case Token_ModMod:
|
||||
if (is_type_unsigned(integral_type)) {
|
||||
res.value = LLVMBuildURem(p->builder, lhs.value, rhs.value, "");
|
||||
return res;
|
||||
} else {
|
||||
LLVMValueRef a = LLVMBuildSRem(p->builder, lhs.value, rhs.value, "");
|
||||
LLVMValueRef b = LLVMBuildAdd(p->builder, a, rhs.value, "");
|
||||
LLVMValueRef c = LLVMBuildSRem(p->builder, b, rhs.value, "");
|
||||
res.value = c;
|
||||
return res;
|
||||
}
|
||||
res.value = lb_integer_modulo(p, lhs.value, rhs.value, is_type_unsigned(integral_type), /*is_floored*/true);
|
||||
return res;
|
||||
|
||||
case Token_And:
|
||||
res.value = LLVMBuildAnd(p->builder, lhs.value, rhs.value, "");
|
||||
@@ -1559,16 +1870,24 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
|
||||
return lb_emit_conv(p, cmp, type);
|
||||
} else if (lb_is_empty_string_constant(be->right) && !is_type_union(be->left->tav.type)) {
|
||||
// `x == ""` or `x != ""`
|
||||
Type *str_type = t_string;
|
||||
if (is_type_string16(be->left->tav.type) || is_type_cstring16(be->left->tav.type)) {
|
||||
str_type = t_string16;
|
||||
}
|
||||
lbValue s = lb_build_expr(p, be->left);
|
||||
s = lb_emit_conv(p, s, t_string);
|
||||
s = lb_emit_conv(p, s, str_type);
|
||||
lbValue len = lb_string_len(p, s);
|
||||
lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0));
|
||||
Type *type = default_type(tv.type);
|
||||
return lb_emit_conv(p, cmp, type);
|
||||
} else if (lb_is_empty_string_constant(be->left) && !is_type_union(be->right->tav.type)) {
|
||||
// `"" == x` or `"" != x`
|
||||
Type *str_type = t_string;
|
||||
if (is_type_string16(be->right->tav.type) || is_type_cstring16(be->right->tav.type)) {
|
||||
str_type = t_string16;
|
||||
}
|
||||
lbValue s = lb_build_expr(p, be->right);
|
||||
s = lb_emit_conv(p, s, t_string);
|
||||
s = lb_emit_conv(p, s, str_type);
|
||||
lbValue len = lb_string_len(p, s);
|
||||
lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0));
|
||||
Type *type = default_type(tv.type);
|
||||
@@ -1656,6 +1975,8 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
|
||||
res.type = t;
|
||||
res.value = llvm_cstring(m, str);
|
||||
return res;
|
||||
} else if (src->kind == Type_Basic && src->Basic.kind == Basic_string16 && dst->Basic.kind == Basic_cstring16) {
|
||||
GB_PANIC("TODO(bill): UTF-16 string");
|
||||
}
|
||||
// if (is_type_float(dst)) {
|
||||
// return value;
|
||||
@@ -1795,6 +2116,38 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (is_type_cstring16(src) && is_type_u16_ptr(dst)) {
|
||||
return lb_emit_transmute(p, value, dst);
|
||||
}
|
||||
if (is_type_u16_ptr(src) && is_type_cstring16(dst)) {
|
||||
return lb_emit_transmute(p, value, dst);
|
||||
}
|
||||
if (is_type_cstring16(src) && is_type_u16_multi_ptr(dst)) {
|
||||
return lb_emit_transmute(p, value, dst);
|
||||
}
|
||||
if (is_type_u8_multi_ptr(src) && is_type_cstring16(dst)) {
|
||||
return lb_emit_transmute(p, value, dst);
|
||||
}
|
||||
if (is_type_cstring16(src) && is_type_rawptr(dst)) {
|
||||
return lb_emit_transmute(p, value, dst);
|
||||
}
|
||||
if (is_type_rawptr(src) && is_type_cstring16(dst)) {
|
||||
return lb_emit_transmute(p, value, dst);
|
||||
}
|
||||
|
||||
if (are_types_identical(src, t_cstring16) && are_types_identical(dst, t_string16)) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
lbValue c = lb_emit_conv(p, value, t_cstring16);
|
||||
auto args = array_make<lbValue>(temporary_allocator(), 1);
|
||||
args[0] = c;
|
||||
lbValue s = lb_emit_runtime_call(p, "cstring16_to_string16", args);
|
||||
return lb_emit_conv(p, s, dst);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// integer -> boolean
|
||||
if (is_type_integer(src) && is_type_boolean(dst)) {
|
||||
lbValue res = {};
|
||||
@@ -2296,6 +2649,29 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// [^]u16 <-> cstring16
|
||||
if (is_type_u16_multi_ptr(src) && is_type_cstring16(dst)) {
|
||||
return lb_emit_transmute(p, value, t);
|
||||
}
|
||||
if (is_type_cstring16(src) && is_type_u16_multi_ptr(dst)) {
|
||||
return lb_emit_transmute(p, value, t);
|
||||
}
|
||||
if (is_type_u16_ptr(src) && is_type_cstring16(dst)) {
|
||||
return lb_emit_transmute(p, value, t);
|
||||
}
|
||||
if (is_type_cstring16(src) && is_type_u16_ptr(dst)) {
|
||||
return lb_emit_transmute(p, value, t);
|
||||
}
|
||||
|
||||
|
||||
// []u16 <-> string16
|
||||
if (is_type_u16_slice(src) && is_type_string16(dst)) {
|
||||
return lb_emit_transmute(p, value, t);
|
||||
}
|
||||
if (is_type_string16(src) && is_type_u16_slice(dst)) {
|
||||
return lb_emit_transmute(p, value, t);
|
||||
}
|
||||
|
||||
// []byte/[]u8 <-> string
|
||||
if (is_type_u8_slice(src) && is_type_string(dst)) {
|
||||
return lb_emit_transmute(p, value, t);
|
||||
@@ -2304,6 +2680,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
|
||||
return lb_emit_transmute(p, value, t);
|
||||
}
|
||||
|
||||
|
||||
if (is_type_array_like(dst)) {
|
||||
Type *elem = base_array_type(dst);
|
||||
isize index_count = cast(isize)get_array_type_count(dst);
|
||||
@@ -2710,7 +3087,53 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
|
||||
return lb_compare_records(p, op_kind, left, right, b);
|
||||
}
|
||||
|
||||
|
||||
if (is_type_string16(a) || is_type_cstring16(a)) {
|
||||
if (is_type_cstring16(a) && is_type_cstring16(b)) {
|
||||
left = lb_emit_conv(p, left, t_cstring16);
|
||||
right = lb_emit_conv(p, right, t_cstring16);
|
||||
char const *runtime_procedure = nullptr;
|
||||
switch (op_kind) {
|
||||
case Token_CmpEq: runtime_procedure = "cstring16_eq"; break;
|
||||
case Token_NotEq: runtime_procedure = "cstring16_ne"; break;
|
||||
case Token_Lt: runtime_procedure = "cstring16_lt"; break;
|
||||
case Token_Gt: runtime_procedure = "cstring16_gt"; break;
|
||||
case Token_LtEq: runtime_procedure = "cstring16_le"; break;
|
||||
case Token_GtEq: runtime_procedure = "cstring16_ge"; break;
|
||||
}
|
||||
GB_ASSERT(runtime_procedure != nullptr);
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 2);
|
||||
args[0] = left;
|
||||
args[1] = right;
|
||||
return lb_emit_runtime_call(p, runtime_procedure, args);
|
||||
}
|
||||
|
||||
|
||||
if (is_type_cstring16(a) ^ is_type_cstring16(b)) {
|
||||
left = lb_emit_conv(p, left, t_string16);
|
||||
right = lb_emit_conv(p, right, t_string16);
|
||||
}
|
||||
|
||||
char const *runtime_procedure = nullptr;
|
||||
switch (op_kind) {
|
||||
case Token_CmpEq: runtime_procedure = "string16_eq"; break;
|
||||
case Token_NotEq: runtime_procedure = "string16_ne"; break;
|
||||
case Token_Lt: runtime_procedure = "string16_lt"; break;
|
||||
case Token_Gt: runtime_procedure = "string16_gt"; break;
|
||||
case Token_LtEq: runtime_procedure = "string16_le"; break;
|
||||
case Token_GtEq: runtime_procedure = "string16_ge"; break;
|
||||
}
|
||||
GB_ASSERT(runtime_procedure != nullptr);
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 2);
|
||||
args[0] = left;
|
||||
args[1] = right;
|
||||
return lb_emit_runtime_call(p, runtime_procedure, args);
|
||||
}
|
||||
|
||||
if (is_type_string(a)) {
|
||||
|
||||
if (is_type_cstring(a) && is_type_cstring(b)) {
|
||||
left = lb_emit_conv(p, left, t_cstring);
|
||||
right = lb_emit_conv(p, right, t_cstring);
|
||||
@@ -3056,6 +3479,13 @@ gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind,
|
||||
res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
|
||||
}
|
||||
return res;
|
||||
case Basic_cstring16:
|
||||
if (op_kind == Token_CmpEq) {
|
||||
res.value = LLVMBuildIsNull(p->builder, x.value, "");
|
||||
} else if (op_kind == Token_NotEq) {
|
||||
res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
|
||||
}
|
||||
return res;
|
||||
case Basic_any:
|
||||
{
|
||||
// TODO(bill): is this correct behaviour for nil comparison for any?
|
||||
@@ -4298,12 +4728,13 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
|
||||
|
||||
case Type_Basic: { // Basic_string
|
||||
case Type_Basic: { // Basic_string/Basic_string16
|
||||
lbValue str;
|
||||
lbValue elem;
|
||||
lbValue len;
|
||||
lbValue index;
|
||||
|
||||
|
||||
str = lb_build_expr(p, ie->expr);
|
||||
if (deref) {
|
||||
str = lb_emit_load(p, str);
|
||||
@@ -4432,6 +4863,22 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
|
||||
case Type_Basic: {
|
||||
if (is_type_string16(type)) {
|
||||
GB_ASSERT_MSG(are_types_identical(type, t_string16), "got %s", type_to_string(type));
|
||||
lbValue len = lb_string_len(p, base);
|
||||
if (high.value == nullptr) high = len;
|
||||
|
||||
if (!no_indices) {
|
||||
lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
|
||||
}
|
||||
|
||||
lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low);
|
||||
lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
|
||||
|
||||
lbAddr str = lb_add_local_generated(p, t_string16, false);
|
||||
lb_fill_string(p, str, elem, new_len);
|
||||
return str;
|
||||
}
|
||||
GB_ASSERT_MSG(are_types_identical(type, t_string), "got %s", type_to_string(type));
|
||||
lbValue len = lb_string_len(p, base);
|
||||
if (high.value == nullptr) high = len;
|
||||
|
||||
@@ -85,6 +85,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
|
||||
string_map_init(&m->members);
|
||||
string_map_init(&m->procedures);
|
||||
string_map_init(&m->const_strings);
|
||||
string16_map_init(&m->const_string16s);
|
||||
map_init(&m->function_type_map);
|
||||
string_map_init(&m->gen_procs);
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
@@ -1812,6 +1813,37 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
return type;
|
||||
}
|
||||
case Basic_cstring: return LLVMPointerType(LLVMInt8TypeInContext(ctx), 0);
|
||||
|
||||
|
||||
case Basic_string16:
|
||||
{
|
||||
char const *name = "..string16";
|
||||
LLVMTypeRef type = LLVMGetTypeByName(m->mod, name);
|
||||
if (type != nullptr) {
|
||||
return type;
|
||||
}
|
||||
type = LLVMStructCreateNamed(ctx, name);
|
||||
|
||||
if (build_context.metrics.ptr_size < build_context.metrics.int_size) {
|
||||
GB_ASSERT(build_context.metrics.ptr_size == 4);
|
||||
GB_ASSERT(build_context.metrics.int_size == 8);
|
||||
LLVMTypeRef fields[3] = {
|
||||
LLVMPointerType(lb_type(m, t_u16), 0),
|
||||
lb_type(m, t_i32),
|
||||
lb_type(m, t_int),
|
||||
};
|
||||
LLVMStructSetBody(type, fields, 3, false);
|
||||
} else {
|
||||
LLVMTypeRef fields[2] = {
|
||||
LLVMPointerType(lb_type(m, t_u16), 0),
|
||||
lb_type(m, t_int),
|
||||
};
|
||||
LLVMStructSetBody(type, fields, 2, false);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
case Basic_cstring16: return LLVMPointerType(LLVMInt16TypeInContext(ctx), 0);
|
||||
|
||||
case Basic_any:
|
||||
{
|
||||
char const *name = "..any";
|
||||
@@ -2684,6 +2716,57 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String co
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal LLVMValueRef lb_find_or_add_entity_string16_ptr(lbModule *m, String16 const &str, bool custom_link_section) {
|
||||
String16HashKey key = {};
|
||||
LLVMValueRef *found = nullptr;
|
||||
|
||||
if (!custom_link_section) {
|
||||
key = string_hash_string(str);
|
||||
found = string16_map_get(&m->const_string16s, key);
|
||||
}
|
||||
if (found != nullptr) {
|
||||
return *found;
|
||||
}
|
||||
|
||||
|
||||
|
||||
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
|
||||
|
||||
LLVMValueRef data = nullptr;
|
||||
{
|
||||
LLVMTypeRef llvm_u16 = LLVMInt16TypeInContext(m->ctx);
|
||||
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, str.len+1);
|
||||
|
||||
for (isize i = 0; i < str.len; i++) {
|
||||
values[i] = LLVMConstInt(llvm_u16, str.text[i], false);
|
||||
}
|
||||
values[str.len] = LLVMConstInt(llvm_u16, 0, false);
|
||||
|
||||
data = LLVMConstArray(llvm_u16, values, cast(unsigned)(str.len+1));
|
||||
}
|
||||
|
||||
|
||||
u32 id = m->global_array_index.fetch_add(1);
|
||||
gbString name = gb_string_make(temporary_allocator(), "csbs$");
|
||||
name = gb_string_appendc(name, m->module_name);
|
||||
name = gb_string_append_fmt(name, "$%x", id);
|
||||
|
||||
LLVMTypeRef type = LLVMTypeOf(data);
|
||||
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
|
||||
LLVMSetInitializer(global_data, data);
|
||||
lb_make_global_private_const(global_data);
|
||||
LLVMSetAlignment(global_data, 2);
|
||||
|
||||
LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2);
|
||||
if (!custom_link_section) {
|
||||
string16_map_set(&m->const_string16s, key, ptr);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str, bool custom_link_section) {
|
||||
LLVMValueRef ptr = nullptr;
|
||||
if (str.len != 0) {
|
||||
@@ -2744,6 +2827,60 @@ gb_internal lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule *
|
||||
return res;
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_find_or_add_entity_string16_slice_with_type(lbModule *m, String16 const &str, Type *slice_type) {
|
||||
GB_ASSERT(is_type_slice(slice_type));
|
||||
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
|
||||
LLVMValueRef data = nullptr;
|
||||
{
|
||||
LLVMTypeRef llvm_u16 = LLVMInt16TypeInContext(m->ctx);
|
||||
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, str.len+1);
|
||||
|
||||
for (isize i = 0; i < str.len; i++) {
|
||||
values[i] = LLVMConstInt(llvm_u16, str.text[i], false);
|
||||
}
|
||||
values[str.len] = LLVMConstInt(llvm_u16, 0, false);
|
||||
|
||||
data = LLVMConstArray(llvm_u16, values, cast(unsigned)(str.len+1));
|
||||
}
|
||||
|
||||
u32 id = m->global_array_index.fetch_add(1);
|
||||
gbString name = gb_string_make(temporary_allocator(), "csba$");
|
||||
name = gb_string_appendc(name, m->module_name);
|
||||
name = gb_string_append_fmt(name, "$%x", id);
|
||||
|
||||
LLVMTypeRef type = LLVMTypeOf(data);
|
||||
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
|
||||
LLVMSetInitializer(global_data, data);
|
||||
lb_make_global_private_const(global_data);
|
||||
LLVMSetAlignment(global_data, 2);
|
||||
|
||||
i64 data_len = str.len;
|
||||
LLVMValueRef ptr = nullptr;
|
||||
if (data_len != 0) {
|
||||
ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2);
|
||||
} else {
|
||||
ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
|
||||
}
|
||||
if (!is_type_u16_slice(slice_type)) {
|
||||
Type *bt = base_type(slice_type);
|
||||
Type *elem = bt->Slice.elem;
|
||||
i64 sz = type_size_of(elem);
|
||||
GB_ASSERT(sz > 0);
|
||||
ptr = LLVMConstPointerCast(ptr, lb_type(m, alloc_type_pointer(elem)));
|
||||
data_len /= sz;
|
||||
}
|
||||
|
||||
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), data_len, true);
|
||||
LLVMValueRef values[2] = {ptr, len};
|
||||
|
||||
lbValue res = {};
|
||||
res.value = llvm_const_named_struct(m, slice_type, values, 2);
|
||||
res.type = slice_type;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) {
|
||||
@@ -2807,6 +2944,7 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e
|
||||
gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
|
||||
lbGenerator *gen = m->gen;
|
||||
|
||||
GB_ASSERT(e != nullptr);
|
||||
GB_ASSERT(is_type_proc(e->type));
|
||||
e = strip_entity_wrapping(e);
|
||||
GB_ASSERT(e != nullptr);
|
||||
|
||||
@@ -1024,6 +1024,7 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue
|
||||
gb_internal lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name) {
|
||||
AstPackage *pkg = m->info->runtime_package;
|
||||
Entity *e = scope_lookup_current(pkg->scope, name);
|
||||
GB_ASSERT_MSG(e != nullptr, "Runtime procedure not found: %s", name);
|
||||
return lb_find_procedure_value_from_entity(m, e);
|
||||
}
|
||||
|
||||
@@ -2328,6 +2329,10 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
}
|
||||
if (is_type_cstring(t)) {
|
||||
return lb_cstring_len(p, v);
|
||||
} else if (is_type_cstring16(t)) {
|
||||
return lb_cstring16_len(p, v);
|
||||
} else if (is_type_string16(t)) {
|
||||
return lb_string_len(p, v);
|
||||
} else if (is_type_string(t)) {
|
||||
return lb_string_len(p, v);
|
||||
} else if (is_type_array(t)) {
|
||||
@@ -2767,6 +2772,11 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
res = lb_emit_conv(p, res, tv.type);
|
||||
} else if (t->Basic.kind == Basic_cstring) {
|
||||
res = lb_emit_conv(p, x, tv.type);
|
||||
} else if (t->Basic.kind == Basic_string16) {
|
||||
res = lb_string_elem(p, x);
|
||||
res = lb_emit_conv(p, res, tv.type);
|
||||
} else if (t->Basic.kind == Basic_cstring16) {
|
||||
res = lb_emit_conv(p, x, tv.type);
|
||||
}
|
||||
break;
|
||||
case Type_Pointer:
|
||||
@@ -3331,16 +3341,22 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
}
|
||||
GB_ASSERT(name != nullptr);
|
||||
|
||||
LLVMTypeRef types[1] = {lb_type(p->module, platform_type)};
|
||||
lbValue res = {};
|
||||
res.type = platform_type;
|
||||
|
||||
LLVMValueRef args[3] = {
|
||||
if (id == BuiltinProc_fixed_point_div ||
|
||||
id == BuiltinProc_fixed_point_div_sat) {
|
||||
res.value = lb_integer_division_intrinsics(p, x.value, y.value, scale.value, platform_type, name);
|
||||
} else {
|
||||
LLVMTypeRef types[1] = {lb_type(p->module, platform_type)};
|
||||
|
||||
LLVMValueRef args[3] = {
|
||||
x.value,
|
||||
y.value,
|
||||
scale.value };
|
||||
|
||||
res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
|
||||
res.type = platform_type;
|
||||
res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
|
||||
}
|
||||
return lb_emit_conv(p, res, tv.type);
|
||||
}
|
||||
|
||||
@@ -3776,6 +3792,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
|
||||
case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
|
||||
case BuiltinProc_objc_ivar_get: return lb_handle_objc_ivar_get(p, expr);
|
||||
case BuiltinProc_objc_block: return lb_handle_objc_block(p, expr);
|
||||
|
||||
|
||||
case BuiltinProc_constant_utf16_cstring:
|
||||
|
||||
+120
-1
@@ -622,6 +622,121 @@ gb_internal void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_t
|
||||
if (done_) *done_ = done;
|
||||
}
|
||||
|
||||
gb_internal void lb_build_range_string16(lbProcedure *p, lbValue expr, Type *val_type,
|
||||
lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_,
|
||||
bool is_reverse) {
|
||||
|
||||
lbModule *m = p->module;
|
||||
lbValue count = lb_const_int(m, t_int, 0);
|
||||
Type *expr_type = base_type(expr.type);
|
||||
switch (expr_type->kind) {
|
||||
case Type_Basic:
|
||||
count = lb_string_len(p, expr);
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("Cannot do range_string of %s", type_to_string(expr_type));
|
||||
break;
|
||||
}
|
||||
|
||||
lbValue val = {};
|
||||
lbValue idx = {};
|
||||
lbBlock *loop = nullptr;
|
||||
lbBlock *done = nullptr;
|
||||
lbBlock *body = nullptr;
|
||||
|
||||
loop = lb_create_block(p, "for.string16.loop");
|
||||
body = lb_create_block(p, "for.string16.body");
|
||||
done = lb_create_block(p, "for.string16.done");
|
||||
|
||||
lbAddr offset_ = lb_add_local_generated(p, t_int, false);
|
||||
lbValue offset = {};
|
||||
lbValue cond = {};
|
||||
|
||||
if (!is_reverse) {
|
||||
/*
|
||||
for c, offset in str {
|
||||
...
|
||||
}
|
||||
|
||||
offset := 0
|
||||
for offset < len(str) {
|
||||
c, _w := string16_decode_rune(str[offset:])
|
||||
...
|
||||
offset += _w
|
||||
}
|
||||
*/
|
||||
lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
|
||||
|
||||
lb_emit_jump(p, loop);
|
||||
lb_start_block(p, loop);
|
||||
|
||||
|
||||
offset = lb_addr_load(p, offset_);
|
||||
cond = lb_emit_comp(p, Token_Lt, offset, count);
|
||||
} else {
|
||||
// NOTE(bill): REVERSED LOGIC
|
||||
/*
|
||||
#reverse for c, offset in str {
|
||||
...
|
||||
}
|
||||
|
||||
offset := len(str)
|
||||
for offset > 0 {
|
||||
c, _w := string16_decode_last_rune(str[:offset])
|
||||
offset -= _w
|
||||
...
|
||||
}
|
||||
*/
|
||||
lb_addr_store(p, offset_, count);
|
||||
|
||||
lb_emit_jump(p, loop);
|
||||
lb_start_block(p, loop);
|
||||
|
||||
offset = lb_addr_load(p, offset_);
|
||||
cond = lb_emit_comp(p, Token_Gt, offset, lb_const_int(m, t_int, 0));
|
||||
}
|
||||
lb_emit_if(p, cond, body, done);
|
||||
lb_start_block(p, body);
|
||||
|
||||
|
||||
lbValue rune_and_len = {};
|
||||
if (!is_reverse) {
|
||||
lbValue str_elem = lb_emit_ptr_offset(p, lb_string_elem(p, expr), offset);
|
||||
lbValue str_len = lb_emit_arith(p, Token_Sub, count, offset, t_int);
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 1);
|
||||
args[0] = lb_emit_string16(p, str_elem, str_len);
|
||||
|
||||
rune_and_len = lb_emit_runtime_call(p, "string16_decode_rune", args);
|
||||
lbValue len = lb_emit_struct_ev(p, rune_and_len, 1);
|
||||
lb_addr_store(p, offset_, lb_emit_arith(p, Token_Add, offset, len, t_int));
|
||||
|
||||
idx = offset;
|
||||
} else {
|
||||
// NOTE(bill): REVERSED LOGIC
|
||||
lbValue str_elem = lb_string_elem(p, expr);
|
||||
lbValue str_len = offset;
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 1);
|
||||
args[0] = lb_emit_string16(p, str_elem, str_len);
|
||||
|
||||
rune_and_len = lb_emit_runtime_call(p, "string16_decode_last_rune", args);
|
||||
lbValue len = lb_emit_struct_ev(p, rune_and_len, 1);
|
||||
lb_addr_store(p, offset_, lb_emit_arith(p, Token_Sub, offset, len, t_int));
|
||||
|
||||
idx = lb_addr_load(p, offset_);
|
||||
}
|
||||
|
||||
|
||||
if (val_type != nullptr) {
|
||||
val = lb_emit_struct_ev(p, rune_and_len, 0);
|
||||
}
|
||||
|
||||
if (val_) *val_ = val;
|
||||
if (idx_) *idx_ = idx;
|
||||
if (loop_) *loop_ = loop;
|
||||
if (done_) *done_ = done;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal Ast *lb_strip_and_prefix(Ast *ident) {
|
||||
if (ident != nullptr) {
|
||||
@@ -1138,7 +1253,11 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
|
||||
}
|
||||
Type *t = base_type(string.type);
|
||||
GB_ASSERT(!is_type_cstring(t));
|
||||
lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse);
|
||||
if (is_type_string16(t)) {
|
||||
lb_build_range_string16(p, string, val0_type, &val, &key, &loop, &done, rs->reverse);
|
||||
} else {
|
||||
lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type_Tuple:
|
||||
|
||||
@@ -525,14 +525,48 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
break;
|
||||
|
||||
case Basic_string:
|
||||
tag_type = t_type_info_string;
|
||||
{
|
||||
tag_type = t_type_info_string;
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_const_bool(m, t_bool, false).value,
|
||||
lb_const_int(m, t_type_info_string_encoding_kind, 0).value,
|
||||
};
|
||||
|
||||
variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
|
||||
}
|
||||
break;
|
||||
|
||||
case Basic_cstring:
|
||||
{
|
||||
tag_type = t_type_info_string;
|
||||
LLVMValueRef vals[1] = {
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_const_bool(m, t_bool, true).value,
|
||||
lb_const_int(m, t_type_info_string_encoding_kind, 0).value,
|
||||
};
|
||||
|
||||
variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
|
||||
}
|
||||
break;
|
||||
|
||||
case Basic_string16:
|
||||
{
|
||||
tag_type = t_type_info_string;
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_const_bool(m, t_bool, false).value,
|
||||
lb_const_int(m, t_type_info_string_encoding_kind, 1).value,
|
||||
};
|
||||
|
||||
variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case Basic_cstring16:
|
||||
{
|
||||
tag_type = t_type_info_string;
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_const_bool(m, t_bool, true).value,
|
||||
lb_const_int(m, t_type_info_string_encoding_kind, 1).value,
|
||||
};
|
||||
|
||||
variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
|
||||
|
||||
@@ -6,6 +6,7 @@ gb_internal bool lb_is_type_aggregate(Type *t) {
|
||||
case Type_Basic:
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_string:
|
||||
case Basic_string16:
|
||||
case Basic_any:
|
||||
return true;
|
||||
|
||||
@@ -190,6 +191,23 @@ gb_internal lbValue lb_emit_clamp(lbProcedure *p, Type *t, lbValue x, lbValue mi
|
||||
return z;
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_emit_string16(lbProcedure *p, lbValue str_elem, lbValue str_len) {
|
||||
if (false && lb_is_const(str_elem) && lb_is_const(str_len)) {
|
||||
LLVMValueRef values[2] = {
|
||||
str_elem.value,
|
||||
str_len.value,
|
||||
};
|
||||
lbValue res = {};
|
||||
res.type = t_string16;
|
||||
res.value = llvm_const_named_struct(p->module, t_string16, values, gb_count_of(values));
|
||||
return res;
|
||||
} else {
|
||||
lbAddr res = lb_add_local_generated(p, t_string16, false);
|
||||
lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 0), str_elem);
|
||||
lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 1), str_len);
|
||||
return lb_addr_load(p, res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) {
|
||||
@@ -981,7 +999,8 @@ gb_internal i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index) {
|
||||
} else if (build_context.ptr_size != build_context.int_size) {
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
if (t->Basic.kind != Basic_string) {
|
||||
if (t->Basic.kind != Basic_string &&
|
||||
t->Basic.kind != Basic_string16) {
|
||||
break;
|
||||
}
|
||||
/*fallthrough*/
|
||||
@@ -1160,6 +1179,11 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
|
||||
case 0: result_type = alloc_type_pointer(t->Slice.elem); break;
|
||||
case 1: result_type = t_int; break;
|
||||
}
|
||||
} else if (is_type_string16(t)) {
|
||||
switch (index) {
|
||||
case 0: result_type = t_u16_ptr; break;
|
||||
case 1: result_type = t_int; break;
|
||||
}
|
||||
} else if (is_type_string(t)) {
|
||||
switch (index) {
|
||||
case 0: result_type = t_u8_ptr; break;
|
||||
@@ -1273,6 +1297,12 @@ gb_internal lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_string16:
|
||||
switch (index) {
|
||||
case 0: result_type = t_u16_ptr; break;
|
||||
case 1: result_type = t_int; break;
|
||||
}
|
||||
break;
|
||||
case Basic_string:
|
||||
switch (index) {
|
||||
case 0: result_type = t_u8_ptr; break;
|
||||
@@ -1440,6 +1470,10 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection
|
||||
e = lb_emit_struct_ep(p, e, index);
|
||||
break;
|
||||
|
||||
case Basic_string16:
|
||||
e = lb_emit_struct_ep(p, e, index);
|
||||
break;
|
||||
|
||||
default:
|
||||
GB_PANIC("un-gep-able type %s", type_to_string(type));
|
||||
break;
|
||||
@@ -1626,11 +1660,17 @@ gb_internal void lb_fill_string(lbProcedure *p, lbAddr const &string, lbValue ba
|
||||
|
||||
gb_internal lbValue lb_string_elem(lbProcedure *p, lbValue string) {
|
||||
Type *t = base_type(string.type);
|
||||
if (t->kind == Type_Basic && t->Basic.kind == Basic_string16) {
|
||||
return lb_emit_struct_ev(p, string, 0);
|
||||
}
|
||||
GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
|
||||
return lb_emit_struct_ev(p, string, 0);
|
||||
}
|
||||
gb_internal lbValue lb_string_len(lbProcedure *p, lbValue string) {
|
||||
Type *t = base_type(string.type);
|
||||
if (t->kind == Type_Basic && t->Basic.kind == Basic_string16) {
|
||||
return lb_emit_struct_ev(p, string, 1);
|
||||
}
|
||||
GB_ASSERT_MSG(t->kind == Type_Basic && t->Basic.kind == Basic_string, "%s", type_to_string(t));
|
||||
return lb_emit_struct_ev(p, string, 1);
|
||||
}
|
||||
@@ -1641,6 +1681,12 @@ gb_internal lbValue lb_cstring_len(lbProcedure *p, lbValue value) {
|
||||
args[0] = lb_emit_conv(p, value, t_cstring);
|
||||
return lb_emit_runtime_call(p, "cstring_len", args);
|
||||
}
|
||||
gb_internal lbValue lb_cstring16_len(lbProcedure *p, lbValue value) {
|
||||
GB_ASSERT(is_type_cstring16(value.type));
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 1);
|
||||
args[0] = lb_emit_conv(p, value, t_cstring16);
|
||||
return lb_emit_runtime_call(p, "cstring16_len", args);
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbValue lb_array_elem(lbProcedure *p, lbValue array_ptr) {
|
||||
@@ -2217,6 +2263,397 @@ gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) {
|
||||
return lb_handle_objc_ivar_for_objc_object_pointer(p, self);
|
||||
}
|
||||
|
||||
gb_internal void lb_create_objc_block_helper_procs(
|
||||
lbModule *m, LLVMTypeRef block_lit_type, isize capture_field_offset,
|
||||
Slice<lbValue> capture_values, Slice<isize> objc_object_indices,
|
||||
lbProcedure *&out_copy_helper, lbProcedure *&out_dispose_helper
|
||||
) {
|
||||
gbString copy_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_copy_helper_%lld", m->objc_next_block_id);
|
||||
gbString dispose_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_dispose_helper_%lld", m->objc_next_block_id);
|
||||
|
||||
// copy: Block_Literal *dst, Block_Literal *src, i32 field_apropos
|
||||
// dispose: Block_Literal *src, i32 field_apropos
|
||||
Type *types[3] = { t_rawptr, t_rawptr, t_i32 };
|
||||
|
||||
Type *copy_tuple = alloc_type_tuple_from_field_types(types, 3, false, true);
|
||||
Type *dispose_tuple = alloc_type_tuple_from_field_types(&types[1], 2, false, true);
|
||||
|
||||
Type *copy_proc_type = alloc_type_proc(nullptr, copy_tuple, 3, nullptr, 0, false, ProcCC_CDecl);
|
||||
Type *dispose_proc_type = alloc_type_proc(nullptr, dispose_tuple, 2, nullptr, 0, false, ProcCC_CDecl);
|
||||
|
||||
lbProcedure *copy_proc = lb_create_dummy_procedure(m, make_string((u8*)copy_helper_name, gb_string_length(copy_helper_name)), copy_proc_type);
|
||||
lbProcedure *dispose_proc = lb_create_dummy_procedure(m, make_string((u8*)dispose_helper_name, gb_string_length(dispose_helper_name)), dispose_proc_type);
|
||||
LLVMSetLinkage(copy_proc->value, LLVMPrivateLinkage);
|
||||
LLVMSetLinkage(dispose_proc->value, LLVMPrivateLinkage);
|
||||
|
||||
|
||||
const int BLOCK_FIELD_IS_OBJECT = 3; // id, NSObject, __attribute__((NSObject)), block, ...
|
||||
const int BLOCK_FIELD_IS_BLOCK = 7; // a block variable
|
||||
|
||||
Type *block_base_type = find_core_type(m->info->checker, str_lit("Objc_Block"));
|
||||
|
||||
auto is_object_objc_block = [](Type *type, Type *block_base_type) -> bool {
|
||||
|
||||
Type *base = base_type(type_deref(type));
|
||||
GB_ASSERT(base->kind == Type_Struct);
|
||||
|
||||
while (is_type_polymorphic_record_specialized(base)) {
|
||||
if (base->Struct.polymorphic_parent) {
|
||||
base = base->Struct.polymorphic_parent;
|
||||
|
||||
if (base == block_base_type) {
|
||||
return true;
|
||||
}
|
||||
base = base_type(base);
|
||||
GB_ASSERT(base->kind == Type_Struct);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
lb_begin_procedure_body(copy_proc);
|
||||
lb_begin_procedure_body(dispose_proc);
|
||||
{
|
||||
for (isize object_index : objc_object_indices) {
|
||||
const auto field_offset = unsigned(capture_field_offset+object_index);
|
||||
|
||||
Type *field_type = capture_values[object_index].type;
|
||||
LLVMTypeRef field_raw_type = lb_type(m, field_type);
|
||||
|
||||
GB_ASSERT(is_type_objc_object(field_type));
|
||||
bool is_block_obj = is_object_objc_block(field_type, block_base_type);
|
||||
|
||||
auto copy_args = array_make<lbValue>(temporary_allocator(), 3, 3);
|
||||
auto dispose_args = array_make<lbValue>(temporary_allocator(), 2, 2);
|
||||
|
||||
// Copy helper
|
||||
{
|
||||
LLVMValueRef dst_field = LLVMBuildStructGEP2(copy_proc->builder, block_lit_type, copy_proc->raw_input_parameters[0], field_offset, "");
|
||||
LLVMValueRef src_field = LLVMBuildStructGEP2(copy_proc->builder, block_lit_type, copy_proc->raw_input_parameters[1], field_offset, "");
|
||||
|
||||
lbValue dst_value = {}, src_value = {};
|
||||
dst_value.type = alloc_type_pointer(field_type);
|
||||
dst_value.value = dst_field;
|
||||
|
||||
src_value.type = field_type;
|
||||
src_value.value = LLVMBuildLoad2(copy_proc->builder, field_raw_type, src_field, "");
|
||||
|
||||
copy_args[0] = dst_value;
|
||||
copy_args[1] = src_value;
|
||||
copy_args[2] = lb_const_int(m, t_i32, u64(is_block_obj ? BLOCK_FIELD_IS_BLOCK : BLOCK_FIELD_IS_OBJECT));
|
||||
|
||||
lb_emit_runtime_call(copy_proc, "_Block_object_assign", copy_args);
|
||||
}
|
||||
|
||||
// Dispose helper
|
||||
{
|
||||
LLVMValueRef src_field = LLVMBuildStructGEP2(dispose_proc->builder, block_lit_type, dispose_proc->raw_input_parameters[0], field_offset, "");
|
||||
lbValue src_value = {};
|
||||
src_value.type = field_type;
|
||||
src_value.value = LLVMBuildLoad2(dispose_proc->builder, field_raw_type, src_field, "");
|
||||
|
||||
dispose_args[0] = src_value;
|
||||
dispose_args[1] = lb_const_int(m, t_i32, u64(is_block_obj ? BLOCK_FIELD_IS_BLOCK : BLOCK_FIELD_IS_OBJECT));
|
||||
|
||||
lb_emit_runtime_call(dispose_proc, "_Block_object_dispose", dispose_args);
|
||||
}
|
||||
}
|
||||
}
|
||||
lb_end_procedure_body(copy_proc);
|
||||
lb_end_procedure_body(dispose_proc);
|
||||
|
||||
|
||||
out_copy_helper = copy_proc;
|
||||
out_dispose_helper = dispose_proc;
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
||||
/// #See: https://clang.llvm.org/docs/Block-ABI-Apple.html
|
||||
/// https://www.newosxbook.com/src.php?tree=xnu&file=/libkern/libkern/Block_private.h
|
||||
/// https://github.com/llvm/llvm-project/blob/21f1f9558df3830ffa637def364e3c0cb0dbb3c0/compiler-rt/lib/BlocksRuntime/Block_private.h
|
||||
/// https://github.com/apple-oss-distributions/libclosure/blob/3668b0837f47be3cc1c404fb5e360f4ff178ca13/runtime.cpp
|
||||
|
||||
ast_node(ce, CallExpr, expr);
|
||||
GB_ASSERT(ce->args.count > 0);
|
||||
|
||||
lbModule *m = p->module;
|
||||
|
||||
m->objc_next_block_id += 1;
|
||||
|
||||
const isize capture_arg_count = ce->args.count - 1;
|
||||
|
||||
Type *block_result_type = type_of_expr(expr);
|
||||
GB_ASSERT(block_result_type != nullptr && block_result_type->kind == Type_Pointer);
|
||||
|
||||
LLVMTypeRef lb_type_rawptr = lb_type(m, t_rawptr);
|
||||
LLVMTypeRef lb_type_i32 = lb_type(m, t_i32);
|
||||
LLVMTypeRef lb_type_int = lb_type(m, t_int);
|
||||
|
||||
// Build user proc
|
||||
// Type * user_proc_type = type_of_expr(ce->args[capture_arg_count]);
|
||||
lbValue user_proc_value = lb_build_expr(p, ce->args[capture_arg_count]);
|
||||
auto& user_proc = user_proc_value.type->Proc;
|
||||
GB_ASSERT(user_proc_value.type->kind == Type_Proc);
|
||||
|
||||
const bool is_global = capture_arg_count == 0 && user_proc.calling_convention != ProcCC_Odin;
|
||||
const isize block_forward_args = user_proc.param_count - capture_arg_count;
|
||||
const isize capture_fields_offset = user_proc.calling_convention != ProcCC_Odin ? 5 : 6;
|
||||
|
||||
Ast *proc_lit = unparen_expr(ce->args[capture_arg_count]);
|
||||
if (proc_lit->kind == Ast_Ident) {
|
||||
proc_lit = proc_lit->Ident.entity->decl_info->proc_lit;
|
||||
}
|
||||
GB_ASSERT(proc_lit->kind == Ast_ProcLit);
|
||||
|
||||
lbProcedure *copy_helper = {}, *dispose_helper = {};
|
||||
|
||||
// Build captured arguments & collect the ones that are Objective-C objects
|
||||
auto captured_values = array_make<lbValue>(temporary_allocator(), capture_arg_count, capture_arg_count);
|
||||
auto objc_captures = array_make<isize>(temporary_allocator());
|
||||
|
||||
for (isize i = 0; i < capture_arg_count; i++) {
|
||||
captured_values[i] = lb_build_expr(p, ce->args[i]);
|
||||
|
||||
if (is_type_pointer(captured_values[i].type) && is_type_objc_object(captured_values[i].type)) {
|
||||
array_add(&objc_captures, i);
|
||||
}
|
||||
}
|
||||
|
||||
const bool has_objc_fields = objc_captures.count > 0;
|
||||
|
||||
|
||||
// Create proc with the block signature
|
||||
// (takes a block literal pointer as the first parameter, followed by any expected ones from the user's proc)
|
||||
gbString block_invoker_name = gb_string_append_fmt(gb_string_make(permanent_allocator(), ""), "__$objc_block_invoker_%lld", m->objc_next_block_id);
|
||||
|
||||
// Add + 1 because the first parameter received is the block literal pointer itself
|
||||
auto invoker_args = array_make<Type *>(temporary_allocator(), block_forward_args + 1, block_forward_args + 1);
|
||||
invoker_args[0] = t_rawptr;
|
||||
|
||||
GB_ASSERT(block_forward_args <= user_proc.param_count);
|
||||
if (user_proc.param_count > 0) {
|
||||
Slice<Entity *> user_proc_param_types = user_proc.params->Tuple.variables;
|
||||
for (isize i = 0; i < block_forward_args; i++) {
|
||||
invoker_args[i+1] = user_proc_param_types[i]->type;
|
||||
}
|
||||
}
|
||||
|
||||
GB_ASSERT(user_proc.result_count <= 1);
|
||||
|
||||
Type *invoker_args_tuple = alloc_type_tuple_from_field_types(invoker_args.data, invoker_args.count, false, true);
|
||||
Type *invoker_results_tuple = nullptr;
|
||||
if (user_proc.result_count > 0) {
|
||||
invoker_results_tuple = alloc_type_tuple_from_field_types(&user_proc.results->Tuple.variables[0]->type, 1, false, true);
|
||||
}
|
||||
|
||||
Type *invoker_proc_type = alloc_type_proc(nullptr, invoker_args_tuple, invoker_args_tuple->Tuple.variables.count,
|
||||
invoker_results_tuple, user_proc.result_count, false, ProcCC_CDecl);
|
||||
|
||||
lbProcedure *invoker_proc = lb_create_dummy_procedure(m, make_string((u8*)block_invoker_name,
|
||||
gb_string_length(block_invoker_name)), invoker_proc_type);
|
||||
LLVMSetLinkage(invoker_proc->value, LLVMPrivateLinkage);
|
||||
|
||||
// Create the block descriptor and block literal
|
||||
gbString block_lit_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Literal_");
|
||||
block_lit_type_name = gb_string_append_fmt(block_lit_type_name, "%lld", m->objc_next_block_id);
|
||||
|
||||
gbString block_desc_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Descriptor_");
|
||||
block_desc_type_name = gb_string_append_fmt(block_desc_type_name, "%lld", m->objc_next_block_id);
|
||||
|
||||
LLVMTypeRef block_lit_type = {};
|
||||
LLVMTypeRef block_desc_type = {};
|
||||
LLVMValueRef block_desc_initializer = {};
|
||||
|
||||
{
|
||||
block_desc_type = LLVMStructCreateNamed(m->ctx, block_desc_type_name);
|
||||
|
||||
LLVMTypeRef fields_types[4] = {
|
||||
lb_type_int, // Reserved
|
||||
lb_type_int, // Block size
|
||||
lb_type_rawptr, // Copy helper func pointer
|
||||
lb_type_rawptr, // Dispose helper func pointer
|
||||
};
|
||||
|
||||
LLVMStructSetBody(block_desc_type, fields_types, has_objc_fields ? 4 : 2, false);
|
||||
}
|
||||
|
||||
{
|
||||
block_lit_type = LLVMStructCreateNamed(m->ctx, block_lit_type_name);
|
||||
|
||||
auto fields = array_make<LLVMTypeRef>(temporary_allocator());
|
||||
|
||||
array_add(&fields, lb_type_rawptr); // isa
|
||||
array_add(&fields, lb_type_i32); // flags
|
||||
array_add(&fields, lb_type_i32); // reserved
|
||||
array_add(&fields, lb_type_rawptr); // invoke
|
||||
array_add(&fields, block_desc_type); // descriptor
|
||||
|
||||
if (user_proc.calling_convention == ProcCC_Odin) {
|
||||
array_add(&fields, lb_type(m, t_context)); // context
|
||||
}
|
||||
|
||||
// From here on, fields for the captured vars are added
|
||||
for (lbValue cap_arg : captured_values) {
|
||||
array_add(&fields, lb_type(m, cap_arg.type));
|
||||
}
|
||||
|
||||
LLVMStructSetBody(block_lit_type, fields.data, (unsigned)fields.count, false);
|
||||
}
|
||||
|
||||
// Generate copy and dispose helper functions for captured params that are Objective-C objects (or a Block)
|
||||
if (has_objc_fields) {
|
||||
lb_create_objc_block_helper_procs(m, block_lit_type, capture_fields_offset,
|
||||
slice(captured_values, 0, captured_values.count),
|
||||
slice(objc_captures, 0, objc_captures.count),
|
||||
copy_helper, dispose_helper);
|
||||
}
|
||||
|
||||
{
|
||||
LLVMValueRef fields_values[4] = {
|
||||
lb_const_int(m, t_int, 0).value, // Reserved
|
||||
lb_const_int(m, t_int, u64(lb_sizeof(block_lit_type))).value, // Block size
|
||||
has_objc_fields ? copy_helper->value : nullptr, // Copy helper
|
||||
has_objc_fields ? dispose_helper->value : nullptr, // Dispose helper
|
||||
};
|
||||
|
||||
block_desc_initializer = LLVMConstNamedStruct(block_desc_type, fields_values, has_objc_fields ? 4 : 2);
|
||||
}
|
||||
|
||||
// Create global block descriptor
|
||||
gbString desc_global_name = gb_string_make(temporary_allocator(), "__$objc_block_desc_");
|
||||
desc_global_name = gb_string_append_fmt(desc_global_name, "%lld", m->objc_next_block_id);
|
||||
|
||||
LLVMValueRef p_descriptor = LLVMAddGlobal(m->mod, block_desc_type, desc_global_name);
|
||||
LLVMSetInitializer(p_descriptor, block_desc_initializer);
|
||||
|
||||
|
||||
/// Invoker body
|
||||
lb_begin_procedure_body(invoker_proc);
|
||||
{
|
||||
auto call_args = array_make<lbValue>(temporary_allocator(), user_proc.param_count, user_proc.param_count);
|
||||
|
||||
for (isize i = 1; i < invoker_proc->raw_input_parameters.count; i++) {
|
||||
lbValue arg = {};
|
||||
arg.type = invoker_args[i];
|
||||
arg.value = invoker_proc->raw_input_parameters[i],
|
||||
call_args[i-1] = arg;
|
||||
}
|
||||
|
||||
LLVMValueRef block_literal = invoker_proc->raw_input_parameters[0];
|
||||
|
||||
// Push context, if needed
|
||||
if (user_proc.calling_convention == ProcCC_Odin) {
|
||||
LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context");
|
||||
lbValue ctx_val = {};
|
||||
ctx_val.type = t_context_ptr;
|
||||
ctx_val.value = p_context;
|
||||
|
||||
lb_push_context_onto_stack(invoker_proc, lb_addr(ctx_val));
|
||||
}
|
||||
|
||||
// Copy capture parameters from the block literal
|
||||
for (isize i = 0; i < capture_arg_count; i++) {
|
||||
LLVMValueRef cap_value = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, unsigned(capture_fields_offset + i), "");
|
||||
|
||||
lbValue cap_arg = {};
|
||||
cap_arg.value = cap_value;
|
||||
cap_arg.type = alloc_type_pointer(captured_values[i].type);
|
||||
|
||||
lbValue arg = lb_emit_load(invoker_proc, cap_arg);
|
||||
call_args[block_forward_args+i] = arg;
|
||||
}
|
||||
|
||||
lbValue result = lb_emit_call(invoker_proc, user_proc_value, call_args, proc_lit->ProcLit.inlining);
|
||||
|
||||
GB_ASSERT(user_proc.result_count <= 1);
|
||||
if (user_proc.result_count > 0) {
|
||||
GB_ASSERT(result.value != nullptr);
|
||||
LLVMBuildRet(p->builder, result.value);
|
||||
}
|
||||
}
|
||||
lb_end_procedure_body(invoker_proc);
|
||||
|
||||
|
||||
/// Create local block literal
|
||||
const int BLOCK_HAS_COPY_DISPOSE = (1 << 25);
|
||||
const int BLOCK_IS_GLOBAL = (1 << 28);
|
||||
|
||||
int raw_flags = is_global ? BLOCK_IS_GLOBAL : 0;
|
||||
if (has_objc_fields) {
|
||||
raw_flags |= BLOCK_HAS_COPY_DISPOSE;
|
||||
}
|
||||
|
||||
gbString block_var_name = gb_string_make(temporary_allocator(), "__$objc_block_literal_");
|
||||
block_var_name = gb_string_append_fmt(block_var_name, "%lld", m->objc_next_block_id);
|
||||
|
||||
lbValue result = {};
|
||||
result.type = block_result_type;
|
||||
|
||||
lbValue isa_val = lb_find_runtime_value(m, is_global ? str_lit("_NSConcreteGlobalBlock") : str_lit("_NSConcreteStackBlock"));
|
||||
lbValue flags_val = lb_const_int(m, t_i32, (u64)raw_flags);
|
||||
lbValue reserved_val = lb_const_int(m, t_i32, 0);
|
||||
|
||||
if (is_global) {
|
||||
LLVMValueRef p_block_lit = LLVMAddGlobal(m->mod, block_lit_type, block_var_name);
|
||||
result.value = p_block_lit;
|
||||
|
||||
LLVMValueRef fields_values[5] = {
|
||||
isa_val.value, // isa
|
||||
flags_val.value, // flags
|
||||
reserved_val.value, // reserved
|
||||
invoker_proc->value, // invoke
|
||||
p_descriptor // descriptor
|
||||
};
|
||||
|
||||
LLVMValueRef g_block_lit_initializer = LLVMConstNamedStruct(block_lit_type, fields_values, gb_count_of(fields_values));
|
||||
LLVMSetInitializer(p_block_lit, g_block_lit_initializer);
|
||||
|
||||
} else {
|
||||
LLVMValueRef p_block_lit = llvm_alloca(p, block_lit_type, lb_alignof(block_lit_type), block_var_name);
|
||||
result.value = p_block_lit;
|
||||
|
||||
// Initialize it
|
||||
LLVMValueRef f_isa = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 0, "isa");
|
||||
LLVMValueRef f_flags = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 1, "flags");
|
||||
LLVMValueRef f_reserved = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 2, "reserved");
|
||||
LLVMValueRef f_invoke = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 3, "invoke");
|
||||
LLVMValueRef f_descriptor = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 4, "descriptor");
|
||||
|
||||
LLVMBuildStore(p->builder, isa_val.value, f_isa);
|
||||
LLVMBuildStore(p->builder, flags_val.value, f_flags);
|
||||
LLVMBuildStore(p->builder, reserved_val.value, f_reserved);
|
||||
LLVMBuildStore(p->builder, invoker_proc->value, f_invoke);
|
||||
LLVMBuildStore(p->builder, p_descriptor, f_descriptor);
|
||||
|
||||
// Store current context, if there is one
|
||||
if (user_proc.calling_convention == ProcCC_Odin) {
|
||||
LLVMValueRef f_context = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 5, "context");
|
||||
lbAddr p_current_context = lb_find_or_generate_context_ptr(p);
|
||||
|
||||
LLVMValueRef context_size = LLVMConstInt(LLVMInt64TypeInContext(m->ctx), (u64)lb_sizeof(lb_type(m, t_context)), false);
|
||||
LLVMBuildMemCpy(p->builder, f_context, lb_try_get_alignment(f_context, 1),
|
||||
p_current_context.addr.value, lb_try_get_alignment(p_current_context.addr.value, 1), context_size);
|
||||
}
|
||||
|
||||
// Store captured args into the block
|
||||
for (isize i = 0; i < captured_values.count; i++) {
|
||||
lbValue capture_arg = captured_values[i];
|
||||
|
||||
unsigned field_index = unsigned(capture_fields_offset + i);
|
||||
LLVMValueRef f_capture = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, field_index, "capture_arg");
|
||||
|
||||
lbValue f_capture_val = {};
|
||||
f_capture_val.type = alloc_type_pointer(capture_arg.type);
|
||||
f_capture_val.value = f_capture;
|
||||
|
||||
lb_emit_store(p, f_capture_val, capture_arg);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
|
||||
ast_node(ce, CallExpr, expr);
|
||||
|
||||
|
||||
+37
-5
@@ -142,9 +142,9 @@ gb_internal i32 system_exec_command_line_app_internal(bool exit_on_err, char con
|
||||
}
|
||||
|
||||
wcmd = string_to_string16(permanent_allocator(), make_string(cast(u8 *)cmd_line, cmd_len-1));
|
||||
if (CreateProcessW(nullptr, wcmd.text,
|
||||
nullptr, nullptr, true, 0, nullptr, nullptr,
|
||||
&start_info, &pi)) {
|
||||
if (CreateProcessW(nullptr, cast(wchar_t *)wcmd.text,
|
||||
nullptr, nullptr, true, 0, nullptr, nullptr,
|
||||
&start_info, &pi)) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
|
||||
|
||||
@@ -232,7 +232,7 @@ gb_internal Array<String> setup_args(int argc, char const **argv) {
|
||||
wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc);
|
||||
auto args = array_make<String>(a, 0, wargc);
|
||||
for (isize i = 0; i < wargc; i++) {
|
||||
wchar_t *warg = wargv[i];
|
||||
u16 *warg = cast(u16 *)wargv[i];
|
||||
isize wlen = string16_len(warg);
|
||||
String16 wstr = make_string16(warg, wlen);
|
||||
String arg = string16_to_string(a, wstr);
|
||||
@@ -392,6 +392,8 @@ enum BuildFlagKind {
|
||||
|
||||
BuildFlag_PrintLinkerFlags,
|
||||
|
||||
BuildFlag_IntegerDivisionByZero,
|
||||
|
||||
// internal use only
|
||||
BuildFlag_InternalFastISel,
|
||||
BuildFlag_InternalIgnoreLazy,
|
||||
@@ -613,6 +615,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
|
||||
add_flag(&build_flags, BuildFlag_PrintLinkerFlags, str_lit("print-linker-flags"), BuildFlagParam_None, Command_build);
|
||||
|
||||
add_flag(&build_flags, BuildFlag_IntegerDivisionByZero, str_lit("integer-division-by-zero"), BuildFlagParam_String, Command__does_check);
|
||||
|
||||
|
||||
add_flag(&build_flags, BuildFlag_InternalFastISel, str_lit("internal-fast-isel"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None, Command_all);
|
||||
@@ -1515,7 +1520,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
} else if (str_eq_ignore_case(value.value_string, str_lit("unix"))) {
|
||||
build_context.ODIN_ERROR_POS_STYLE = ErrorPosStyle_Unix;
|
||||
} else {
|
||||
gb_printf_err("-error-pos-style options are 'unix', 'odin' and 'default' (odin)\n");
|
||||
gb_printf_err("-error-pos-style options are 'unix', 'odin', and 'default' (odin)\n");
|
||||
bad_flags = true;
|
||||
}
|
||||
break;
|
||||
@@ -1539,6 +1544,20 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
build_context.print_linker_flags = true;
|
||||
break;
|
||||
|
||||
case BuildFlag_IntegerDivisionByZero:
|
||||
GB_ASSERT(value.kind == ExactValue_String);
|
||||
if (str_eq_ignore_case(value.value_string, "trap")) {
|
||||
build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Trap;
|
||||
} else if (str_eq_ignore_case(value.value_string, "zero")) {
|
||||
build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Zero;
|
||||
} else if (str_eq_ignore_case(value.value_string, "self")) {
|
||||
build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Self;
|
||||
}else {
|
||||
gb_printf_err("-integer-division-by-zero options are 'trap', 'zero', and 'self'.\n");
|
||||
bad_flags = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case BuildFlag_InternalFastISel:
|
||||
build_context.fast_isel = true;
|
||||
break;
|
||||
@@ -2561,7 +2580,20 @@ gb_internal int print_show_help(String const arg0, String command, String option
|
||||
if (print_flag("-ignore-warnings")) {
|
||||
print_usage_line(2, "Ignores warning messages.");
|
||||
}
|
||||
}
|
||||
|
||||
if (check) {
|
||||
if (print_flag("-integer-division-by-zero:<string>")) {
|
||||
print_usage_line(2, "Specifies the default behaviour for integer division by zero.");
|
||||
print_usage_line(2, "Available Options:");
|
||||
print_usage_line(3, "-integer-division-by-zero:trap Trap on division/modulo/remainder by zero");
|
||||
print_usage_line(3, "-integer-division-by-zero:zero x/0 == 0 and x%%0 == x and x%%%%0 == x");
|
||||
print_usage_line(3, "-integer-division-by-zero:self x/0 == x and x%%0 == 0 and x%%%%0 == 0");
|
||||
print_usage_line(3, "-integer-division-by-zero:all-bits x/0 == ~T(0) and x%%0 == x and x%%%%0 == x");
|
||||
}
|
||||
}
|
||||
|
||||
if (check) {
|
||||
if (print_flag("-json-errors")) {
|
||||
print_usage_line(2, "Prints the error messages as json to stderr.");
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ struct Find_Result {
|
||||
};
|
||||
|
||||
gb_internal String mc_wstring_to_string(wchar_t const *str) {
|
||||
return string16_to_string(mc_allocator, make_string16_c(str));
|
||||
return string16_to_string(mc_allocator, make_string16_c(cast(u16 *)str));
|
||||
}
|
||||
|
||||
gb_internal String16 mc_string_to_wstring(String str) {
|
||||
@@ -103,7 +103,7 @@ gb_internal HANDLE mc_find_first(String wildcard, MC_Find_Data *find_data) {
|
||||
String16 wildcard_wide = mc_string_to_wstring(wildcard);
|
||||
defer (mc_free(wildcard_wide));
|
||||
|
||||
HANDLE handle = FindFirstFileW(wildcard_wide.text, &_find_data);
|
||||
HANDLE handle = FindFirstFileW(cast(wchar_t *)wildcard_wide.text, &_find_data);
|
||||
if (handle == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
|
||||
|
||||
find_data->file_attributes = _find_data.dwFileAttributes;
|
||||
|
||||
@@ -505,7 +505,13 @@ write_base_name:
|
||||
|
||||
Type *params = nullptr;
|
||||
Entity *parent = type_get_polymorphic_parent(e->type, ¶ms);
|
||||
if (parent && (parent->token.string == e->token.string)) {
|
||||
if (parent && (e->token.string == parent->token.string)) {
|
||||
// Check for `distinct` forms
|
||||
type_writer_append(w, parent->token.string.text, parent->token.string.len);
|
||||
write_canonical_params(w, params);
|
||||
} else if (parent && string_starts_with(e->token.string, parent->token.string) &&
|
||||
string_contains_char(e->token.string, '(')) {
|
||||
// Check for named specialization forms
|
||||
type_writer_append(w, parent->token.string.text, parent->token.string.len);
|
||||
write_canonical_params(w, params);
|
||||
} else {
|
||||
@@ -767,7 +773,6 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
|
||||
case Type_Named:
|
||||
if (type->Named.type_name != nullptr) {
|
||||
write_canonical_entity_name(w, type->Named.type_name);
|
||||
return;
|
||||
} else {
|
||||
type_writer_append(w, type->Named.name.text, type->Named.name.len);
|
||||
}
|
||||
|
||||
+31
-12
@@ -6327,7 +6327,7 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
|
||||
return any_correct;
|
||||
}
|
||||
|
||||
gb_internal String vet_tag_get_token(String s, String *out) {
|
||||
gb_internal String vet_tag_get_token(String s, String *out, bool allow_colon) {
|
||||
s = string_trim_whitespace(s);
|
||||
isize n = 0;
|
||||
while (n < s.len) {
|
||||
@@ -6335,7 +6335,7 @@ gb_internal String vet_tag_get_token(String s, String *out) {
|
||||
isize width = utf8_decode(&s[n], s.len-n, &rune);
|
||||
if (n == 0 && rune == '!') {
|
||||
|
||||
} else if (!rune_is_letter(rune) && !rune_is_digit(rune) && rune != '-') {
|
||||
} else if (!rune_is_letter(rune) && !rune_is_digit(rune) && rune != '-' && !(allow_colon && rune == ':')) {
|
||||
isize k = gb_max(gb_max(n, width), 1);
|
||||
*out = substring(s, k, s.len);
|
||||
return substring(s, 0, k);
|
||||
@@ -6361,7 +6361,7 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
|
||||
u64 vet_not_flags = 0;
|
||||
|
||||
while (s.len > 0) {
|
||||
String p = string_trim_whitespace(vet_tag_get_token(s, &s));
|
||||
String p = string_trim_whitespace(vet_tag_get_token(s, &s, /*allow_colon*/false));
|
||||
if (p.len == 0) {
|
||||
break;
|
||||
}
|
||||
@@ -6429,7 +6429,7 @@ gb_internal u64 parse_feature_tag(Token token_for_pos, String s) {
|
||||
u64 feature_not_flags = 0;
|
||||
|
||||
while (s.len > 0) {
|
||||
String p = string_trim_whitespace(vet_tag_get_token(s, &s));
|
||||
String p = string_trim_whitespace(vet_tag_get_token(s, &s, /*allow_colon*/true));
|
||||
if (p.len == 0) {
|
||||
break;
|
||||
}
|
||||
@@ -6451,26 +6451,45 @@ gb_internal u64 parse_feature_tag(Token token_for_pos, String s) {
|
||||
} else {
|
||||
feature_flags |= flag;
|
||||
}
|
||||
if (is_notted) {
|
||||
switch (flag) {
|
||||
case OptInFeatureFlag_IntegerDivisionByZero_Trap:
|
||||
case OptInFeatureFlag_IntegerDivisionByZero_Zero:
|
||||
syntax_error(token_for_pos, "Feature flag does not support notting with '!' - '%.*s'", LIT(p));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ERROR_BLOCK();
|
||||
syntax_error(token_for_pos, "Invalid feature flag name: %.*s", LIT(p));
|
||||
error_line("\tExpected one of the following\n");
|
||||
error_line("\tdynamic-literals\n");
|
||||
error_line("\tinteger-division-by-zero:trap\n");
|
||||
error_line("\tinteger-division-by-zero:zero\n");
|
||||
error_line("\tinteger-division-by-zero:self\n");
|
||||
return OptInFeatureFlag_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
u64 res = OptInFeatureFlag_NONE;
|
||||
|
||||
if (feature_flags == 0 && feature_not_flags == 0) {
|
||||
return OptInFeatureFlag_NONE;
|
||||
res = OptInFeatureFlag_NONE;
|
||||
} else if (feature_flags == 0 && feature_not_flags != 0) {
|
||||
res = OptInFeatureFlag_NONE &~ feature_not_flags;
|
||||
} else if (feature_flags != 0 && feature_not_flags == 0) {
|
||||
res = feature_flags;
|
||||
} else {
|
||||
GB_ASSERT(feature_flags != 0 && feature_not_flags != 0);
|
||||
res = feature_flags &~ feature_not_flags;
|
||||
}
|
||||
if (feature_flags == 0 && feature_not_flags != 0) {
|
||||
return OptInFeatureFlag_NONE &~ feature_not_flags;
|
||||
|
||||
u64 idbz_count = gb_count_set_bits(res & OptInFeatureFlag_IntegerDivisionByZero_ALL);
|
||||
if (idbz_count > 1) {
|
||||
syntax_error(token_for_pos, "Only one integer-division-by-zero feature flag can be enabled");
|
||||
}
|
||||
if (feature_flags != 0 && feature_not_flags == 0) {
|
||||
return feature_flags;
|
||||
}
|
||||
GB_ASSERT(feature_flags != 0 && feature_not_flags != 0);
|
||||
return feature_flags &~ feature_not_flags;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
gb_internal String dir_from_path(String path) {
|
||||
|
||||
+4
-4
@@ -130,7 +130,7 @@ gb_internal String directory_from_path(String const &s) {
|
||||
String16 wstr = string_to_string16(a, path);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
i32 attribs = GetFileAttributesW(wstr.text);
|
||||
i32 attribs = GetFileAttributesW(cast(wchar_t *)wstr.text);
|
||||
if (attribs < 0) return false;
|
||||
|
||||
return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
@@ -360,7 +360,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi)
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
WIN32_FIND_DATAW file_data = {};
|
||||
HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
|
||||
HANDLE find_file = FindFirstFileW(cast(wchar_t *)wstr.text, &file_data);
|
||||
if (find_file == INVALID_HANDLE_VALUE) {
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
@@ -372,7 +372,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi)
|
||||
wchar_t *filename_w = file_data.cFileName;
|
||||
u64 size = cast(u64)file_data.nFileSizeLow;
|
||||
size |= (cast(u64)file_data.nFileSizeHigh) << 32;
|
||||
String name = string16_to_string(a, make_string16_c(filename_w));
|
||||
String name = string16_to_string(a, make_string16_c(cast(u16 *)filename_w));
|
||||
if (name == "." || name == "..") {
|
||||
gb_free(a, name.text);
|
||||
continue;
|
||||
@@ -494,7 +494,7 @@ gb_internal bool write_directory(String path) {
|
||||
#else
|
||||
gb_internal bool write_directory(String path) {
|
||||
String16 wstr = string_to_string16(heap_allocator(), path);
|
||||
LPCWSTR wdirectory_name = wstr.text;
|
||||
LPCWSTR wdirectory_name = cast(wchar_t *)wstr.text;
|
||||
|
||||
HANDLE directory = CreateFileW(wdirectory_name,
|
||||
GENERIC_WRITE,
|
||||
|
||||
+162
-19
@@ -26,15 +26,14 @@ struct String_Iterator {
|
||||
|
||||
// NOTE(bill): String16 is only used for Windows due to its file directories
|
||||
struct String16 {
|
||||
wchar_t *text;
|
||||
isize len;
|
||||
wchar_t const &operator[](isize i) const {
|
||||
u16 * text;
|
||||
isize len;
|
||||
u16 const &operator[](isize i) const {
|
||||
GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
|
||||
return text[i];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
gb_internal gb_inline String make_string(u8 const *text, isize len) {
|
||||
String s;
|
||||
s.text = cast(u8 *)text;
|
||||
@@ -45,19 +44,19 @@ gb_internal gb_inline String make_string(u8 const *text, isize len) {
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
gb_internal gb_inline String16 make_string16(wchar_t const *text, isize len) {
|
||||
gb_internal gb_inline String16 make_string16(u16 const *text, isize len) {
|
||||
String16 s;
|
||||
s.text = cast(wchar_t *)text;
|
||||
s.text = cast(u16 *)text;
|
||||
s.len = len;
|
||||
return s;
|
||||
}
|
||||
|
||||
gb_internal isize string16_len(wchar_t const *s) {
|
||||
|
||||
gb_internal isize string16_len(u16 const *s) {
|
||||
if (s == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
wchar_t const *p = s;
|
||||
u16 const *p = s;
|
||||
while (*p) {
|
||||
p++;
|
||||
}
|
||||
@@ -69,7 +68,7 @@ gb_internal gb_inline String make_string_c(char const *text) {
|
||||
return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
|
||||
}
|
||||
|
||||
gb_internal gb_inline String16 make_string16_c(wchar_t const *text) {
|
||||
gb_internal gb_inline String16 make_string16_c(u16 const *text) {
|
||||
return make_string16(text, string16_len(text));
|
||||
}
|
||||
|
||||
@@ -80,6 +79,13 @@ gb_internal String substring(String const &s, isize lo, isize hi) {
|
||||
return make_string(s.text+lo, hi-lo);
|
||||
}
|
||||
|
||||
gb_internal String16 substring(String16 const &s, isize lo, isize hi) {
|
||||
isize max = s.len;
|
||||
GB_ASSERT_MSG(lo <= hi && hi <= max, "%td..%td..%td", lo, hi, max);
|
||||
|
||||
return make_string16(s.text+lo, hi-lo);
|
||||
}
|
||||
|
||||
|
||||
gb_internal char *alloc_cstring(gbAllocator a, String s) {
|
||||
char *c_str = gb_alloc_array(a, char, s.len+1);
|
||||
@@ -145,6 +151,27 @@ gb_internal int string_compare(String const &a, String const &b) {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
gb_internal int string16_compare(String16 const &a, String16 const &b) {
|
||||
if (a.text == b.text) {
|
||||
return cast(int)(a.len - b.len);
|
||||
}
|
||||
if (a.text == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
if (b.text == nullptr) {
|
||||
return +1;
|
||||
}
|
||||
|
||||
uintptr n = gb_min(a.len, b.len);
|
||||
int res = memcmp(a.text, b.text, n*gb_size_of(u16));
|
||||
if (res == 0) {
|
||||
res = cast(int)(a.len - b.len);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
gb_internal isize string_index_byte(String const &s, u8 x) {
|
||||
for (isize i = 0; i < s.len; i++) {
|
||||
if (s.text[i] == x) {
|
||||
@@ -182,6 +209,26 @@ template <isize N> gb_internal bool operator >= (String const &a, char const (&b
|
||||
template <> bool operator == (String const &a, char const (&b)[1]) { return a.len == 0; }
|
||||
template <> bool operator != (String const &a, char const (&b)[1]) { return a.len != 0; }
|
||||
|
||||
|
||||
gb_internal gb_inline bool str_eq(String16 const &a, String16 const &b) {
|
||||
if (a.len != b.len) return false;
|
||||
if (a.len == 0) return true;
|
||||
return memcmp(a.text, b.text, a.len) == 0;
|
||||
}
|
||||
gb_internal gb_inline bool str_ne(String16 const &a, String16 const &b) { return !str_eq(a, b); }
|
||||
gb_internal gb_inline bool str_lt(String16 const &a, String16 const &b) { return string16_compare(a, b) < 0; }
|
||||
gb_internal gb_inline bool str_gt(String16 const &a, String16 const &b) { return string16_compare(a, b) > 0; }
|
||||
gb_internal gb_inline bool str_le(String16 const &a, String16 const &b) { return string16_compare(a, b) <= 0; }
|
||||
gb_internal gb_inline bool str_ge(String16 const &a, String16 const &b) { return string16_compare(a, b) >= 0; }
|
||||
|
||||
gb_internal gb_inline bool operator == (String16 const &a, String16 const &b) { return str_eq(a, b); }
|
||||
gb_internal gb_inline bool operator != (String16 const &a, String16 const &b) { return str_ne(a, b); }
|
||||
gb_internal gb_inline bool operator < (String16 const &a, String16 const &b) { return str_lt(a, b); }
|
||||
gb_internal gb_inline bool operator > (String16 const &a, String16 const &b) { return str_gt(a, b); }
|
||||
gb_internal gb_inline bool operator <= (String16 const &a, String16 const &b) { return str_le(a, b); }
|
||||
gb_internal gb_inline bool operator >= (String16 const &a, String16 const &b) { return str_ge(a, b); }
|
||||
|
||||
|
||||
gb_internal gb_inline bool string_starts_with(String const &s, String const &prefix) {
|
||||
if (prefix.len > s.len) {
|
||||
return false;
|
||||
@@ -611,10 +658,9 @@ gb_internal String normalize_path(gbAllocator a, String const &path, String cons
|
||||
|
||||
|
||||
|
||||
// TODO(bill): Make this non-windows specific
|
||||
gb_internal String16 string_to_string16(gbAllocator a, String s) {
|
||||
int len, len1;
|
||||
wchar_t *text;
|
||||
u16 *text;
|
||||
|
||||
if (s.len < 1) {
|
||||
return make_string16(nullptr, 0);
|
||||
@@ -625,15 +671,14 @@ gb_internal String16 string_to_string16(gbAllocator a, String s) {
|
||||
return make_string16(nullptr, 0);
|
||||
}
|
||||
|
||||
text = gb_alloc_array(a, wchar_t, len+1);
|
||||
text = gb_alloc_array(a, u16, len+1);
|
||||
|
||||
len1 = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, text, cast(int)len);
|
||||
len1 = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, cast(wchar_t *)text, cast(int)len);
|
||||
if (len1 == 0) {
|
||||
gb_free(a, text);
|
||||
return make_string16(nullptr, 0);
|
||||
}
|
||||
text[len] = 0;
|
||||
|
||||
return make_string16(text, len);
|
||||
}
|
||||
|
||||
@@ -646,7 +691,7 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
|
||||
len = convert_widechar_to_multibyte(s.text, cast(int)s.len, nullptr, 0);
|
||||
len = convert_widechar_to_multibyte(cast(wchar_t *)s.text, cast(int)s.len, nullptr, 0);
|
||||
if (len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
@@ -654,7 +699,7 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) {
|
||||
|
||||
text = gb_alloc_array(a, u8, len+1);
|
||||
|
||||
len1 = convert_widechar_to_multibyte(s.text, cast(int)s.len, cast(char *)text, cast(int)len);
|
||||
len1 = convert_widechar_to_multibyte(cast(wchar_t *)s.text, cast(int)s.len, cast(char *)text, cast(int)len);
|
||||
if (len1 == 0) {
|
||||
gb_free(a, text);
|
||||
return make_string(nullptr, 0);
|
||||
@@ -674,9 +719,9 @@ gb_internal String temporary_directory(gbAllocator allocator) {
|
||||
return String{0};
|
||||
}
|
||||
DWORD len = gb_max(MAX_PATH, n);
|
||||
wchar_t *b = gb_alloc_array(heap_allocator(), wchar_t, len+1);
|
||||
u16 *b = gb_alloc_array(heap_allocator(), u16, len+1);
|
||||
defer (gb_free(heap_allocator(), b));
|
||||
n = GetTempPathW(len, b);
|
||||
n = GetTempPathW(len, cast(wchar_t *)b);
|
||||
if (n == 3 && b[1] == ':' && b[2] == '\\') {
|
||||
|
||||
} else if (n > 0 && b[n-1] == '\\') {
|
||||
@@ -791,6 +836,104 @@ gb_internal String quote_to_ascii(gbAllocator a, String str, u8 quote='"') {
|
||||
return res;
|
||||
}
|
||||
|
||||
gb_internal Rune decode_surrogate_pair(u16 r1, u16 r2) {
|
||||
static Rune const _surr1 = 0xd800;
|
||||
static Rune const _surr2 = 0xdc00;
|
||||
static Rune const _surr3 = 0xe000;
|
||||
static Rune const _surr_self = 0x10000;
|
||||
|
||||
if (_surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3) {
|
||||
return (((r1-_surr1)<<10) | (r2 - _surr2)) + _surr_self;
|
||||
}
|
||||
return GB_RUNE_INVALID;
|
||||
}
|
||||
|
||||
gb_internal String quote_to_ascii(gbAllocator a, String16 str, u8 quote='"') {
|
||||
static Rune const _surr1 = 0xd800;
|
||||
static Rune const _surr2 = 0xdc00;
|
||||
static Rune const _surr3 = 0xe000;
|
||||
static Rune const _surr_self = 0x10000;
|
||||
|
||||
u16 *s = cast(u16 *)str.text;
|
||||
isize n = str.len;
|
||||
auto buf = array_make<u8>(a, 0, n*2);
|
||||
array_add(&buf, quote);
|
||||
for (isize width = 0; n > 0; s += width, n -= width) {
|
||||
Rune r = cast(Rune)s[0];
|
||||
width = 1;
|
||||
if (r < _surr1 || _surr3 <= r) {
|
||||
r = cast(Rune)r;
|
||||
} else if (_surr1 <= r && r < _surr2) {
|
||||
if (n>1) {
|
||||
r = decode_surrogate_pair(s[0], s[1]);
|
||||
if (r != GB_RUNE_INVALID) {
|
||||
width = 2;
|
||||
}
|
||||
} else {
|
||||
r = GB_RUNE_INVALID;
|
||||
}
|
||||
}
|
||||
if (width == 1 && r == GB_RUNE_INVALID) {
|
||||
array_add(&buf, cast(u8)'\\');
|
||||
array_add(&buf, cast(u8)'x');
|
||||
array_add(&buf, cast(u8)lower_hex[s[0]>>4]);
|
||||
array_add(&buf, cast(u8)lower_hex[s[0]&0xf]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r == quote || r == '\\') {
|
||||
array_add(&buf, cast(u8)'\\');
|
||||
array_add(&buf, u8(r));
|
||||
continue;
|
||||
}
|
||||
if (r < 0x80 && is_printable(r)) {
|
||||
array_add(&buf, u8(r));
|
||||
continue;
|
||||
}
|
||||
switch (r) {
|
||||
case '\a':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
default:
|
||||
if (r < ' ') {
|
||||
u8 b = cast(u8)r;
|
||||
array_add(&buf, cast(u8)'\\');
|
||||
array_add(&buf, cast(u8)'x');
|
||||
array_add(&buf, cast(u8)lower_hex[b>>4]);
|
||||
array_add(&buf, cast(u8)lower_hex[b&0xf]);
|
||||
}
|
||||
if (r > GB_RUNE_MAX) {
|
||||
r = 0XFFFD;
|
||||
}
|
||||
if (r < 0x10000) {
|
||||
array_add(&buf, cast(u8)'\\');
|
||||
array_add(&buf, cast(u8)'u');
|
||||
for (isize i = 12; i >= 0; i -= 4) {
|
||||
array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]);
|
||||
}
|
||||
} else {
|
||||
array_add(&buf, cast(u8)'\\');
|
||||
array_add(&buf, cast(u8)'U');
|
||||
for (isize i = 28; i >= 0; i -= 4) {
|
||||
array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
array_add(&buf, quote);
|
||||
String res = {};
|
||||
res.text = buf.data;
|
||||
res.len = buf.count;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,538 @@
|
||||
GB_STATIC_ASSERT(sizeof(MapIndex) == sizeof(u32));
|
||||
|
||||
|
||||
struct String16HashKey {
|
||||
String16 string;
|
||||
u32 hash;
|
||||
|
||||
operator String16() const noexcept {
|
||||
return this->string;
|
||||
}
|
||||
operator String16 const &() const noexcept {
|
||||
return this->string;
|
||||
}
|
||||
};
|
||||
gb_internal gb_inline u32 string_hash(String16 const &s) {
|
||||
u32 res = fnv32a(s.text, s.len*gb_size_of(u16)) & 0x7fffffff;
|
||||
return res | (res == 0);
|
||||
}
|
||||
|
||||
gb_internal gb_inline String16HashKey string_hash_string(String16 const &s) {
|
||||
String16HashKey hash_key = {};
|
||||
hash_key.hash = string_hash(s);
|
||||
hash_key.string = s;
|
||||
return hash_key;
|
||||
}
|
||||
|
||||
|
||||
#if 1 /* old string map */
|
||||
|
||||
template <typename T>
|
||||
struct String16MapEntry {
|
||||
String16 key;
|
||||
u32 hash;
|
||||
MapIndex next;
|
||||
T value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct String16Map {
|
||||
MapIndex * hashes;
|
||||
usize hashes_count;
|
||||
String16MapEntry<T> *entries;
|
||||
u32 count;
|
||||
u32 entries_capacity;
|
||||
};
|
||||
|
||||
|
||||
template <typename T> gb_internal void string16_map_init (String16Map<T> *h, usize capacity = 16);
|
||||
template <typename T> gb_internal void string16_map_destroy (String16Map<T> *h);
|
||||
|
||||
template <typename T> gb_internal T * string16_map_get (String16Map<T> *h, String16HashKey const &key);
|
||||
template <typename T> gb_internal T & string16_map_must_get(String16Map<T> *h, String16HashKey const &key);
|
||||
template <typename T> gb_internal void string16_map_set (String16Map<T> *h, String16HashKey const &key, T const &value);
|
||||
|
||||
// template <typename T> gb_internal void string16_map_remove (String16Map<T> *h, String16HashKey const &key);
|
||||
template <typename T> gb_internal void string16_map_clear (String16Map<T> *h);
|
||||
template <typename T> gb_internal void string16_map_grow (String16Map<T> *h);
|
||||
template <typename T> gb_internal void string16_map_reserve (String16Map<T> *h, usize new_count);
|
||||
|
||||
gb_internal gbAllocator string16_map_allocator(void) {
|
||||
return heap_allocator();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline void string16_map_init(String16Map<T> *h, usize capacity) {
|
||||
capacity = next_pow2_isize(capacity);
|
||||
string16_map_reserve(h, capacity);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline void string16_map_destroy(String16Map<T> *h) {
|
||||
gb_free(string16_map_allocator(), h->hashes);
|
||||
gb_free(string16_map_allocator(), h->entries);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal void string16_map__resize_hashes(String16Map<T> *h, usize count) {
|
||||
h->hashes_count = cast(u32)resize_array_raw(&h->hashes, string16_map_allocator(), h->hashes_count, count, MAP_CACHE_LINE_SIZE);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal void string16_map__reserve_entries(String16Map<T> *h, usize capacity) {
|
||||
h->entries_capacity = cast(u32)resize_array_raw(&h->entries, string16_map_allocator(), h->entries_capacity, capacity, MAP_CACHE_LINE_SIZE);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal MapIndex string16_map__add_entry(String16Map<T> *h, u32 hash, String16 const &key) {
|
||||
String16MapEntry<T> e = {};
|
||||
e.key = key;
|
||||
e.hash = hash;
|
||||
e.next = MAP_SENTINEL;
|
||||
if (h->count+1 >= h->entries_capacity) {
|
||||
string16_map__reserve_entries(h, gb_max(h->entries_capacity*2, 4));
|
||||
}
|
||||
h->entries[h->count++] = e;
|
||||
return cast(MapIndex)(h->count-1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal MapFindResult string16_map__find(String16Map<T> *h, u32 hash, String16 const &key) {
|
||||
MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
|
||||
if (h->hashes_count != 0) {
|
||||
fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1));
|
||||
fr.entry_index = h->hashes[fr.hash_index];
|
||||
while (fr.entry_index != MAP_SENTINEL) {
|
||||
auto *entry = &h->entries[fr.entry_index];
|
||||
if (entry->hash == hash && entry->key == key) {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = entry->next;
|
||||
}
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal MapFindResult string16_map__find_from_entry(String16Map<T> *h, String16MapEntry<T> *e) {
|
||||
MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
|
||||
if (h->hashes_count != 0) {
|
||||
fr.hash_index = cast(MapIndex)(e->hash & (h->hashes_count-1));
|
||||
fr.entry_index = h->hashes[fr.hash_index];
|
||||
while (fr.entry_index != MAP_SENTINEL) {
|
||||
auto *entry = &h->entries[fr.entry_index];
|
||||
if (entry == e) {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = entry->next;
|
||||
}
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal b32 string16_map__full(String16Map<T> *h) {
|
||||
return 0.75f * h->hashes_count <= h->count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_inline void string16_map_grow(String16Map<T> *h) {
|
||||
isize new_count = gb_max(h->hashes_count<<1, 16);
|
||||
string16_map_reserve(h, new_count);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal void string16_map_reset_entries(String16Map<T> *h) {
|
||||
for (u32 i = 0; i < h->hashes_count; i++) {
|
||||
h->hashes[i] = MAP_SENTINEL;
|
||||
}
|
||||
for (isize i = 0; i < h->count; i++) {
|
||||
MapFindResult fr;
|
||||
String16MapEntry<T> *e = &h->entries[i];
|
||||
e->next = MAP_SENTINEL;
|
||||
fr = string16_map__find_from_entry(h, e);
|
||||
if (fr.entry_prev == MAP_SENTINEL) {
|
||||
h->hashes[fr.hash_index] = cast(MapIndex)i;
|
||||
} else {
|
||||
h->entries[fr.entry_prev].next = cast(MapIndex)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal void string16_map_reserve(String16Map<T> *h, usize cap) {
|
||||
if (h->count*2 < h->hashes_count) {
|
||||
return;
|
||||
}
|
||||
string16_map__reserve_entries(h, cap);
|
||||
string16_map__resize_hashes(h, cap*2);
|
||||
string16_map_reset_entries(h);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal T *string16_map_get(String16Map<T> *h, u32 hash, String16 const &key) {
|
||||
MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
|
||||
if (h->hashes_count != 0) {
|
||||
fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1));
|
||||
fr.entry_index = h->hashes[fr.hash_index];
|
||||
while (fr.entry_index != MAP_SENTINEL) {
|
||||
auto *entry = &h->entries[fr.entry_index];
|
||||
if (entry->hash == hash && entry->key == key) {
|
||||
return &entry->value;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = entry->next;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline T *string16_map_get(String16Map<T> *h, String16HashKey const &key) {
|
||||
return string16_map_get(h, key.hash, key.string);
|
||||
}
|
||||
template <typename T>
|
||||
gb_internal T &string16_map_must_get(String16Map<T> *h, u32 hash, String16 const &key) {
|
||||
isize index = string16_map__find(h, hash, key).entry_index;
|
||||
GB_ASSERT(index != MAP_SENTINEL);
|
||||
return h->entries[index].value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal T &string16_map_must_get(String16Map<T> *h, String16HashKey const &key) {
|
||||
return string16_map_must_get(h, key.hash, key.string);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal void string16_map_set(String16Map<T> *h, u32 hash, String16 const &key, T const &value) {
|
||||
MapIndex index;
|
||||
MapFindResult fr;
|
||||
if (h->hashes_count == 0) {
|
||||
string16_map_grow(h);
|
||||
}
|
||||
fr = string16_map__find(h, hash, key);
|
||||
if (fr.entry_index != MAP_SENTINEL) {
|
||||
index = fr.entry_index;
|
||||
} else {
|
||||
index = string16_map__add_entry(h, hash, key);
|
||||
if (fr.entry_prev != MAP_SENTINEL) {
|
||||
h->entries[fr.entry_prev].next = index;
|
||||
} else {
|
||||
h->hashes[fr.hash_index] = index;
|
||||
}
|
||||
}
|
||||
h->entries[index].value = value;
|
||||
|
||||
if (string16_map__full(h)) {
|
||||
string16_map_grow(h);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline void string16_map_set(String16Map<T> *h, String16HashKey const &key, T const &value) {
|
||||
string16_map_set(h, key.hash, key.string, value);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline void string16_map_clear(String16Map<T> *h) {
|
||||
h->count = 0;
|
||||
for (u32 i = 0; i < h->hashes_count; i++) {
|
||||
h->hashes[i] = MAP_SENTINEL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal String16MapEntry<T> *begin(String16Map<T> &m) noexcept {
|
||||
return m.entries;
|
||||
}
|
||||
template <typename T>
|
||||
gb_internal String16MapEntry<T> const *begin(String16Map<T> const &m) noexcept {
|
||||
return m.entries;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal String16MapEntry<T> *end(String16Map<T> &m) noexcept {
|
||||
return m.entries + m.count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal String16MapEntry<T> const *end(String16Map<T> const &m) noexcept {
|
||||
return m.entries + m.count;
|
||||
}
|
||||
|
||||
#else /* new string map */
|
||||
|
||||
template <typename T>
|
||||
struct StringMapEntry {
|
||||
String key;
|
||||
u32 hash;
|
||||
T value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct StringMap {
|
||||
String16MapEntry<T> *entries;
|
||||
u32 count;
|
||||
u32 capacity;
|
||||
};
|
||||
|
||||
|
||||
template <typename T> gb_internal void string16_map_init (String16Map<T> *h, usize capacity = 16);
|
||||
template <typename T> gb_internal void string16_map_destroy (String16Map<T> *h);
|
||||
|
||||
template <typename T> gb_internal T * string16_map_get (String16Map<T> *h, String16 const &key);
|
||||
template <typename T> gb_internal T * string16_map_get (String16Map<T> *h, String16HashKey const &key);
|
||||
|
||||
template <typename T> gb_internal T & string16_map_must_get(String16Map<T> *h, String16 const &key);
|
||||
template <typename T> gb_internal T & string16_map_must_get(String16Map<T> *h, String16HashKey const &key);
|
||||
|
||||
template <typename T> gb_internal void string16_map_set (String16Map<T> *h, String16 const &key, T const &value);
|
||||
template <typename T> gb_internal void string16_map_set (String16Map<T> *h, String16HashKey const &key, T const &value);
|
||||
|
||||
// template <typename T> gb_internal void string16_map_remove (String16Map<T> *h, String16HashKey const &key);
|
||||
template <typename T> gb_internal void string16_map_clear (String16Map<T> *h);
|
||||
template <typename T> gb_internal void string16_map_grow (String16Map<T> *h);
|
||||
template <typename T> gb_internal void string16_map_reserve (String16Map<T> *h, usize new_count);
|
||||
|
||||
gb_internal gbAllocator string16_map_allocator(void) {
|
||||
return heap_allocator();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline void string16_map_init(String16Map<T> *h, usize capacity) {
|
||||
capacity = next_pow2_isize(capacity);
|
||||
string16_map_reserve(h, capacity);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline void string16_map_destroy(String16Map<T> *h) {
|
||||
gb_free(string16_map_allocator(), h->entries);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal void string16_map__insert(String16Map<T> *h, u32 hash, String16 const &key, T const &value) {
|
||||
if (h->count+1 >= h->capacity) {
|
||||
string16_map_grow(h);
|
||||
}
|
||||
GB_ASSERT(h->count+1 < h->capacity);
|
||||
|
||||
u32 mask = h->capacity-1;
|
||||
MapIndex index = hash & mask;
|
||||
MapIndex original_index = index;
|
||||
do {
|
||||
auto *entry = h->entries+index;
|
||||
if (entry->hash == 0) {
|
||||
entry->key = key;
|
||||
entry->hash = hash;
|
||||
entry->value = value;
|
||||
|
||||
h->count += 1;
|
||||
return;
|
||||
}
|
||||
index = (index+1)&mask;
|
||||
} while (index != original_index);
|
||||
|
||||
GB_PANIC("Full map");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal b32 string16_map__full(String16Map<T> *h) {
|
||||
return 0.75f * h->count <= h->capacity;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_inline void string16_map_grow(String16Map<T> *h) {
|
||||
isize new_capacity = gb_max(h->capacity<<1, 16);
|
||||
string16_map_reserve(h, new_capacity);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal void string16_map_reserve(String16Map<T> *h, usize cap) {
|
||||
if (cap < h->capacity) {
|
||||
return;
|
||||
}
|
||||
cap = next_pow2_isize(cap);
|
||||
|
||||
String16Map<T> new_h = {};
|
||||
new_h.count = 0;
|
||||
new_h.capacity = cast(u32)cap;
|
||||
new_h.entries = gb_alloc_array(string16_map_allocator(), String16MapEntry<T>, new_h.capacity);
|
||||
|
||||
if (h->count) {
|
||||
for (u32 i = 0; i < h->capacity; i++) {
|
||||
auto *entry = h->entries+i;
|
||||
if (entry->hash) {
|
||||
string16_map__insert(&new_h, entry->hash, entry->key, entry->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
string16_map_destroy(h);
|
||||
*h = new_h;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal T *string16_map_get(String16Map<T> *h, u32 hash, String16 const &key) {
|
||||
if (h->count == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
u32 mask = (h->capacity-1);
|
||||
u32 index = hash & mask;
|
||||
u32 original_index = index;
|
||||
do {
|
||||
auto *entry = h->entries+index;
|
||||
u32 curr_hash = entry->hash;
|
||||
if (curr_hash == 0) {
|
||||
// NOTE(bill): no found, but there isn't any key removal for this hash map
|
||||
return nullptr;
|
||||
} else if (curr_hash == hash && entry->key == key) {
|
||||
return &entry->value;
|
||||
}
|
||||
index = (index+1) & mask;
|
||||
} while (original_index != index);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline T *string16_map_get(String16Map<T> *h, String16HashKey const &key) {
|
||||
return string16_map_get(h, key.hash, key.string);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline T *string16_map_get(String16Map<T> *h, String16 const &key) {
|
||||
return string16_map_get(h, string_hash(key), key);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal T &string16_map_must_get(String16Map<T> *h, u32 hash, String16 const &key) {
|
||||
T *found = string16_map_get(h, hash, key);
|
||||
GB_ASSERT(found != nullptr);
|
||||
return *found;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal T &string16_map_must_get(String16Map<T> *h, String16HashKey const &key) {
|
||||
return string16_map_must_get(h, key.hash, key.string);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline T &string16_map_must_get(String16Map<T> *h, String16 const &key) {
|
||||
return string16_map_must_get(h, string_hash(key), key);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal void string16_map_set(String16Map<T> *h, u32 hash, String16 const &key, T const &value) {
|
||||
if (h->count == 0) {
|
||||
string16_map_grow(h);
|
||||
}
|
||||
auto *found = string16_map_get(h, hash, key);
|
||||
if (found) {
|
||||
*found = value;
|
||||
return;
|
||||
}
|
||||
string16_map__insert(h, hash, key, value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline void string16_map_set(String16Map<T> *h, String16 const &key, T const &value) {
|
||||
string16_map_set(h, string_hash_string(key), value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline void string16_map_set(String16Map<T> *h, String16HashKey const &key, T const &value) {
|
||||
string16_map_set(h, key.hash, key.string, value);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal gb_inline void string16_map_clear(String16Map<T> *h) {
|
||||
h->count = 0;
|
||||
gb_zero_array(h->entries, h->capacity);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct StringMapIterator {
|
||||
String16Map<T> *map;
|
||||
MapIndex index;
|
||||
|
||||
StringMapIterator<T> &operator++() noexcept {
|
||||
for (;;) {
|
||||
++index;
|
||||
if (map->capacity == index) {
|
||||
return *this;
|
||||
}
|
||||
String16MapEntry<T> *entry = map->entries+index;
|
||||
if (entry->hash != 0) {
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(StringMapIterator<T> const &other) const noexcept {
|
||||
return this->map == other->map && this->index == other->index;
|
||||
}
|
||||
|
||||
operator String16MapEntry<T> *() const {
|
||||
return map->entries+index;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal StringMapIterator<T> end(String16Map<T> &m) noexcept {
|
||||
return StringMapIterator<T>{&m, m.capacity};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal StringMapIterator<T> const end(String16Map<T> const &m) noexcept {
|
||||
return StringMapIterator<T>{&m, m.capacity};
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_internal StringMapIterator<T> begin(String16Map<T> &m) noexcept {
|
||||
if (m.count == 0) {
|
||||
return end(m);
|
||||
}
|
||||
|
||||
MapIndex index = 0;
|
||||
while (index < m.capacity) {
|
||||
if (m.entries[index].hash) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return StringMapIterator<T>{&m, index};
|
||||
}
|
||||
template <typename T>
|
||||
gb_internal StringMapIterator<T> const begin(String16Map<T> const &m) noexcept {
|
||||
if (m.count == 0) {
|
||||
return end(m);
|
||||
}
|
||||
|
||||
MapIndex index = 0;
|
||||
while (index < m.capacity) {
|
||||
if (m.entries[index].hash) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return StringMapIterator<T>{&m, index};
|
||||
}
|
||||
|
||||
#endif
|
||||
+118
-17
@@ -41,8 +41,13 @@ enum BasicKind {
|
||||
Basic_uint,
|
||||
Basic_uintptr,
|
||||
Basic_rawptr,
|
||||
Basic_string, // ^u8 + int
|
||||
Basic_cstring, // ^u8
|
||||
|
||||
Basic_string, // [^]u8 + int
|
||||
Basic_cstring, // [^]u8
|
||||
|
||||
Basic_string16, // [^]u16 + int
|
||||
Basic_cstring16, // [^]u16 + int
|
||||
|
||||
Basic_any, // rawptr + ^Type_Info
|
||||
|
||||
Basic_typeid,
|
||||
@@ -501,8 +506,14 @@ gb_global Type basic_types[] = {
|
||||
{Type_Basic, {Basic_uintptr, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uintptr")}},
|
||||
|
||||
{Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}},
|
||||
|
||||
{Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}},
|
||||
{Type_Basic, {Basic_cstring, BasicFlag_String, -1, STR_LIT("cstring")}},
|
||||
|
||||
{Type_Basic, {Basic_string16, BasicFlag_String, -1, STR_LIT("string16")}},
|
||||
{Type_Basic, {Basic_cstring16, BasicFlag_String, -1, STR_LIT("cstring16")}},
|
||||
|
||||
|
||||
{Type_Basic, {Basic_any, 0, 16, STR_LIT("any")}},
|
||||
|
||||
{Type_Basic, {Basic_typeid, 0, 8, STR_LIT("typeid")}},
|
||||
@@ -592,8 +603,12 @@ gb_global Type *t_uint = &basic_types[Basic_uint];
|
||||
gb_global Type *t_uintptr = &basic_types[Basic_uintptr];
|
||||
|
||||
gb_global Type *t_rawptr = &basic_types[Basic_rawptr];
|
||||
|
||||
gb_global Type *t_string = &basic_types[Basic_string];
|
||||
gb_global Type *t_cstring = &basic_types[Basic_cstring];
|
||||
gb_global Type *t_string16 = &basic_types[Basic_string16];
|
||||
gb_global Type *t_cstring16 = &basic_types[Basic_cstring16];
|
||||
|
||||
gb_global Type *t_any = &basic_types[Basic_any];
|
||||
|
||||
gb_global Type *t_typeid = &basic_types[Basic_typeid];
|
||||
@@ -631,6 +646,8 @@ gb_global Type *t_untyped_uninit = &basic_types[Basic_UntypedUninit];
|
||||
|
||||
gb_global Type *t_u8_ptr = nullptr;
|
||||
gb_global Type *t_u8_multi_ptr = nullptr;
|
||||
gb_global Type *t_u16_ptr = nullptr;
|
||||
gb_global Type *t_u16_multi_ptr = nullptr;
|
||||
gb_global Type *t_int_ptr = nullptr;
|
||||
gb_global Type *t_i64_ptr = nullptr;
|
||||
gb_global Type *t_f64_ptr = nullptr;
|
||||
@@ -644,6 +661,8 @@ gb_global Type *t_type_info_enum_value = nullptr;
|
||||
gb_global Type *t_type_info_ptr = nullptr;
|
||||
gb_global Type *t_type_info_enum_value_ptr = nullptr;
|
||||
|
||||
gb_global Type *t_type_info_string_encoding_kind = nullptr;
|
||||
|
||||
gb_global Type *t_type_info_named = nullptr;
|
||||
gb_global Type *t_type_info_integer = nullptr;
|
||||
gb_global Type *t_type_info_rune = nullptr;
|
||||
@@ -1293,6 +1312,14 @@ gb_internal bool is_type_string(Type *t) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_string16(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_string16;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_cstring(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
@@ -1301,6 +1328,14 @@ gb_internal bool is_type_cstring(Type *t) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_cstring16(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_cstring16;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_typed(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
@@ -1430,6 +1465,12 @@ gb_internal bool is_type_u8(Type *t) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_u16(Type *t) {
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_u16;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_array(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
@@ -1691,6 +1732,39 @@ gb_internal bool is_type_rune_array(Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool is_type_u16_slice(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Slice) {
|
||||
return is_type_u16(t->Slice.elem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_u16_array(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Array) {
|
||||
return is_type_u16(t->Array.elem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_u16_ptr(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_Pointer) {
|
||||
return is_type_u16(t->Slice.elem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal bool is_type_u16_multi_ptr(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == nullptr) { return false; }
|
||||
if (t->kind == Type_MultiPointer) {
|
||||
return is_type_u16(t->Slice.elem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool is_type_array_like(Type *t) {
|
||||
return is_type_array(t) || is_type_enumerated_array(t);
|
||||
@@ -2110,7 +2184,7 @@ gb_internal bool is_type_indexable(Type *t) {
|
||||
Type *bt = base_type(t);
|
||||
switch (bt->kind) {
|
||||
case Type_Basic:
|
||||
return bt->Basic.kind == Basic_string;
|
||||
return bt->Basic.kind == Basic_string || bt->Basic.kind == Basic_string16;
|
||||
case Type_Array:
|
||||
case Type_Slice:
|
||||
case Type_DynamicArray:
|
||||
@@ -2130,7 +2204,7 @@ gb_internal bool is_type_sliceable(Type *t) {
|
||||
Type *bt = base_type(t);
|
||||
switch (bt->kind) {
|
||||
case Type_Basic:
|
||||
return bt->Basic.kind == Basic_string;
|
||||
return bt->Basic.kind == Basic_string || bt->Basic.kind == Basic_string16;
|
||||
case Type_Array:
|
||||
case Type_Slice:
|
||||
case Type_DynamicArray:
|
||||
@@ -2377,6 +2451,7 @@ gb_internal bool type_has_nil(Type *t) {
|
||||
case Basic_any:
|
||||
return true;
|
||||
case Basic_cstring:
|
||||
case Basic_cstring16:
|
||||
return true;
|
||||
case Basic_typeid:
|
||||
return true;
|
||||
@@ -2444,8 +2519,9 @@ gb_internal bool is_type_comparable(Type *t) {
|
||||
case Basic_rune:
|
||||
return true;
|
||||
case Basic_string:
|
||||
return true;
|
||||
case Basic_cstring:
|
||||
case Basic_string16:
|
||||
case Basic_cstring16:
|
||||
return true;
|
||||
case Basic_typeid:
|
||||
return true;
|
||||
@@ -3831,10 +3907,12 @@ gb_internal i64 type_size_of(Type *t) {
|
||||
if (t->kind == Type_Basic) {
|
||||
GB_ASSERT_MSG(is_type_typed(t), "%s", type_to_string(t));
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_string: size = 2*build_context.int_size; break;
|
||||
case Basic_cstring: size = build_context.ptr_size; break;
|
||||
case Basic_any: size = 16; break;
|
||||
case Basic_typeid: size = 8; break;
|
||||
case Basic_string: size = 2*build_context.int_size; break;
|
||||
case Basic_cstring: size = build_context.ptr_size; break;
|
||||
case Basic_string16: size = 2*build_context.int_size; break;
|
||||
case Basic_cstring16: size = build_context.ptr_size; break;
|
||||
case Basic_any: size = 16; break;
|
||||
case Basic_typeid: size = 8; break;
|
||||
|
||||
case Basic_int: case Basic_uint:
|
||||
size = build_context.int_size;
|
||||
@@ -3894,10 +3972,12 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
|
||||
case Type_Basic: {
|
||||
GB_ASSERT(is_type_typed(t));
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_string: return build_context.int_size;
|
||||
case Basic_cstring: return build_context.ptr_size;
|
||||
case Basic_any: return 8;
|
||||
case Basic_typeid: return 8;
|
||||
case Basic_string: return build_context.int_size;
|
||||
case Basic_cstring: return build_context.ptr_size;
|
||||
case Basic_string16: return build_context.int_size;
|
||||
case Basic_cstring16: return build_context.ptr_size;
|
||||
case Basic_any: return 8;
|
||||
case Basic_typeid: return 8;
|
||||
|
||||
case Basic_int: case Basic_uint:
|
||||
return build_context.int_size;
|
||||
@@ -4145,10 +4225,12 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
|
||||
return size;
|
||||
}
|
||||
switch (kind) {
|
||||
case Basic_string: return 2*build_context.int_size;
|
||||
case Basic_cstring: return build_context.ptr_size;
|
||||
case Basic_any: return 16;
|
||||
case Basic_typeid: return 8;
|
||||
case Basic_string: return 2*build_context.int_size;
|
||||
case Basic_cstring: return build_context.ptr_size;
|
||||
case Basic_string16: return 2*build_context.int_size;
|
||||
case Basic_cstring16: return build_context.ptr_size;
|
||||
case Basic_any: return 16;
|
||||
case Basic_typeid: return 8;
|
||||
|
||||
case Basic_int: case Basic_uint:
|
||||
return build_context.int_size;
|
||||
@@ -4380,6 +4462,15 @@ gb_internal i64 type_offset_of(Type *t, i64 index, Type **field_type_) {
|
||||
if (field_type_) *field_type_ = t_int;
|
||||
return build_context.int_size; // len
|
||||
}
|
||||
} else if (t->Basic.kind == Basic_string16) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
if (field_type_) *field_type_ = t_u16_ptr;
|
||||
return 0; // data
|
||||
case 1:
|
||||
if (field_type_) *field_type_ = t_int;
|
||||
return build_context.int_size; // len
|
||||
}
|
||||
} else if (t->Basic.kind == Basic_any) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
@@ -4456,6 +4547,11 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) {
|
||||
case 0: t = t_rawptr; break;
|
||||
case 1: t = t_int; break;
|
||||
}
|
||||
} else if (t->Basic.kind == Basic_string16) {
|
||||
switch (index) {
|
||||
case 0: t = t_rawptr; break;
|
||||
case 1: t = t_int; break;
|
||||
}
|
||||
} else if (t->Basic.kind == Basic_any) {
|
||||
switch (index) {
|
||||
case 0: t = t_rawptr; break;
|
||||
@@ -4697,6 +4793,11 @@ gb_internal Type *type_internal_index(Type *t, isize index) {
|
||||
GB_ASSERT(index == 0 || index == 1);
|
||||
return index == 0 ? t_u8_ptr : t_int;
|
||||
}
|
||||
case Basic_string16:
|
||||
{
|
||||
GB_ASSERT(index == 0 || index == 1);
|
||||
return index == 0 ? t_u16_ptr : t_int;
|
||||
}
|
||||
case Basic_any:
|
||||
{
|
||||
GB_ASSERT(index == 0 || index == 1);
|
||||
|
||||
Reference in New Issue
Block a user