mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-03 22:28:46 +00:00
Maybe types; value, ok := maybe_value(x)
This commit is contained in:
+17
-13
@@ -168,6 +168,7 @@ enum BuiltinProcId {
|
||||
|
||||
BuiltinProc_enum_to_string,
|
||||
|
||||
BuiltinProc_maybe_value,
|
||||
|
||||
BuiltinProc_Count,
|
||||
};
|
||||
@@ -213,6 +214,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
|
||||
|
||||
{STR_LIT("enum_to_string"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("maybe_value"), 1, false, Expr_Expr},
|
||||
|
||||
};
|
||||
|
||||
struct CheckerContext {
|
||||
@@ -891,7 +894,7 @@ Map<Entity *> generate_minimum_dependency_map(CheckerInfo *info, Entity *start)
|
||||
#include "expr.cpp"
|
||||
#include "stmt.cpp"
|
||||
|
||||
void init_runtime_types(Checker *c) {
|
||||
void init_preload_types(Checker *c) {
|
||||
if (t_type_info == NULL) {
|
||||
Entity *e = current_scope_lookup_entity(c->global_scope, make_string("Type_Info"));
|
||||
if (e == NULL) {
|
||||
@@ -900,13 +903,13 @@ void init_runtime_types(Checker *c) {
|
||||
}
|
||||
t_type_info = e->type;
|
||||
t_type_info_ptr = make_type_pointer(c->allocator, t_type_info);
|
||||
|
||||
GB_ASSERT(is_type_union(e->type));
|
||||
auto *record = &base_type(e->type)->Record;
|
||||
|
||||
t_type_info_member = record->other_fields[0]->type;
|
||||
t_type_info_member_ptr = make_type_pointer(c->allocator, t_type_info_member);
|
||||
|
||||
if (record->field_count != 16) {
|
||||
if (record->field_count != 17) {
|
||||
compiler_error("Invalid `Type_Info` layout");
|
||||
}
|
||||
t_type_info_named = record->fields[ 1]->type;
|
||||
@@ -915,15 +918,16 @@ void init_runtime_types(Checker *c) {
|
||||
t_type_info_string = record->fields[ 4]->type;
|
||||
t_type_info_boolean = record->fields[ 5]->type;
|
||||
t_type_info_pointer = record->fields[ 6]->type;
|
||||
t_type_info_procedure = record->fields[ 7]->type;
|
||||
t_type_info_array = record->fields[ 8]->type;
|
||||
t_type_info_slice = record->fields[ 9]->type;
|
||||
t_type_info_vector = record->fields[10]->type;
|
||||
t_type_info_tuple = record->fields[11]->type;
|
||||
t_type_info_struct = record->fields[12]->type;
|
||||
t_type_info_union = record->fields[13]->type;
|
||||
t_type_info_raw_union = record->fields[14]->type;
|
||||
t_type_info_enum = record->fields[15]->type;
|
||||
t_type_info_maybe = record->fields[ 7]->type;
|
||||
t_type_info_procedure = record->fields[ 8]->type;
|
||||
t_type_info_array = record->fields[ 9]->type;
|
||||
t_type_info_slice = record->fields[10]->type;
|
||||
t_type_info_vector = record->fields[11]->type;
|
||||
t_type_info_tuple = record->fields[12]->type;
|
||||
t_type_info_struct = record->fields[13]->type;
|
||||
t_type_info_union = record->fields[14]->type;
|
||||
t_type_info_raw_union = record->fields[15]->type;
|
||||
t_type_info_enum = record->fields[16]->type;
|
||||
}
|
||||
|
||||
if (t_allocator == NULL) {
|
||||
@@ -1206,7 +1210,7 @@ void check_parsed_files(Checker *c) {
|
||||
|
||||
check_global_entity(c, Entity_TypeName);
|
||||
|
||||
init_runtime_types(c);
|
||||
init_preload_types(c);
|
||||
|
||||
check_global_entity(c, Entity_Constant);
|
||||
check_global_entity(c, Entity_Procedure);
|
||||
|
||||
+74
-17
@@ -7,7 +7,7 @@ void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, T
|
||||
Entity * check_selector (Checker *c, Operand *operand, AstNode *node);
|
||||
void check_not_tuple (Checker *c, Operand *operand);
|
||||
b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value);
|
||||
void convert_to_typed (Checker *c, Operand *operand, Type *target_type);
|
||||
void convert_to_typed (Checker *c, Operand *operand, Type *target_type, i32 level = 0);
|
||||
gbString expr_to_string (AstNode *expression);
|
||||
void check_entity_decl (Checker *c, Entity *e, DeclInfo *decl, Type *named_type, CycleChecker *cycle_checker = NULL);
|
||||
void check_proc_body (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body);
|
||||
@@ -88,6 +88,11 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_type_maybe(dst)) {
|
||||
Type *elem = base_type(dst)->Maybe.elem;
|
||||
return are_types_identical(elem, src);
|
||||
}
|
||||
|
||||
if (is_type_untyped_nil(src)) {
|
||||
return type_has_nil(dst);
|
||||
}
|
||||
@@ -103,6 +108,8 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (dst->kind == Type_Array && src->kind == Type_Array) {
|
||||
if (are_types_identical(dst->Array.elem, src->Array.elem)) {
|
||||
return dst->Array.count == src->Array.count;
|
||||
@@ -138,9 +145,7 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1048,6 +1053,12 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
|
||||
goto end;
|
||||
case_end;
|
||||
|
||||
case_ast_node(mt, MaybeType, e);
|
||||
Type *elem = check_type(c, mt->type);
|
||||
type = make_type_maybe(c->allocator, elem);
|
||||
goto end;
|
||||
case_end;
|
||||
|
||||
case_ast_node(at, ArrayType, e);
|
||||
if (at->count != NULL) {
|
||||
Type *elem = check_type(c, at->elem, NULL, cycle_checker);
|
||||
@@ -1243,8 +1254,10 @@ b32 check_binary_op(Checker *c, Operand *o, Token op) {
|
||||
|
||||
}
|
||||
b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value) {
|
||||
if (in_value.kind == ExactValue_Invalid)
|
||||
if (in_value.kind == ExactValue_Invalid) {
|
||||
// NOTE(bill): There's already been an error
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_type_boolean(type)) {
|
||||
return in_value.kind == ExactValue_Bool;
|
||||
@@ -1252,8 +1265,9 @@ b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exac
|
||||
return in_value.kind == ExactValue_String;
|
||||
} else if (is_type_integer(type)) {
|
||||
ExactValue v = exact_value_to_integer(in_value);
|
||||
if (v.kind != ExactValue_Integer)
|
||||
if (v.kind != ExactValue_Integer) {
|
||||
return false;
|
||||
}
|
||||
if (out_value) *out_value = v;
|
||||
i64 i = v.value_integer;
|
||||
u64 u = *cast(u64 *)&i;
|
||||
@@ -1287,8 +1301,9 @@ b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exac
|
||||
}
|
||||
} else if (is_type_float(type)) {
|
||||
ExactValue v = exact_value_to_float(in_value);
|
||||
if (v.kind != ExactValue_Float)
|
||||
if (v.kind != ExactValue_Float) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_f32:
|
||||
@@ -1986,7 +2001,7 @@ void convert_untyped_error(Checker *c, Operand *operand, Type *target_type) {
|
||||
operand->mode = Addressing_Invalid;
|
||||
}
|
||||
|
||||
void convert_to_typed(Checker *c, Operand *operand, Type *target_type) {
|
||||
void convert_to_typed(Checker *c, Operand *operand, Type *target_type, i32 level) {
|
||||
GB_ASSERT_NOT_NULL(target_type);
|
||||
if (operand->mode == Addressing_Invalid ||
|
||||
is_type_typed(operand->type) ||
|
||||
@@ -2018,7 +2033,6 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type) {
|
||||
}
|
||||
update_expr_value(c, operand->expr, operand->value);
|
||||
} else {
|
||||
// TODO(bill): Is this really needed?
|
||||
switch (operand->type->Basic.kind) {
|
||||
case Basic_UntypedBool:
|
||||
if (!is_type_boolean(target_type)) {
|
||||
@@ -2045,14 +2059,24 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type) {
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_Maybe:
|
||||
if (is_type_untyped_nil(operand->type)) {
|
||||
// Okay
|
||||
} else if (level == 0) {
|
||||
convert_to_typed(c, operand, t->Maybe.elem, level+1);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
if (!type_has_nil(target_type)) {
|
||||
if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) {
|
||||
convert_untyped_error(c, operand, target_type);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
operand->type = target_type;
|
||||
update_expr_type(c, operand->expr, target_type, true);
|
||||
}
|
||||
@@ -2361,15 +2385,16 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
break;
|
||||
|
||||
case BuiltinProc_offset_of: {
|
||||
// offset_val :: proc(Type, field) -> int
|
||||
// offset_of :: proc(Type, field) -> int
|
||||
Operand op = {};
|
||||
Type *type = base_type(check_type(c, ce->args[0]));
|
||||
if (type != NULL || type == t_invalid) {
|
||||
Type *bt = check_type(c, ce->args[0]);
|
||||
Type *type = base_type(bt);
|
||||
if (type == NULL || type == t_invalid) {
|
||||
error(ast_node_token(ce->args[0]), "Expected a type for `offset_of`");
|
||||
return false;
|
||||
}
|
||||
if (!is_type_struct(type)) {
|
||||
error(ast_node_token(ce->args[0]), "Expected a structure type for `offset_of`");
|
||||
error(ast_node_token(ce->args[0]), "Expected a struct type for `offset_of`");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2384,7 +2409,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
ast_node(arg, Ident, field_arg);
|
||||
Selection sel = lookup_field(c->allocator, type, arg->string, operand->mode == Addressing_Type);
|
||||
if (sel.entity == NULL) {
|
||||
gbString type_str = type_to_string(type);
|
||||
gbString type_str = type_to_string(bt);
|
||||
defer (gb_string_free(type_str));
|
||||
error(ast_node_token(ce->args[0]),
|
||||
"`%s` has no field named `%.*s`", type_str, LIT(arg->string));
|
||||
return false;
|
||||
@@ -2397,7 +2423,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
} break;
|
||||
|
||||
case BuiltinProc_offset_of_val: {
|
||||
// offset_val :: proc(val: expression) -> int
|
||||
// offset_of_val :: proc(val: expression) -> int
|
||||
AstNode *arg = unparen_expr(ce->args[0]);
|
||||
if (arg->kind != AstNode_SelectorExpr) {
|
||||
gbString str = expr_to_string(arg);
|
||||
@@ -2418,6 +2444,11 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_type_struct(type)) {
|
||||
error(ast_node_token(ce->args[0]), "Expected a struct type for `offset_of_val`");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
ast_node(i, Ident, s->selector);
|
||||
Selection sel = lookup_field(c->allocator, type, i->string, operand->mode == Addressing_Type);
|
||||
@@ -2677,8 +2708,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
|
||||
if (operand->mode == Addressing_Constant &&
|
||||
op.mode == Addressing_Constant) {
|
||||
u8 *ptr = cast(u8 *)operand->value.value_pointer;
|
||||
isize elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem);
|
||||
i64 ptr = operand->value.value_pointer;
|
||||
i64 elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem);
|
||||
ptr += elem_size * op.value.value_integer;
|
||||
operand->value.value_pointer = ptr;
|
||||
} else {
|
||||
@@ -2997,6 +3028,31 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = t_string;
|
||||
} break;
|
||||
|
||||
case BuiltinProc_maybe_value: {
|
||||
Type *type = operand->type;
|
||||
if (!is_type_maybe(type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
defer (gb_string_free(type_str));
|
||||
error(ast_node_token(call),
|
||||
"Expected a maybe to `maybe_value`, got `%s`",
|
||||
type_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
|
||||
Entity **variables = gb_alloc_array(c->allocator, Entity *, 2);
|
||||
Type *elem = base_type(type)->Maybe.elem;
|
||||
Token t = make_token_ident(make_string(""));
|
||||
variables[0] = make_entity_param(c->allocator, NULL, t, elem, false);
|
||||
variables[1] = make_entity_param(c->allocator, NULL, t, t_bool, false);
|
||||
|
||||
Type *tuple = make_type_tuple(c->allocator);
|
||||
tuple->Tuple.variables = variables;
|
||||
tuple->Tuple.variable_count = 2;
|
||||
operand->type = tuple;
|
||||
} break;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -3677,6 +3733,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
||||
|
||||
case AstNode_ProcType:
|
||||
case AstNode_PointerType:
|
||||
case AstNode_MaybeType:
|
||||
case AstNode_ArrayType:
|
||||
case AstNode_VectorType:
|
||||
case AstNode_StructType:
|
||||
|
||||
+51
-11
@@ -58,11 +58,12 @@ struct BasicType {
|
||||
#define TYPE_KINDS \
|
||||
TYPE_KIND(Invalid), \
|
||||
TYPE_KIND(Basic), \
|
||||
TYPE_KIND(Pointer), \
|
||||
TYPE_KIND(Array), \
|
||||
TYPE_KIND(Vector), \
|
||||
TYPE_KIND(Slice), \
|
||||
TYPE_KIND(Maybe), \
|
||||
TYPE_KIND(Record), \
|
||||
TYPE_KIND(Pointer), \
|
||||
TYPE_KIND(Named), \
|
||||
TYPE_KIND(Tuple), \
|
||||
TYPE_KIND(Proc), \
|
||||
@@ -96,6 +97,7 @@ struct Type {
|
||||
u32 flags; // See parser.cpp `enum TypeFlag`
|
||||
union {
|
||||
BasicType Basic;
|
||||
struct { Type *elem; } Pointer;
|
||||
struct {
|
||||
Type *elem;
|
||||
i64 count;
|
||||
@@ -107,6 +109,9 @@ struct Type {
|
||||
struct {
|
||||
Type *elem;
|
||||
} Slice;
|
||||
struct {
|
||||
Type *elem;
|
||||
} Maybe;
|
||||
struct {
|
||||
TypeRecordKind kind;
|
||||
|
||||
@@ -134,7 +139,6 @@ struct Type {
|
||||
Entity **other_fields;
|
||||
isize other_field_count;
|
||||
} Record;
|
||||
struct { Type *elem; } Pointer;
|
||||
struct {
|
||||
String name;
|
||||
Type * base;
|
||||
@@ -187,6 +191,18 @@ Type *make_type_basic(gbAllocator a, BasicType basic) {
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *make_type_pointer(gbAllocator a, Type *elem) {
|
||||
Type *t = alloc_type(a, Type_Pointer);
|
||||
t->Pointer.elem = elem;
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *make_type_maybe(gbAllocator a, Type *elem) {
|
||||
Type *t = alloc_type(a, Type_Maybe);
|
||||
t->Maybe.elem = elem;
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *make_type_array(gbAllocator a, Type *elem, i64 count) {
|
||||
Type *t = alloc_type(a, Type_Array);
|
||||
t->Array.elem = elem;
|
||||
@@ -207,6 +223,7 @@ Type *make_type_slice(gbAllocator a, Type *elem) {
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
Type *make_type_struct(gbAllocator a) {
|
||||
Type *t = alloc_type(a, Type_Record);
|
||||
t->Record.kind = TypeRecord_Struct;
|
||||
@@ -231,11 +248,7 @@ Type *make_type_enum(gbAllocator a) {
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *make_type_pointer(gbAllocator a, Type *elem) {
|
||||
Type *t = alloc_type(a, Type_Pointer);
|
||||
t->Pointer.elem = elem;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
Type *make_type_named(gbAllocator a, String name, Type *base, Entity *type_name) {
|
||||
Type *t = alloc_type(a, Type_Named);
|
||||
@@ -357,6 +370,7 @@ gb_global Type *t_type_info_float = NULL;
|
||||
gb_global Type *t_type_info_string = NULL;
|
||||
gb_global Type *t_type_info_boolean = NULL;
|
||||
gb_global Type *t_type_info_pointer = NULL;
|
||||
gb_global Type *t_type_info_maybe = NULL;
|
||||
gb_global Type *t_type_info_procedure = NULL;
|
||||
gb_global Type *t_type_info_array = NULL;
|
||||
gb_global Type *t_type_info_slice = NULL;
|
||||
@@ -468,6 +482,15 @@ b32 is_type_pointer(Type *t) {
|
||||
}
|
||||
return t->kind == Type_Pointer;
|
||||
}
|
||||
b32 is_type_maybe(Type *t) {
|
||||
t = base_type(t);
|
||||
return t->kind == Type_Maybe;
|
||||
}
|
||||
b32 is_type_tuple(Type *t) {
|
||||
t = base_type(t);
|
||||
return t->kind == Type_Tuple;
|
||||
}
|
||||
|
||||
|
||||
b32 is_type_int_or_uint(Type *t) {
|
||||
if (t->kind == Type_Basic) {
|
||||
@@ -671,6 +694,11 @@ b32 are_types_identical(Type *x, Type *y) {
|
||||
return are_types_identical(x->Pointer.elem, y->Pointer.elem);
|
||||
break;
|
||||
|
||||
case Type_Maybe:
|
||||
if (y->kind == Type_Maybe)
|
||||
return are_types_identical(x->Maybe.elem, y->Maybe.elem);
|
||||
break;
|
||||
|
||||
case Type_Named:
|
||||
if (y->kind == Type_Named) {
|
||||
return x->Named.base == y->Named.base;
|
||||
@@ -987,6 +1015,9 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
return max;
|
||||
} break;
|
||||
|
||||
case Type_Maybe:
|
||||
return gb_max(type_align_of(s, allocator, t->Maybe.elem), type_align_of(s, allocator, t_bool));
|
||||
|
||||
case Type_Record: {
|
||||
switch (t->Record.kind) {
|
||||
case TypeRecord_Struct:
|
||||
@@ -1115,6 +1146,9 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
case Type_Slice: // ptr + len + cap
|
||||
return 3 * s.word_size;
|
||||
|
||||
case Type_Maybe: // value + bool
|
||||
return type_size_of(s, allocator, t->Maybe.elem) + type_size_of(s, allocator, t_bool);
|
||||
|
||||
case Type_Record: {
|
||||
switch (t->Record.kind) {
|
||||
case TypeRecord_Struct: {
|
||||
@@ -1199,6 +1233,16 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
str = gb_string_append_length(str, type->Basic.name.text, type->Basic.name.len);
|
||||
break;
|
||||
|
||||
case Type_Pointer:
|
||||
str = gb_string_appendc(str, "^");
|
||||
str = write_type_to_string(str, type->Pointer.elem);
|
||||
break;
|
||||
|
||||
case Type_Maybe:
|
||||
str = gb_string_appendc(str, "?");
|
||||
str = write_type_to_string(str, type->Maybe.elem);
|
||||
break;
|
||||
|
||||
case Type_Array:
|
||||
str = gb_string_appendc(str, gb_bprintf("[%td]", type->Array.count));
|
||||
str = write_type_to_string(str, type->Array.elem);
|
||||
@@ -1266,10 +1310,6 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case Type_Pointer:
|
||||
str = gb_string_appendc(str, "^");
|
||||
str = write_type_to_string(str, type->Pointer.elem);
|
||||
break;
|
||||
|
||||
case Type_Named:
|
||||
if (type->Named.type_name != NULL) {
|
||||
|
||||
Reference in New Issue
Block a user