NODE(PolyStructType) \
NODE(PolyStructParam) \
NODE(PolyCallType) \
+ NODE(UnionType) \
+ NODE(UnionVariant) \
+ NODE(PolyUnionType) \
NODE(EnumType) \
NODE(EnumValue) \
NODE(TypeAlias) \
Ast_Kind_Struct_Type,
Ast_Kind_Poly_Struct_Type,
Ast_Kind_Poly_Call_Type,
+ Ast_Kind_Union_Type,
+ Ast_Kind_Poly_Union_Type,
Ast_Kind_Enum_Type,
Ast_Kind_Type_Alias,
Ast_Kind_Type_Raw_Alias,
Ast_Kind_Type_End,
Ast_Kind_Struct_Member,
+ Ast_Kind_Union_Variant,
Ast_Kind_Enum_Value,
Ast_Kind_NumLit,
// NOTE: These nodes can be either AstTypes, or AstTyped expressions.
bh_arr(AstNode *) params;
};
+struct AstUnionType {
+ AstType_base;
+ char *name;
+
+ bh_arr(AstUnionVariant *) variants;
+ bh_arr(AstTyped *) meta_tags;
+
+ // NOTE: Used to cache the actual type, since building
+ // a union type is kind of complicated and should
+ // only happen once.
+ Type *utcache;
+
+ // NOTE: This type is used when the union has not been
+ // completely generated, but is a valid pointer to where the
+ // type will be generated to.
+ Type *pending_type;
+
+ // NOTE: Used to store statically bound expressions in the union.
+ Scope* scope;
+
+ OnyxFilePos polymorphic_error_loc;
+ ConstraintContext constraints;
+
+ bh_arr(AstType *) polymorphic_argument_types;
+ bh_arr(AstPolySolution) polymorphic_arguments;
+
+ b32 pending_type_is_valid : 1;
+ // b32 ready_to_build_type : 1;
+};
+struct AstUnionVariant {
+ AstTyped_base;
+ bh_arr(AstTyped *) meta_tags;
+};
struct AstEnumType {
AstType_base;
char *name;
Token_Type_Keyword_Start,
Token_Type_Keyword_Package,
Token_Type_Keyword_Struct,
+ Token_Type_Keyword_Union,
Token_Type_Keyword_Enum,
Token_Type_Keyword_Use,
Token_Type_Keyword_If,
SPS_Uses_Done,
} StructProcessingStatus;
+typedef struct UnionVariant {
+ char *name;
+ Type *type;
+ u32 tag_value;
+ bh_arr(struct AstTyped *) meta_tags;
+ struct OnyxToken *token;
+} UnionVariant;
+
#define TYPE_KINDS \
TYPE_KIND(Basic, TypeBasic) \
TYPE_KIND(Pointer, struct { \
TYPE_KIND(Distinct, struct { \
char* name; \
Type* base_type; \
+ }) \
+ TYPE_KIND(Union, struct { \
+ u32 size; \
+ u32 alignment; \
+ char* name; \
+ Type* tag_type; \
+ Table(UnionVariant *) variants; \
+ bh_arr(struct AstPolySolution) poly_sln; \
+ struct AstType *constructed_from; \
+ bh_arr(struct AstTyped *) meta_tags; \
})
+
typedef enum TypeKind {
Type_Kind_Invalid,
"STRUCT TYPE",
"POLYMORPHIC STRUCT TYPE",
"POLYMORPHIC STRUCT CALL TYPE",
+ "UNION TYPE",
+ "POLYMORPHIC UNION TYPE",
"ENUM TYPE",
"TYPE_ALIAS",
"TYPE RAW ALIAS",
"TYPE_END (BAD)",
"STRUCT MEMBER",
+ "UNION VARIANT",
"ENUM VALUE",
"NUMERIC LITERAL",
if (sl->type == NULL)
YIELD(sl->token->pos, "Trying to resolve type of struct literal.");
}
+
+ if (sl->type->kind == Type_Kind_Union) {
+ 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;
+ token_toggle_end(value->token);
+
+ if (!matched_variant) {
+ ERROR_(value->token->pos, "'%b' is not a variant of '%s'.",
+ value->token->text, value->token->length, type_get_name(union_type));
+ }
+
+ TYPE_CHECK(&value->value, matched_variant->type) {
+ ERROR_(value->token->pos,
+ "Mismatched type in initialized type. Expected something of type '%s', got '%s'.",
+ type_get_name(matched_variant->type),
+ type_get_name(value->value->type));
+ }
+
+ AstNumLit *tag_value = make_int_literal(context.ast_alloc, matched_variant->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, value->value);
+ return Check_Success;
+ }
if (!type_is_structlike_strict(sl->type)) {
//
return Check_Success;
}
+static void mark_all_functions_used_in_scope(Scope *scope) {
+ //
+ // This ensures that all procedures defined inside of a structure are
+ // not pruned and omitted from the binary. This is because a large
+ // use-case of procedures in structures is dynamically linking them
+ // using the type info data.
+ if (scope) {
+ fori (i, 0, shlen(scope->symbols)) {
+ AstNode* node = scope->symbols[i].value;
+ if (node->kind == Ast_Kind_Function) {
+ node->flags |= Ast_Flag_Function_Used;
+ }
+ }
+ }
+}
+
+CheckStatus check_meta_tags(bh_arr(AstTyped *) tags) {
+ if (tags) {
+ bh_arr_each(AstTyped *, meta, tags) {
+ CHECK(expression, meta);
+ resolve_expression_type(*meta);
+
+ if (((*meta)->flags & Ast_Flag_Comptime) == 0) {
+ onyx_report_error((*meta)->token->pos, Error_Critical, "#tag expressions are expected to be compile-time known.");
+ return Check_Error;
+ }
+ }
+ }
+
+ return Check_Success;
+}
+
CheckStatus check_struct(AstStructType* s_node) {
if (s_node->entity_defaults && s_node->entity_defaults->state < Entity_State_Check_Types)
YIELD(s_node->token->pos, "Waiting for struct member defaults to pass symbol resolution.");
CHECK(constraint_context, &s_node->constraints, s_node->scope, pos);
}
- //
- // This ensures that all procedures defined inside of a structure are
- // not pruned and omitted from the binary. This is because a large
- // use-case of procedures in structures is dynamically linking them
- // using the type info data.
- if (s_node->scope) {
- fori (i, 0, shlen(s_node->scope->symbols)) {
- AstNode* node = s_node->scope->symbols[i].value;
- if (node->kind == Ast_Kind_Function) {
- node->flags |= Ast_Flag_Function_Used;
- }
- }
- }
+ mark_all_functions_used_in_scope(s_node->scope);
bh_arr_each(AstStructMember *, smem, s_node->members) {
if ((*smem)->type_node != NULL) {
if (s_node->entity_type && s_node->entity_type->state == Entity_State_Failed)
return Check_Failed;
- if (s_node->meta_tags) {
- bh_arr_each(AstTyped *, meta, s_node->meta_tags) {
- CHECK(expression, meta);
- resolve_expression_type(*meta);
-
- if (((*meta)->flags & Ast_Flag_Comptime) == 0) {
- onyx_report_error((*meta)->token->pos, Error_Critical, "#tag expressions are expected to be compile-time known.");
- return Check_Error;
- }
- }
- }
+ CHECK(meta_tags, s_node->meta_tags);
bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
if ((*smem)->initial_value && *(*smem)->initial_value) {
resolve_expression_type(*(*smem)->initial_value);
}
- if ((*smem)->meta_tags) {
- bh_arr_each(AstTyped *, meta, (*smem)->meta_tags) {
- CHECK(expression, meta);
- resolve_expression_type(*meta);
+ CHECK(meta_tags, (*smem)->meta_tags);
+ }
- if (((*meta)->flags & Ast_Flag_Comptime) == 0) {
- onyx_report_error((*meta)->token->pos, Error_Critical, "#tag expressions are expected to be compile-time known.");
- return Check_Error;
- }
- }
+ return Check_Success;
+}
+
+CheckStatus check_union(AstUnionType *u_node) {
+ if (u_node->constraints.constraints) {
+ u_node->constraints.produce_errors = (u_node->flags & Ast_Flag_Header_Check_No_Error) == 0;
+
+ OnyxFilePos pos = u_node->token->pos;
+ if (u_node->polymorphic_error_loc.filename) {
+ pos = u_node->polymorphic_error_loc;
}
+ CHECK(constraint_context, &u_node->constraints, u_node->scope, pos);
+ }
+
+ mark_all_functions_used_in_scope(u_node->scope);
+
+ CHECK(meta_tags, u_node->meta_tags);
+
+ bh_arr_each(AstUnionVariant *, variant, u_node->variants) {
+ CHECK(type, &(* variant)->type_node);
+ CHECK(meta_tags, (* variant)->meta_tags);
}
+ type_build_from_ast(context.ast_alloc, (AstType *) u_node);
+ if (u_node->pending_type == NULL || !u_node->pending_type_is_valid)
+ YIELD(u_node->token->pos, "Waiting for type to be constructed.");
+
+ u_node->utcache = u_node->pending_type;
return Check_Success;
}
// fallthrough
}
+ case Ast_Kind_Union_Type:
case Ast_Kind_Poly_Struct_Type:
case Ast_Kind_Type_Alias: {
ent.type = Entity_Type_Type_Alias;
"", // start
"package",
"struct",
+ "union",
"enum",
"use",
"if",
break;
case 'u':
LITERAL_TOKEN("use", 1, Token_Type_Keyword_Use);
+ LITERAL_TOKEN("union", 1, Token_Type_Keyword_Union);
break;
case 'w':
LITERAL_TOKEN("while", 1, Token_Type_Keyword_While);
return type_of;
}
-static void struct_type_create_scope(OnyxParser *parser, AstStructType *s_node) {
- if (!s_node->scope) {
- s_node->scope = scope_create(context.ast_alloc, parser->current_scope, s_node->token->pos);
+static void type_create_scope(OnyxParser *parser, Scope ** scope, OnyxToken* token) {
+ if (scope && !*scope) {
+ *scope = scope_create(context.ast_alloc, parser->current_scope, token->pos);
if (bh_arr_length(parser->current_symbol_stack) == 0) {
- s_node->scope->name = "<anonymous>";
+ (*scope)->name = "<anonymous>";
} else {
OnyxToken* current_symbol = bh_arr_last(parser->current_symbol_stack);
- s_node->scope->name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
+ (*scope)->name = bh_aprintf(global_heap_allocator, "%b", current_symbol->text, current_symbol->length);
}
}
}
+static void parse_meta_tags(OnyxParser *parser, bh_arr(AstTyped *) *out_arr) {
+ bh_arr(AstTyped *) meta_tags = NULL;
+ while (parse_possible_directive(parser, "tag") || consume_token_if_next(parser, '@')) {
+ if (meta_tags == NULL) bh_arr_new(global_heap_allocator, meta_tags, 1);
+
+ parser->tag_depth += 1;
+
+ do {
+ AstTyped* expr = parse_expression(parser, 0);
+ bh_arr_push(meta_tags, expr);
+ } while (consume_token_if_next(parser, ','));
+
+ parser->tag_depth -= 1;
+ }
+
+ *out_arr = meta_tags;
+}
+
static AstStructType* parse_struct(OnyxParser* parser) {
OnyxToken *s_token = expect_token(parser, Token_Type_Keyword_Struct);
flush_stored_tags(parser, &s_node->meta_tags);
- struct_type_create_scope(parser, s_node);
+ type_create_scope(parser, &s_node->scope, s_node->token);
Scope *scope_to_restore_parser_to = parser->current_scope;
Scope *scope_symbols_in_structures_should_be_bound_to = s_node->scope;
}
bh_arr(AstTyped *) meta_tags=NULL;
- while (parse_possible_directive(parser, "tag") || consume_token_if_next(parser, '@')) {
- if (meta_tags == NULL) bh_arr_new(global_heap_allocator, meta_tags, 1);
-
- parser->tag_depth += 1;
-
- do {
- AstTyped* expr = parse_expression(parser, 0);
- bh_arr_push(meta_tags, expr);
- } while (consume_token_if_next(parser, ','));
-
- parser->tag_depth -= 1;
- }
+ parse_meta_tags(parser, &meta_tags);
if (parser->curr->type == '}') {
consume_token(parser);
}
}
+static AstUnionType* parse_union(OnyxParser* parser) {
+ OnyxToken* union_token = expect_token(parser, Token_Type_Keyword_Union);
+
+ AstUnionType* u_node;
+ u_node = make_node(AstUnionType, Ast_Kind_Union_Type);
+ u_node->token = union_token;
+
+ flush_stored_tags(parser, &u_node->meta_tags);
+
+ type_create_scope(parser, &u_node->scope, u_node->token);
+ Scope *scope_to_restore_parser_to = parser->current_scope;
+ Scope *scope_symbols_in_unions_should_be_bound_to = u_node->scope;
+
+ // Parse constraints clause
+ if (parser->curr->type == Token_Type_Keyword_Where) {
+ parse_constraints(parser, &u_node->constraints);
+ }
+
+ parser->current_scope = scope_symbols_in_unions_should_be_bound_to;
+ bh_arr_new(global_heap_allocator, u_node->variants, 4);
+
+ expect_token(parser, '{');
+ while (!consume_token_if_next(parser, '}')) {
+ if (parser->hit_unexpected_token) return u_node;
+
+ if (next_tokens_are(parser, 3, Token_Type_Symbol, ':', ':')) {
+ OnyxToken* binding_name = expect_token(parser, Token_Type_Symbol);
+ consume_token(parser);
+
+ AstBinding* binding = parse_top_level_binding(parser, binding_name);
+ if (binding) ENTITY_SUBMIT(binding);
+
+ consume_token_if_next(parser, ';');
+ continue;
+ }
+
+ bh_arr(AstTyped *) meta_tags=NULL;
+ parse_meta_tags(parser, &meta_tags);
+
+ if (parser->curr->type == '}') {
+ consume_token(parser);
+ break;
+ }
+
+ AstUnionVariant *variant = make_node(AstUnionVariant, Ast_Kind_Union_Variant);
+ variant->meta_tags = meta_tags;
+ variant->token = expect_token(parser, Token_Type_Symbol);
+
+ expect_token(parser, ':');
+
+ variant->type_node = parse_type(parser);
+
+ bh_arr_push(u_node->variants, variant);
+
+ if (peek_token(0)->type != '}') {
+ expect_token(parser, ';');
+ }
+ }
+
+ parser->current_scope = scope_to_restore_parser_to;
+
+ ENTITY_SUBMIT(u_node);
+ return u_node;
+
+ //if (poly_struct != NULL) {
+ // // NOTE: Not a StructType
+ // return (AstStructType *) poly_struct;
+
+ //} else {
+ // ENTITY_SUBMIT(s_node);
+ // return s_node;
+ //}
+}
+
static AstInterface* parse_interface(OnyxParser* parser) {
AstInterface *interface = make_node(AstInterface, Ast_Kind_Interface);
interface->token = expect_token(parser, Token_Type_Keyword_Interface);
if (parser->curr->type == Token_Type_Keyword_Interface) return (AstTyped *) parse_interface(parser);
if (parser->curr->type == Token_Type_Keyword_Enum) return (AstTyped *) parse_enum_declaration(parser);
if (parser->curr->type == Token_Type_Keyword_Macro) return (AstTyped *) parse_macro(parser);
+ if (parser->curr->type == Token_Type_Keyword_Union) return (AstTyped *) parse_union(parser);
if (parser->curr->type == '#') {
if (parse_possible_directive(parser, "type")) {
case Ast_Kind_StrLit:
break;
+ // This makes a large assumption that the "name" member is in the same
+ // place on all of these structures. It is, but maybe there should be a
+ // "base" struct that these structure "inherit" from, so that is guaranteed?
case Ast_Kind_Interface:
case Ast_Kind_Struct_Type:
case Ast_Kind_Poly_Struct_Type:
case Ast_Kind_Enum_Type:
case Ast_Kind_Distinct_Type:
+ case Ast_Kind_Union_Type:
((AstStructType *) node)->name = generate_name_within_scope(parser, symbol);
goto default_case;
SymresStatus ss = symres_type(&member->type_node);
if (ss != Symres_Success) {
s_node->flags &= ~Ast_Flag_Type_Is_Resolved;
- if (s_node->scope) scope_leave();
+ scope_leave();
return ss;
}
}
}
- if (s_node->scope) scope_leave();
+ scope_leave();
+ return Symres_Success;
+}
+
+static SymresStatus symres_union_type(AstUnionType* u_node) {
+ if (u_node->flags & Ast_Flag_Type_Is_Resolved) return Symres_Success;
+ u_node->flags |= Ast_Flag_Comptime;
+
+ if (u_node->meta_tags) {
+ bh_arr_each(AstTyped *, meta, u_node->meta_tags) {
+ SYMRES(expression, meta);
+ }
+ }
+
+ u_node->flags |= Ast_Flag_Type_Is_Resolved;
+
+ assert(u_node->scope);
+ scope_enter(u_node->scope);
+
+ // if (u_node->polymorphic_argument_types) {
+ // assert(u_node->polymorphic_arguments);
+
+ // SymresStatus ss = Symres_Success, result;
+ // fori (i, 0, (i64) bh_arr_length(u_node->polymorphic_argument_types)) {
+ // result = symres_type(&u_node->polymorphic_argument_types[i]);
+ // if (result > ss) ss = result;
+
+ // if (u_node->polymorphic_arguments[i].value) {
+ // result = symres_expression(&u_node->polymorphic_arguments[i].value);
+ // if (result > ss) ss = result;
+ // }
+ // }
+ // }
+
+ if (u_node->constraints.constraints) {
+ bh_arr_each(AstConstraint *, constraint, u_node->constraints.constraints) {
+ SYMRES(constraint, *constraint);
+ }
+ }
+
+ fori (i, 0, bh_arr_length(u_node->variants)) {
+ AstUnionVariant *variant = u_node->variants[i];
+ // track_declaration_for_symbol_info(member->token->pos, (AstNode *) member);
+
+ assert(variant->type_node);
+ SymresStatus ss = symres_type(&variant->type_node);
+ if (ss != Symres_Success) {
+ u_node->flags &= ~Ast_Flag_Type_Is_Resolved;
+ scope_leave();
+ return ss;
+ }
+ }
+
+ scope_leave();
return Symres_Success;
}
}
case Ast_Kind_Struct_Type: SYMRES(struct_type, (AstStructType *) *type); break;
+ case Ast_Kind_Union_Type: SYMRES(union_type, (AstUnionType *) *type); break;
case Ast_Kind_Array_Type: {
AstArrayType* a_node = (AstArrayType *) *type;
}
static SymresStatus symres_enum(AstEnumType* enum_node) {
- if (enum_node->backing->kind == Ast_Kind_Symbol) SYMRES(symbol, (AstNode **) &enum_node->backing);
- if (enum_node->backing == NULL) return Symres_Error;
+ if (!enum_node->backing_type) {
+ if (enum_node->backing == NULL) return Symres_Error;
+ if (enum_node->backing->kind == Ast_Kind_Symbol) SYMRES(symbol, (AstNode **) &enum_node->backing);
- if (enum_node->scope == NULL) {
enum_node->backing_type = type_build_from_ast(context.ast_alloc, enum_node->backing);
+ }
+
+ if (enum_node->scope == NULL) {
enum_node->scope = scope_create(context.ast_alloc, current_scope, enum_node->token->pos);
type_build_from_ast(context.ast_alloc, (AstType *) enum_node);
#include "astnodes.h"
#include "utils.h"
#include "errors.h"
+#include "parser.h"
// NOTE: These have to be in the same order as Basic
Type basic_types[] = {
case Type_Kind_DynArray: return POINTER_SIZE + 8 + 8 + 2 * POINTER_SIZE; // data (8), count (4), capacity (4), allocator { func (4 + 4 + 8), data (8) }
case Type_Kind_Compound: return type->Compound.size;
case Type_Kind_Distinct: return type_size_of(type->Distinct.base_type);
+ case Type_Kind_Union: return type->Union.size;
default: return 0;
}
}
case Type_Kind_DynArray: return POINTER_SIZE;
case Type_Kind_Compound: return 4; // HACK
case Type_Kind_Distinct: return type_alignment_of(type->Distinct.base_type);
+ case Type_Kind_Union: return type->Union.alignment;
default: return 1;
}
}
s_type->Struct.mem_count = bh_arr_length(s_node->members);
s_type->Struct.meta_tags = s_node->meta_tags;
s_type->Struct.constructed_from = NULL;
+ s_type->Struct.poly_sln = NULL;
s_type->Struct.status = SPS_Start;
type_register(s_type);
s_type = s_node->pending_type;
}
- s_type->Struct.poly_sln = NULL;
-
bh_arr_clear(s_type->Struct.memarr);
shfree(s_type->Struct.members);
sh_new_arena(s_type->Struct.members);
return type_of->resolved_type;
}
- // Why does this have to be here?
- if (type_of->expr->type != NULL) {
- return type_of->expr->type;
- }
-
- return NULL;
+ return type_of->expr->type;
}
case Ast_Kind_Distinct_Type: {
Type *base_type = type_build_from_ast(alloc, distinct->base_type);
if (base_type == NULL) return NULL;
- // if (base_type->kind != Type_Kind_Basic && base_type->kind != Type_Kind_Pointer) {
- // onyx_report_error(distinct->token->pos, Error_Critical, "Distinct types can only be made out of primitive types. '%s' is not a primitive type.", type_get_name(base_type));
- // return NULL;
- // }
Type *distinct_type = type_create(Type_Kind_Distinct, alloc, 0);
distinct_type->Distinct.base_type = base_type;
type_register(distinct_type);
return distinct_type;
}
+
+ case Ast_Kind_Union_Type: {
+ AstUnionType* union_ = (AstUnionType *) type_node;
+ if (union_->utcache) return union_->utcache;
+ if (union_->pending_type && union_->pending_type_is_valid) return union_->pending_type;
+
+ Type *u_type;
+ if (union_->pending_type == NULL) {
+ u_type = type_create(Type_Kind_Union, alloc, 0);
+ union_->pending_type = u_type;
+
+ u_type->ast_type = type_node;
+ u_type->Union.name = union_->name;
+ u_type->Union.meta_tags = union_->meta_tags;
+ u_type->Union.constructed_from = NULL;
+ type_register(u_type);
+
+ u_type->Union.variants = NULL;
+ sh_new_arena(u_type->Union.variants);
+ } else {
+ u_type = union_->pending_type;
+ }
+
+ //
+ // All variants need to have type know.
+ bh_arr_each(AstUnionVariant *, pvariant, union_->variants) {
+ AstUnionVariant *variant = *pvariant;
+ if (!variant->type) {
+ variant->type = type_build_from_ast(alloc, variant->type_node);
+ }
+
+ if (!variant->type) {
+ if (context.cycle_detected) {
+ onyx_report_error(variant->token->pos, Error_Critical, "Unable to figure out the type of this union variant.");
+ }
+
+ union_->pending_type_is_valid = 0;
+ return accept_partial_types ? union_->pending_type : NULL;
+ }
+
+ if (variant->type->kind == Type_Kind_Struct && variant->type->Struct.status == SPS_Start) {
+ union_->pending_type_is_valid = 0;
+ return accept_partial_types ? union_->pending_type : NULL;
+ }
+ }
+
+ // From this point forward, there is no chance we will return early
+ // in a yielding fashion. Everything is either straight success or
+ // failure.
+
+ u32 size = 0;
+ u32 alignment = 0;
+ u32 next_tag_value = 1;
+
+ AstEnumType* tag_enum_node = onyx_ast_node_new(alloc, sizeof(AstEnumType), Ast_Kind_Enum_Type);
+ tag_enum_node->token = union_->token;
+ tag_enum_node->name = bh_aprintf(alloc, "%s.tag_enum", union_->name);
+ tag_enum_node->backing_type = &basic_types[Basic_Kind_U32];
+ bh_arr_new(alloc, tag_enum_node->values, bh_arr_length(union_->variants));
+
+ void add_entities_for_node(bh_arr(Entity *) *target_arr, AstNode* node, Scope* scope, Package* package); // HACK
+ add_entities_for_node(NULL, (AstNode *) tag_enum_node, union_->entity->scope, union_->entity->package);
+
+ //
+ // Create variant instances
+ bh_arr_each(AstUnionVariant *, pvariant, union_->variants) {
+ AstUnionVariant *variant = *pvariant;
+ assert(variant->type);
+
+ u32 var_alignment = type_alignment_of(variant->type);
+ if (var_alignment <= 0) {
+ onyx_report_error(variant->token->pos, Error_Critical, "Invalid variant type '%s', has alignment %d", type_get_name(variant->type), var_alignment);
+ return NULL;
+ }
+
+ if (var_alignment > alignment) alignment = var_alignment;
+
+ token_toggle_end(variant->token);
+ if (shgeti(u_type->Union.variants, variant->token->text) != -1) {
+ onyx_report_error(variant->token->pos, Error_Critical, "Duplicate union variant, '%s'.", variant->token->text);
+ token_toggle_end(variant->token);
+ return NULL;
+ }
+
+ u32 type_size = type_size_of(variant->type);
+ size = bh_max(size, type_size);
+
+ UnionVariant* uv = bh_alloc_item(alloc, UnionVariant);
+ uv->name = bh_strdup(alloc, variant->token->text);
+ uv->token = variant->token;
+ uv->tag_value = next_tag_value++;
+ uv->meta_tags = variant->meta_tags;
+ uv->type = variant->type;
+
+ shput(u_type->Union.variants, variant->token->text, uv);
+ token_toggle_end(variant->token);
+
+ AstEnumValue *ev = onyx_ast_node_new(alloc, sizeof(AstEnumValue), Ast_Kind_Enum_Value);
+ ev->token = uv->token;
+ ev->value = (AstTyped *) make_int_literal(alloc, uv->tag_value);
+ bh_arr_push(tag_enum_node->values, ev);
+ }
+
+ u_type->Union.alignment = alignment;
+ u_type->Union.size = size + alignment; // Add the size of the tag
+ u_type->Union.tag_type = type_build_from_ast(alloc, (AstType *) tag_enum_node);
+
+ return u_type;
+ }
}
return NULL;
return bh_aprintf(global_scratch_allocator, "%s@%l", type->Enum.name, type->id);
else
return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous enum>", type->id);
+ case Type_Kind_Union:
+ if (type->Union.name)
+ return bh_aprintf(global_scratch_allocator, "%s@%l", type->Union.name, type->id);
+ else
+ return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous union>", type->id);
case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_unique_name(type->Slice.elem));
case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_unique_name(type->VarArgs.elem));
else
return "<anonymous enum>";
+ case Type_Kind_Union:
+ if (type->Union.name)
+ return type->Union.name;
+ else
+ return "<anonymous union>";
+
case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_name(type->Slice.elem));
case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_name(type->VarArgs.elem));
case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_name(type->DynArray.elem));
{ 2 * POINTER_SIZE, 2, &basic_types[Basic_Kind_U32], "closure_size", NULL, NULL, -1, 0, 0 },
};
+static const StructMember union_members[] = {
+ // { 0, 0, NULL, "tag", NULL, NULL, -1, 0, 0 },
+};
+
b32 type_lookup_member(Type* type, char* member, StructMember* smem) {
if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
}
}
+ case Type_Kind_Union: {
+ // fori (i, 0, (i64) (sizeof(array_members) / sizeof(StructMember))) {
+ // if (strcmp(array_members[i].name, member) == 0) {
+ // *smem = array_members[i];
+ // if (smem->idx == 0) smem->type = type->Union.tag_enum;
+
+ // return 1;
+ // }
+ // }
+
+ return 0;
+ }
+
default: return 0;
}
}
return 1;
}
+ case Type_Kind_Union: {
+ if (idx > 2) return 0;
+
+ if (idx == 0) {
+ smem->type = type->Union.tag_type;
+ smem->offset = 0;
+ }
+
+ if (idx == 1) {
+ smem->type = NULL;
+ smem->offset = type->Union.alignment;
+ }
+
+ return 1;
+ }
+
default: return 0;
}
}
if (type->kind == Type_Kind_DynArray) return 1;
if (type->kind == Type_Kind_Function) return 1;
if (type->kind == Type_Kind_VarArgs) return 1;
+ if (type->kind == Type_Kind_Union) return 1;
return 0;
}
case Type_Kind_Function: return 3;
case Type_Kind_DynArray: return 4;
case Type_Kind_Distinct: return 1;
+ case Type_Kind_Union: return 2;
default: return 0;
}
}
case Type_Kind_Slice: return 1;
case Type_Kind_VarArgs: return 1;
case Type_Kind_Function: return 1;
- case Type_Kind_DynArray: return 0;
- case Type_Kind_Struct: return 0;
default: return 0;
}
}
case Type_Kind_Slice: return 1;
case Type_Kind_DynArray: return 1;
case Type_Kind_Function: return 1;
+ case Type_Kind_Union: return 1;
default: return 0;
}
}
if (type_struct_is_just_one_basic_value(type)) return 0;
return type->kind == Type_Kind_Struct
- || type->kind == Type_Kind_DynArray;
+ || type->kind == Type_Kind_DynArray
+ || type->kind == Type_Kind_Union;
}
static WasmType onyx_type_to_wasm_type(Type* type) {
--- /dev/null
+Tagged Unions
+-------------
+
+Declaring a union of two types.
+
+ Error :: union {
+ io: struct { ... };
+ network: struct { ... };
+ }
+
+Creating a value of that union.
+Struct literal must have EXACTLY ONE NAMED member.
+
+ err := Error.{ io = .{ ... } };
+
+Accessing data.
+
+ switch err {
+ case .io => io_error {
+ // use io_error
+ }
+
+ case .network => network_error {
+ // use network_error
+ }
+ }
+
+Fields.
+
+ Error.tag_enum // Implicit enum created for every union. would be enum {io, network}
+
+ Error.tag_enum.io // Enum value
+
+ err.io // Same as Error.tag_enum.io
+
+ cast(Error.tag_enum) err // Get the tagged value out of the union
+
use core {*}
-/*
-Optional :: struct (T: type_expr) {
- has_value: bool;
- value: T;
+SimpleUnion :: union {
+ a: i32;
+ b: struct {
+ c, d: str;
+ };
}
-*/
-Optional :: union (T: type_expr) {
- None: void;
- Some: T;
-}
-
-#inject Optional {
- with :: macro (o: Optional($T), code: Code) {
- switch o {
- case .Some => v {
- #unquote code(v);
- }
- }
- }
-
- or_default :: macro (o: Optional($T), default: T) -> T {
- switch o {
- case .Some => v do return v;
- case .None do return default;
- }
- }
-}
-
-// | tag type (Tag_Enum) | data... |
+simple_test :: () {
+ u := SimpleUnion.{ a = 123 };
+ println(sizeof str);
+ println(sizeof str * 2 + sizeof i32);
-Config :: struct {
- name: Optional(str);
-}
-
-main :: () {
- x := Optional(i32).{ Some = 123 };
-
- c: Config;
- c.name = .{ Some = "test" };
-
- printf("{}.{}", typeof c.name.Some, c.name.Some); // "Optional.tag_enum.Some"
-
- x->with(#quote [v] {
- printf("x has the value: {}\n", v);
- });
-
- y := x->or_default(0);
-
- switch x {
- case .None {
-
- }
-
- case .Some => &value {
- printf("x has the value: {}\n", value);
- }
- }
+ __byte_dump(&u, sizeof typeof u);
+ println(u.a);
}
+main :: () {simple_test();}
+
+
+// Link :: union {
+// End: void;
+// Next: struct {
+// data: i32;
+// next: &Link;
+// }
+// }
+//
+// print_links :: (l: Link) {
+// walker := l;
+// while true {
+// switch walker {
+// case .End do break;
+// case .Next {
+// next: struct {data: i32; next: rawptr};
+// printf("{} ", next.data);
+// walker = next.next;
+// }
+// }
+// }
+// }
+//
+// link_test :: () {
+// l := Link.{
+// Next = .{
+// data = 123,
+// next = &Link.{
+// End = .{}
+// }
+// }
+// };
+//
+// print_links(l);
+// }
+//
+// main :: () { link_test(); }
+
+// Optional :: union (T: type_expr) {
+// None: void;
+// Some: T;
+// }
+//
+// #inject Optional {
+// with :: macro (o: Optional($T), code: Code) {
+// switch o {
+// case .Some => v {
+// #unquote code(v);
+// }
+// }
+// }
+//
+// or_default :: macro (o: Optional($T), default: T) -> T {
+// switch o {
+// case .Some => v do return v;
+// case .None do return default;
+// }
+// }
+// }
+// | tag type (Tag_Enum) | data... |
+// Config :: struct {
+// name: Optional(str);
+// }
+//
+// main :: () {
+// x := Optional(i32).{ Some = 123 };
+//
+// c: Config;
+// c.name = .{ Some = "test" };
+//
+// printf("{}.{}", typeof c.name.Some, c.name.Some); // "Optional.tag_enum.Some"
+//
+// x->with(#quote {
+// printf("x has the value: {}\n", it);
+// });
+//
+// y := x->or_default(0);
+//
+// switch x {
+// case .None {
+// printf("x has nothing....\n");
+// }
+//
+// case .Some => &value {
+// printf("x has the value: {}\n", value);
+// }
+// }
+// }
+//
+//
+//
+//