diff --git a/base/runtime/core_builtin_soa.odin b/base/runtime/core_builtin_soa.odin index 1b2e5d316..94f5be1d4 100644 --- a/base/runtime/core_builtin_soa.odin +++ b/base/runtime/core_builtin_soa.odin @@ -86,7 +86,6 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc return } - array.allocator = allocator footer := raw_soa_footer(&array) if size_of(E) == 0 { footer.len = length diff --git a/core/relative/relative.odin b/core/relative/relative.odin new file mode 100644 index 000000000..30a7b86ae --- /dev/null +++ b/core/relative/relative.odin @@ -0,0 +1,171 @@ +package relative_types + +import "base:intrinsics" + +Pointer :: struct($Type: typeid, $Backing: typeid) + where + intrinsics.type_is_pointer(Type) || intrinsics.type_is_multi_pointer(Type), + intrinsics.type_is_integer(Backing) { + offset: Backing, +} + +Slice :: struct($Type: typeid, $Backing: typeid) + where + intrinsics.type_is_slice(Type), + intrinsics.type_is_integer(Backing) { + offset: Backing, + len: Backing, +} + + + +@(require_results) +pointer_get :: proc "contextless" (p: ^$P/Pointer($T, $B)) -> T { + if p.offset == 0 { + return nil + } + ptr := ([^]byte)(p)[p.offset:] + return (T)(ptr) +} + +pointer_set :: proc "contextless" (p: ^$P/Pointer($T, $B), ptr: T) { + if ptr == nil { + p.offset = 0 + } else { + p.offset = B(int(uintptr(ptr)) - int(uintptr(p))) + } +} + +@(require_results) +slice_get :: proc "contextless" (p: ^$S/Slice($T/[]$E, $B)) -> (slice: T) { + if p.offset == 0 { + when size_of(E) == 0 { + slice = T(([^]E)(nil)[:p.len]) + } + } else { + ptr := ([^]E)(([^]byte)(p)[p.offset:]) + slice = T(ptr[:p.len]) + } + return +} + +slice_set :: proc "contextless" (p: ^$S/Slice($T, $B), slice: T) { + if slice == nil { + p.offset, p.len = 0, 0 + } else { + ptr := raw_data(slice) + p.offset = B(int(uintptr(ptr)) - int(uintptr(p))) + p.len = B(len(slice)) + } +} + +get :: proc{ + pointer_get, + slice_get, +} + +set :: proc{ + pointer_set, + slice_set, +} + + + +Set_Safe_Error :: enum { + None, + Memory_Too_Far_Apart, + Length_Out_Of_Bounds, +} + + +@(require_results) +pointer_set_safe :: proc "contextless" (p: ^$P/Pointer($T, $B), ptr: T) -> Set_Safe_Error { + if ptr == nil { + p.offset = 0 + } else { + when intrinsics.type_is_unsigned(B) { + diff := uint(uintptr(ptr) - uintptr(p)) + when size_of(B) < size_of(uint) { + if diff > uint(max(B)) { + return .Memory_Too_Far_Apart + } + } else { + if B(diff) > max(B) { + return .Memory_Too_Far_Apart + } + } + } else { + diff := int(uintptr(ptr)) - int(uintptr(p)) + when size_of(B) < size_of(int) { + if diff > int(max(B)) { + return .Memory_Too_Far_Apart + } + } else { + if B(diff) > max(B) { + return .Memory_Too_Far_Apart + } + } + } + p.offset = B(diff) + } + return .None +} + +@(require_results) +slice_set_safe :: proc "contextless" (p: ^$S/Slice($T, $B), slice: T) -> Set_Safe_Error { + if slice == nil { + p.offset, p.len = 0, 0 + } else { + ptr := raw_data(slice) + when intrinsics.type_is_unsigned(B) { + diff := uint(uintptr(ptr) - uintptr(p)) + when size_of(B) < size_of(uint) { + if diff > uint(max(B)) { + return .Memory_Too_Far_Apart + } + + if uint(len(slice)) > uint(max(B)) { + return .Length_Out_Of_Bounds + } + } else { + if B(diff) > max(B) { + return .Memory_Too_Far_Apart + } + if B(len(slice)) > max(B) { + return .Length_Out_Of_Bounds + } + } + p.offset = B(diff) + p.len = B(len(slice)) + } else { + diff := int(uintptr(ptr)) - int(uintptr(p)) + when size_of(B) < size_of(int) { + if diff > int(max(B)) { + return .Memory_Too_Far_Apart + } + if len(slice) > int(max(B)) || len(slice) < int(min(B)) { + return .Length_Out_Of_Bounds + } + } else { + if B(diff) > max(B) { + return .Memory_Too_Far_Apart + } + if B(len(slice)) > max(B) { + return .Length_Out_Of_Bounds + } + if B(len(slice)) > max(B) || B(len(slice)) < min(B) { + return .Length_Out_Of_Bounds + } + } + } + p.offset = B(diff) + p.len = B(len(slice)) + } + return .None +} + + +set_safe :: proc{ + pointer_set_safe, + slice_set_safe, +} \ No newline at end of file diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index e1cb43ec1..4e374add6 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -5686,7 +5686,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } - operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type)); + operand->value = exact_value_bool(is_type_subtype_of_and_allow_polymorphic(op_src.type, op_dst.type)); operand->mode = Addressing_Constant; operand->type = t_untyped_bool; } break; diff --git a/src/check_type.cpp b/src/check_type.cpp index 0a2113e56..d216ae3e6 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1,4 +1,6 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location); +gb_internal Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand); +gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array const *operands); gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) { t = base_type(t); @@ -393,7 +395,6 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly bool *is_polymorphic_, Ast *node, Array *poly_operands) { Type *polymorphic_params_type = nullptr; - bool can_check_fields = true; GB_ASSERT(is_polymorphic_ != nullptr); if (polymorphic_params == nullptr) { @@ -403,6 +404,17 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly return polymorphic_params_type; } + + // bool is_variadic = false; + // isize variadic_index = 0; + // bool success = false; + // isize specialization_count = 0; + // polymorphic_params_type = check_get_params(ctx, ctx->scope, polymorphic_params, &is_variadic, &variadic_index, &success, &specialization_count, poly_operands); + // if (success) { + // return nullptr; + // } + + bool can_check_fields = true; ast_node(field_list, FieldList, polymorphic_params); Slice params = field_list->list; if (params.count != 0) { @@ -417,11 +429,13 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly auto entities = array_make(permanent_allocator(), 0, variable_count); + i32 field_group_index = -1; for_array(i, params) { Ast *param = params[i]; if (param->kind != Ast_Field) { continue; } + field_group_index += 1; ast_node(p, Field, param); Ast *type_expr = p->type; Ast *default_value = unparen_expr(p->default_value); @@ -481,7 +495,7 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly type = t_invalid; } - if (is_type_polymorphic_type) { + if (is_type_polymorphic_type && !is_type_proc(type)) { gbString str = type_to_string(type); error(params[i], "Parameter types cannot be polymorphic, got %s", str); gb_string_free(str); @@ -523,13 +537,18 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly e->TypeName.is_type_alias = true; e->flags |= EntityFlag_PolyConst; } else { - if (is_type_polymorphic(base_type(operand.type))) { + Type *t = operand.type; + if (is_type_proc(type)) { + t = determine_type_from_polymorphic(ctx, type, operand); + } + if (is_type_polymorphic(base_type(t))) { *is_polymorphic_ = true; can_check_fields = false; } if (e == nullptr) { - e = alloc_entity_constant(scope, token, operand.type, operand.value); + e = alloc_entity_const_param(scope, token, t, operand.value, is_type_polymorphic(t)); e->Constant.param_value = param_value; + e->Constant.field_group_index = field_group_index; } } } else { @@ -538,7 +557,8 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly e->TypeName.is_type_alias = true; e->flags |= EntityFlag_PolyConst; } else { - e = alloc_entity_constant(scope, token, type, param_value.value); + e = alloc_entity_const_param(scope, token, type, param_value.value, is_type_polymorphic(type)); + e->Constant.field_group_index = field_group_index; e->Constant.param_value = param_value; } } @@ -559,7 +579,6 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly if (!*is_polymorphic_) { *is_polymorphic_ = polymorphic_params != nullptr && poly_operands == nullptr; } - return polymorphic_params_type; } diff --git a/src/parser.cpp b/src/parser.cpp index 2a7f41b36..78ac29dfd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2164,6 +2164,49 @@ gb_internal Array parse_union_variant_list(AstFile *f) { return variants; } +gb_internal void parser_check_polymorphic_record_parameters(AstFile *f, Ast *polymorphic_params) { + if (polymorphic_params == nullptr) { + return; + } + if (polymorphic_params->kind != Ast_FieldList) { + return; + } + + + enum {Unknown, Dollar, Bare} prefix = Unknown; + gb_unused(prefix); + + for (Ast *field : polymorphic_params->FieldList.list) { + if (field == nullptr || field->kind != Ast_Field) { + continue; + } + for (Ast *name : field->Field.names) { + if (name == nullptr) { + continue; + } + bool error = false; + + if (name->kind == Ast_Ident) { + switch (prefix) { + case Unknown: prefix = Bare; break; + case Dollar: error = true; break; + case Bare: break; + } + } else if (name->kind == Ast_PolyType) { + switch (prefix) { + case Unknown: prefix = Dollar; break; + case Dollar: break; + case Bare: error = true; break; + } + } + if (error) { + syntax_error(name, "Mixture of polymorphic $ names and normal identifiers are not allowed within record parameters"); + } + } + } +} + + gb_internal Ast *parse_operand(AstFile *f, bool lhs) { Ast *operand = nullptr; // Operand switch (f->curr_token.kind) { @@ -2610,6 +2653,8 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { decls = fields->FieldList.list; } + parser_check_polymorphic_record_parameters(f, polymorphic_params); + return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, field_align, where_token, where_clauses); } break; @@ -2702,6 +2747,8 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { auto variants = parse_union_variant_list(f); Token close = expect_closing_brace_of_field_list(f); + parser_check_polymorphic_record_parameters(f, polymorphic_params); + return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses); } break; diff --git a/src/types.cpp b/src/types.cpp index b99d469e4..c4b03c967 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -4093,7 +4093,7 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) { return offset; } -gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) { +gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false, bool allow_polymorphic=false) { Type *prev_src = src; src = type_deref(src); if (!src_is_ptr) { @@ -4105,11 +4105,19 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi return 0; } + bool dst_is_polymorphic = is_type_polymorphic(dst); + for_array(i, src->Struct.fields) { Entity *f = src->Struct.fields[i]; if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) { continue; } + if (allow_polymorphic && dst_is_polymorphic) { + Type *fb = base_type(type_deref(f->type)); + if (fb->kind == Type_Struct && fb->Struct.polymorphic_parent == dst) { + return true; + } + } if (are_types_identical(f->type, dst)) { return level+1; @@ -4119,7 +4127,7 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi return level+1; } } - isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr); + isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr, allow_polymorphic); if (nested_level > 0) { return nested_level; } @@ -4135,6 +4143,13 @@ gb_internal bool is_type_subtype_of(Type *src, Type *dst) { return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src)); } +gb_internal bool is_type_subtype_of_and_allow_polymorphic(Type *src, Type *dst) { + if (are_types_identical(src, dst)) { + return true; + } + + return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src), true); +} gb_internal bool has_type_got_objc_class_attribute(Type *t) {