u32 type_structlike_mem_count(Type* type);
u32 type_structlike_is_simple(Type* type);
b32 type_is_sl_constructable(Type* type);
-b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from);
+b32 type_constructed_from_poly(Type* base, struct AstType* from);
Type* type_struct_is_just_one_basic_value(Type *type);
#endif // #ifndef ONYX_TYPES
// If the destination type is an optional, and the node's type is a value of
// the same underlying type, then we can construct an optional with a value
// implicitly. This makes working with optionals barable.
- if (type_struct_constructed_from_poly_struct(type, builtin_optional_type)) {
- TypeMatch match = unify_node_and_type_(pnode, type->Struct.poly_sln[0].type, permanent);
+ if (type_constructed_from_poly(type, builtin_optional_type)) {
+ TypeMatch match = unify_node_and_type_(pnode, type->Union.poly_sln[0].type, permanent);
if (match == TYPE_MATCH_SUCCESS) {
if (permanent) {
AstStructLiteral *opt_lit = make_optional_literal_some(context.ast_alloc, node, type);
arguments_initialize(&opt_lit->args);
arguments_ensure_length(&opt_lit->args, 2);
- opt_lit->args.values[0] = (AstTyped *) make_bool_literal(a, 1);
+ opt_lit->args.values[0] = (AstTyped *) make_int_literal(a, 2);
opt_lit->args.values[1] = expr;
opt_lit->type = opt_type;
+ opt_lit->args.values[0]->type = opt_type->Union.tag_type;
return opt_lit;
}
fornode->loop_type = For_Loop_DynArr;
}
- else if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
+ else if (type_constructed_from_poly(iter_type, builtin_iterator_type)) {
if (fornode->by_pointer) {
ERROR(error_loc, "Cannot iterate by pointer over an iterator.");
}
old_inside_for_iterator = context.checker.inside_for_iterator;
context.checker.inside_for_iterator = 0;
iter_type = fornode->iter->type;
- if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
+ if (type_constructed_from_poly(iter_type, builtin_iterator_type)) {
context.checker.inside_for_iterator = 1;
}
if (sl->type->kind == Type_Kind_Union) {
if ((sl->flags & Ast_Flag_Has_Been_Checked) != 0) return Check_Success;
+ Type *union_type = sl->type;
+
+ if (bh_arr_length(sl->args.values) == 0 && bh_arr_length(sl->args.named_values) == 0) {
+ // Produce an empty value of the first union type.
+ UnionVariant *uv = union_type->Union.variants[0].value;
+
+ AstNumLit *tag_value = make_int_literal(context.ast_alloc, uv->tag_value);
+ tag_value->type = union_type->Union.tag_type;
+
+ bh_arr_push(sl->args.values, (AstTyped *) tag_value);
+ bh_arr_push(sl->args.values, (AstTyped *) make_zero_value(context.ast_alloc, sl->token, uv->type));
+
+ sl->flags |= Ast_Flag_Has_Been_Checked;
+ return Check_Success;
+ }
+
if (bh_arr_length(sl->args.values) != 0 || bh_arr_length(sl->args.named_values) != 1) {
ERROR_(sl->token->pos, "Expected exactly one named member when constructing an instance of a union type, '%s'.", type_get_name(sl->type));
}
AstNamedValue* value = sl->args.named_values[0];
token_toggle_end(value->token);
- Type *union_type = sl->type;
UnionVariant *matched_variant = union_type->Union.variants[
shgeti(union_type->Union.variants, value->token->text)
].value;
case Ast_Kind_Macro:
case Ast_Kind_Symbol:
case Ast_Kind_Poly_Struct_Type:
+ case Ast_Kind_Poly_Union_Type:
case Ast_Kind_Basic_Type:
case Ast_Kind_Enum_Type:
case Ast_Kind_Enum_Value:
pc_type->params = params;
*next_insertion = (AstType *) pc_type;
+ } else {
+ next_insertion = NULL;
}
-
- next_insertion = NULL;
break;
}
}
}
- default:
- onyx_report_error(parser->curr->pos, Error_Critical, "unexpected token '%b'.", parser->curr->text, parser->curr->length);
+ case '.': {
consume_token(parser);
+ AstFieldAccess* field = make_node(AstFieldAccess, Ast_Kind_Field_Access);
+ field->token = expect_token(parser, Token_Type_Symbol);
+ field->expr = (AstTyped *) *next_insertion;
+
+ *next_insertion = (AstType *) field;
+ break;
+ }
+
+ default:
+ next_insertion = NULL;
break;
}
// Special case for `? T`
- if (type->kind == Type_Kind_Struct
- && type->Struct.constructed_from == builtin_optional_type) {
+ if (type->kind == Type_Kind_Union
+ && type->Union.constructed_from == builtin_optional_type) {
strncat(name_buf, "? ", 255);
- strncat(name_buf, type_get_name(type->Struct.poly_sln[0].type), 255);
+ strncat(name_buf, type_get_name(type->Union.poly_sln[0].type), 255);
return bh_aprintf(global_heap_allocator, "%s", name_buf);
}
}
}
-b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from) {
- if (struct_type == NULL) return 0;
- if (struct_type->kind != Type_Kind_Struct) return 0;
+b32 type_constructed_from_poly(Type* base, struct AstType* from) {
+ if (base == NULL) return 0;
+ if (base->kind == Type_Kind_Struct) {
+ return base->Struct.constructed_from == from;
+ }
+
+ if (base->kind == Type_Kind_Union) {
+ return base->Union.constructed_from == from;
+ }
- return struct_type->Struct.constructed_from == from;
+ return 0;
}
Type* type_struct_is_just_one_basic_value(Type *type) {
return symbol_raw_resolve(stype->scope, symbol);
}
+ case Ast_Kind_Poly_Union_Type: {
+ AstPolyUnionType* utype = ((AstPolyUnionType *) node);
+ return symbol_raw_resolve(utype->scope, symbol);
+ }
+
case Ast_Kind_Poly_Call_Type: {
AstPolyCallType* pctype = (AstPolyCallType *) node;
if (pctype->resolved_type) {
return &utype->scope;
}
+ case Ast_Kind_Poly_Union_Type: {
+ AstPolyUnionType* putype = (AstPolyUnionType *) node;
+ return &putype->scope;
+ }
+
case Ast_Kind_Poly_Call_Type: {
AstPolyCallType* pctype = (AstPolyCallType *) node;
Type *t = type_build_from_ast(context.ast_alloc, (AstType *) pctype);
for types like '? i32'. In other words, '? i32' is equivalent to
'Optional(i32)'.
"""
-Optional :: struct (Value_Type: type_expr) {
- has_value: bool;
- value: Value_Type;
+Optional :: union (Value_Type: type_expr) {
+ None: void;
+ Some: Value_Type;
}
the type will be inferred from the parameter type.
"""
make :: #match #locked {
- ((x: $T) => (?T).{ has_value = true, value = x }),
- ($T: type_expr, x: T) => ((?T).{ has_value = true, value = x })
+ ((x: $T) => (?T).{ Some = x }),
+ ($T: type_expr, x: T) => ((?T).{ Some = x })
}
#doc """
is mostly useless, because you can use `.{}` in type inferred
places to avoid having to specify the type.
"""
- empty :: macro (T: type_expr) => (?T).{};
+ empty :: macro (T: type_expr) => (?T).{ None = .{} };
#doc """
Converts a pointer to an optional by defining `null` to be `None`,
from_ptr :: macro (p: &$T) -> ?T {
p_ := p;
if p_ do return *p_;
- return .{};
+ return .{ None = .{} };
}
#doc """
no value is present.
"""
value_or :: (o: ?$T, default: T) -> T {
- if !o.has_value do return default;
- return o.value;
+ switch o {
+ case .Some => v do return v;
+ case #default do return default;
+ }
}
#doc "Clears the value in the Optional, zeroing the memory of the value."
reset :: (o: &?$T) {
- o.has_value = false;
- core.memory.set(&o.value, 0, sizeof T);
+ *o = .{ None = .{} };
}
#doc "Sets the value in the Optional."
set :: (o: &?$T, value: T) {
- o.has_value = true;
- o.value = value;
+ *o = .{ Some = value };
}
#doc "Monadic chaining operation."
and_then :: (o: ?$T, transform: (T) -> ?$R) -> ?R {
- if !o.has_value do return .{};
- return transform(o.value);
+ switch o {
+ case .Some => v do return transform(v);
+ case #default do return .{ None = .{} };
+ }
}
#doc "Changes the value inside the optional, if present."
transform :: (o: ?$T, transform: (T) -> $R) -> ?R {
- if !o.has_value do return .{};
- return Optional.make(transform(o.value));
+ switch o {
+ case .Some => v do return .{ Some = transform(v) };
+ case #default do return .{ None = .{} };
+ }
}
#doc """
provide a function to generate a value.
"""
or_else :: (o: ?$T, generate: () -> ?T) -> ?T {
- if o.has_value do return o;
- return generate();
+ switch o {
+ case .Some => v do return o;
+ case #default do return generate();
+ }
}
#doc """
handler must take care of it.
"""
unwrap :: (o: ?$T) -> T {
- if o.has_value do return o.value;
- assert(false, "Unwrapping empty Optional.");
+ switch o {
+ case .Some => v do return v;
+ case #default {
+ assert(false, "Unwrapping empty Optional.");
+ }
+ }
}
- or_return :: macro (o: ?$T) -> T {
- value := o;
- if value.has_value do return value.value;
+ #doc """
+ Returns a pointer to the value inside the optional, if there is one.
+ If not, an assertion is thrown and the context's assert handler must
+ take care of it.
+ """
+ unwrap_ptr :: (o: ?$T) -> &T {
+ switch o {
+ case .Some => &v do return v;
+ case #default {
+ assert(false, "Unwrapping empty Optional.");
+ }
+ }
+ }
- return return .{};
+ or_return :: macro (o: ?$T) -> T {
+ switch value := o; value {
+ case .Some => v do return v;
+ case #default {
+ return return .{};
+ }
+ }
}
catch :: macro (o: ?$T, body: Code) -> T {
- value := o;
- if value.has_value do return value.value;
+ switch value := o; value {
+ case .Some => v do return v;
+ }
#unquote body;
}
hash :: (o: ?$T/core.hash.Hashable) -> u32 {
- if !o.has_value do return 0;
- return core.hash.hash(o.value);
+ switch o {
+ case .Some => v do return core.hash.hash(v);
+ case #default do return 0;
+ }
}
}
#operator == (o1, o2: ?$T) -> bool {
- if o1.has_value != o2.has_value do return false;
- if !o1.has_value do return true;
- return o1.value == o2.value;
+ if cast(Optional(T).tag_enum, o1) != cast(Optional(T).tag_enum, o2) do return false;
+ switch o1 {
+ case .None do return true;
+ case .Some => v1 {
+ v2 := o2->unwrap();
+ return v1 == v2;
+ }
+ }
}
#operator ?? macro (opt: ?$T, default: T) -> T {
- value := opt;
- if value do return value.value;
-
- return default;
+ switch value := opt; value {
+ case .Some => v do return v;
+ case #default do return default;
+ }
}
#operator ?? macro (opt: ?$T, catch: Code) -> T {
- value := opt;
- if value do return value.value;
+ switch value := opt; value {
+ case .Some => v do return v;
+ }
#unquote catch;
}
#operator ? macro (opt: ?$T) -> T {
- value := opt;
- if value do return value.value;
-
- return return .{};
+ switch value := opt; value {
+ case .Some => v do return v;
+ case #default do return return .{};
+ }
}
#overload
-__implicit_bool_cast :: macro (o: ?$T) => o.has_value;
+__implicit_bool_cast :: macro (o: ?$T) => cast(Optional(T).tag_enum, o) == .Some;
if info.kind == .Struct {
s := cast(&Type_Info_Struct) info;
- if s.constructed_from == Optional {
- opt := cast(&?bool) v.data;
-
- if opt.has_value {
- format := *formatting;
- format.quote_strings = true;
-
- output->write("Some(");
- format_any(output, &format, .{ cast([&] u8) v.data + s.members[1].offset, s.members[1].type });
- output->write(")");
-
- } else {
- output->write("None");
- }
-
- return;
- }
-
if s.name.count > 0 {
output->write(s.name);
output->write(" { ");
}
output->write(variant.name);
- output->write("(");
-
- format_any(output, formatting, any.{ cast([&] u8) v.data + u.alignment, variant.type });
-
- output->write(")");
+ if variant.type != void {
+ output->write("(");
+ format_any(output, formatting, any.{ cast([&] u8) v.data + u.alignment, variant.type });
+ output->write(")");
+ }
}
}
}
println(bar(foo(.{})));
println(bar(foo(20)));
-}
\ No newline at end of file
+}
use core {*}
-NewOptional :: union (T: type_expr) {
- None: void;
- Some: T;
-}
-
-unwrap_optional :: (o: NewOptional($T)) -> T {
- switch o {
- case .Some => v do return v;
- case .None do return .{};
- }
-}
-
-create_optional :: () -> NewOptional(i32) {
- return .{ Some = i32 };
+create_optional :: () -> ? i32 {
+ return .{ Some = 123 };
}
new_optional_test :: () {
v := create_optional();
- v2 := NewOptional(str).{ None = .{} };
+ v2 := Optional(str).{ None = .{} };
println(v);
println(v2);
- println(unwrap_optional(v));
+ println(v->unwrap());
}
union_is :: macro (u: $U, $variant: U.tag_enum) -> bool {