Comma for all field separators; Overloaded procedures follow exportation rules

This commit is contained in:
Ginger Bill
2017-01-17 18:47:38 +00:00
parent 383f5b55ad
commit cb59c1cf08
14 changed files with 439 additions and 285 deletions
+88 -28
View File
@@ -5,7 +5,7 @@ ExprKind check_expr_base (Checker *c, Operand *operand, AstNode *
Type * check_type_extra (Checker *c, AstNode *expression, Type *named_type);
Type * check_type (Checker *c, AstNode *expression);
void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, Type *def);
Entity * check_selector (Checker *c, Operand *operand, AstNode *node);
Entity * check_selector (Checker *c, Operand *operand, AstNode *node, Type *type_hint);
void check_not_tuple (Checker *c, Operand *operand);
void convert_to_typed (Checker *c, Operand *operand, Type *target_type, i32 level);
gbString expr_to_string (AstNode *expression);
@@ -757,6 +757,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_v
p->flags &= ~FieldFlag_no_alias; // Remove the flag
}
}
for_array(j, p->names) {
AstNode *name = p->names.e[j];
if (ast_node_expect(name, AstNode_Ident)) {
@@ -779,6 +780,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_v
// Custom Calling convention for variadic parameters
Entity *end = variables[variable_count-1];
end->type = make_type_slice(c->allocator, end->type);
end->flags |= EntityFlag_Ellipsis;
}
Type *tuple = make_type_tuple(c->allocator);
@@ -874,10 +876,10 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Type
Scope *s = e->scope;
bool skip = false;
Entity **procs = gb_alloc_array(heap_allocator(), Entity *, overload_count);
map_entity_multi_get_all(&s->elements, key, procs);
if (type_hint != NULL) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
Entity **procs = gb_alloc_array(c->tmp_allocator, Entity *, overload_count);
map_entity_multi_get_all(&s->elements, key, procs);
// NOTE(bill): These should be done
for (isize i = 0; i < overload_count; i++) {
Type *t = base_type(procs[i]->type);
@@ -899,12 +901,13 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Type
}
if (!skip) {
o->mode = Addressing_Overload;
o->type = t_invalid;
o->overload_count = overload_count;
o->initial_overload_entity = e;
o->mode = Addressing_Overload;
o->type = t_invalid;
o->overload_count = overload_count;
o->overload_entities = procs;
return;
}
gb_free(heap_allocator(), procs);
}
add_entity_use(c, n, e);
@@ -1040,7 +1043,7 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) {
case_ast_node(se, SelectorExpr, e);
Operand o = {0};
check_selector(c, &o, e);
check_selector(c, &o, e, NULL);
switch (o.mode) {
case Addressing_Invalid:
@@ -2461,7 +2464,7 @@ bool check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *val
return true;
}
Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_hint) {
ast_node(se, SelectorExpr, node);
bool check_op_expr = true;
@@ -2480,31 +2483,88 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
}
if (op_expr->kind == AstNode_Ident) {
b32 is_not_exported = true;
String name = op_expr->Ident.string;
Entity *e = scope_lookup_entity(c->context.scope, name);
add_entity_use(c, op_expr, e);
expr_entity = e;
if (e != NULL && e->kind == Entity_ImportName &&
selector->kind == AstNode_Ident) {
String sel_name = selector->Ident.string;
check_op_expr = false;
entity = scope_lookup_entity(e->ImportName.scope, sel_name);
if (entity == NULL) {
error_node(op_expr, "`%.*s` is not declared by `%.*s`", LIT(sel_name), LIT(name));
goto error;
}
if (entity->type == NULL) { // Not setup yet
check_entity_decl(c, entity, NULL, NULL);
}
check_entity_decl(c, entity, NULL, NULL);
GB_ASSERT(entity->type != NULL);
b32 is_not_exported = true;
Entity **found = map_entity_get(&e->ImportName.scope->implicit, hash_string(sel_name));
if (found == NULL) {
bool is_overloaded = false;
isize overload_count = 0;
HashKey key = {0};
if (entity->kind == Entity_Procedure) {
key = hash_string(entity->token.string);
// NOTE(bill): Overloads are only allowed with the same scope
Scope *s = entity->scope;
overload_count = map_entity_multi_count(&s->elements, key);
if (overload_count > 1) {
is_overloaded = true;
}
}
if (is_overloaded) {
Scope *s = entity->scope;
bool skip = false;
Entity **procs = gb_alloc_array(heap_allocator(), Entity *, overload_count);
map_entity_multi_get_all(&s->elements, key, procs);
for (isize i = 0; i < overload_count; /**/) {
Type *t = base_type(procs[i]->type);
if (t == t_invalid) {
continue;
}
// NOTE(bill): Check to see if it's imported
if (map_bool_get(&e->ImportName.scope->implicit, hash_pointer(procs[i]))) {
gb_swap(Entity *, procs[i], procs[overload_count-1]);
overload_count--;
continue;
}
Operand x = {0};
x.mode = Addressing_Value;
x.type = t;
if (type_hint != NULL) {
if (check_is_assignable_to(c, &x, type_hint)) {
entity = procs[i];
skip = true;
break;
}
}
i++;
}
if (overload_count > 0 && !skip) {
operand->mode = Addressing_Overload;
operand->type = t_invalid;
operand->expr = node;
operand->overload_count = overload_count;
operand->overload_entities = procs;
return procs[0];
}
}
bool *found = map_bool_get(&e->ImportName.scope->implicit, hash_pointer(entity));
if (!found) {
is_not_exported = false;
} else {
Entity *f = *found;
if (f->kind == Entity_ImportName) {
if (entity->kind == Entity_ImportName) {
is_not_exported = true;
}
}
@@ -2515,8 +2575,6 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
gb_string_free(sel_str);
// NOTE(bill): Not really an error so don't goto error
}
add_entity_use(c, selector, entity);
}
}
if (check_op_expr) {
@@ -3606,16 +3664,15 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
if (operand->mode == Addressing_Overload) {
Scope *s = operand->initial_overload_entity->scope;
String name = operand->initial_overload_entity->token.string;
HashKey key = hash_string(name);
GB_ASSERT(operand->overload_entities != NULL &&
operand->overload_count > 0);
isize overload_count = operand->overload_count;
Entity ** procs = gb_alloc_array(heap_allocator(), Entity *, overload_count);
Entity ** procs = operand->overload_entities;
ValidProcAndScore *valids = gb_alloc_array(heap_allocator(), ValidProcAndScore, overload_count);
isize valid_count = 0;
map_entity_multi_get_all(&s->elements, key, procs);
String name = procs[0]->token.string;
for (isize i = 0; i < overload_count; i++) {
Entity *e = procs[i];
@@ -3666,9 +3723,13 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
}
proc_type = t_invalid;
} else {
GB_ASSERT(operand->expr->kind == AstNode_Ident);
AstNode *expr = operand->expr;
while (expr->kind == AstNode_SelectorExpr) {
expr = expr->SelectorExpr.selector;
}
GB_ASSERT(expr->kind == AstNode_Ident);
Entity *e = procs[valids[0].index];
add_entity_use(c, operand->expr, e);
add_entity_use(c, expr, e);
proc_type = e->type;
i64 score = 0;
CallArgumentError err = check_call_arguments_internal(c, call, proc_type, operands.e, operands.count, true, &score);
@@ -4473,7 +4534,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
case_ast_node(se, SelectorExpr, node);
check_selector(c, o, node);
check_selector(c, o, node, type_hint);
case_end;
@@ -4763,7 +4824,6 @@ gbString write_params_to_string(gbString str, AstNodeArray params, char *sep) {
if (i > 0) {
str = gb_string_appendc(str, sep);
}
str = write_expr_to_string(str, params.e[i]);
}
return str;
+5 -11
View File
@@ -215,14 +215,9 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
if (op_a->mode == Addressing_Overload) {
isize overload_count = op_a->overload_count;
Entity *entity = op_a->initial_overload_entity;
String name = entity->token.string;
Scope *s = entity->scope;
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
Entity **procs = gb_alloc_array(c->tmp_allocator, Entity *, overload_count);
Entity **procs = op_a->overload_entities;
GB_ASSERT(procs != NULL && overload_count > 0);
HashKey key = hash_string(name);
map_entity_multi_get_all(&s->elements, key, procs);
// NOTE(bill): These should be done
for (isize i = 0; i < overload_count; i++) {
Type *t = base_type(procs[i]->type);
@@ -238,15 +233,14 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
break;
}
}
gb_temp_arena_memory_end(tmp);
if (e != NULL) {
// HACK TODO(bill): Should the entities be freed as it's technically a leak
op_a->mode = Addressing_Value;
op_a->type = e->type;
op_a->overload_count = 0;
op_a->initial_overload_entity = NULL;
op_a->overload_entities = NULL;
}
} else {
if (node->kind == AstNode_Ident) {
ast_node(i, Ident, node);
@@ -1085,7 +1079,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
e = scope_lookup_entity(c->context.scope, name);
} else if (expr->kind == AstNode_SelectorExpr) {
Operand o = {0};
e = check_selector(c, &o, expr);
e = check_selector(c, &o, expr, NULL);
is_selector = true;
}
+6 -6
View File
@@ -28,7 +28,7 @@ typedef struct Operand {
AstNode * expr;
BuiltinProcId builtin_id;
isize overload_count;
Entity * initial_overload_entity;
Entity ** overload_entities;
} Operand;
typedef struct TypeAndValue {
@@ -93,7 +93,7 @@ typedef struct Scope {
Scope * first_child;
Scope * last_child;
MapEntity elements; // Key: String
MapEntity implicit; // Key: String
MapBool implicit; // Key: Entity *
Array(Scope *) shared;
Array(Scope *) imported;
@@ -344,7 +344,7 @@ Scope *make_scope(Scope *parent, gbAllocator allocator) {
Scope *s = gb_alloc_item(allocator, Scope);
s->parent = parent;
map_entity_init(&s->elements, heap_allocator());
map_entity_init(&s->implicit, heap_allocator());
map_bool_init(&s->implicit, heap_allocator());
array_init(&s->shared, heap_allocator());
array_init(&s->imported, heap_allocator());
@@ -371,7 +371,7 @@ void destroy_scope(Scope *scope) {
}
map_entity_destroy(&scope->elements);
map_entity_destroy(&scope->implicit);
map_bool_destroy(&scope->implicit);
array_free(&scope->shared);
array_free(&scope->imported);
@@ -540,6 +540,7 @@ Entity *scope_insert_entity(Scope *s, Entity *entity) {
return NULL;
}
void check_scope_usage(Checker *c, Scope *scope) {
// TODO(bill): Use this?
}
@@ -1601,8 +1602,7 @@ void check_import_entities(Checker *c, MapScope *file_scopes) {
// NOTE(bill): Do not add other imported entities
bool ok = add_entity(c, parent_scope, NULL, e);
if (ok && id->is_import) { // `#import`ed entities don't get exported
HashKey key = hash_string(e->token.string);
map_entity_set(&parent_scope->implicit, key, e);
map_bool_set(&parent_scope->implicit, hash_pointer(e), true);
}
}
} else {
+3 -2
View File
@@ -34,8 +34,9 @@ typedef enum EntityFlag {
EntityFlag_Anonymous = 1<<2,
EntityFlag_Field = 1<<3,
EntityFlag_Param = 1<<4,
EntityFlag_VectorElem = 1<<5,
EntityFlag_NoAlias = 1<<6,
EntityFlag_Ellipsis = 1<<5,
EntityFlag_VectorElem = 1<<6,
EntityFlag_NoAlias = 1<<7,
} EntityFlag;
typedef enum OverloadKind {
+167 -77
View File
@@ -432,7 +432,10 @@ Token ast_node_token(AstNode *node) {
case AstNode_CallExpr:
return ast_node_token(node->CallExpr.proc);
case AstNode_SelectorExpr:
return ast_node_token(node->SelectorExpr.selector);
if (node->SelectorExpr.selector != NULL) {
return ast_node_token(node->SelectorExpr.selector);
}
return node->SelectorExpr.token;
case AstNode_IndexExpr:
return node->IndexExpr.open;
case AstNode_SliceExpr:
@@ -1851,10 +1854,14 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
case Token_Ident:
operand = make_selector_expr(f, token, operand, parse_identifier(f));
break;
case Token_Integer:
operand = make_selector_expr(f, token, operand, parse_expr(f, lhs));
break;
default: {
syntax_error(f->curr_token, "Expected a selector");
next_token(f);
operand = make_selector_expr(f, f->curr_token, operand, NULL);
operand = make_bad_expr(f, ast_node_token(operand), f->curr_token);
// operand = make_selector_expr(f, f->curr_token, operand, NULL);
} break;
}
} break;
@@ -2046,7 +2053,7 @@ AstNodeArray parse_rhs_expr_list(AstFile *f) {
return parse_expr_list(f, false);
}
AstNodeArray parse_identifier_list(AstFile *f) {
AstNodeArray parse_ident_list(AstFile *f) {
AstNodeArray list = make_ast_node_array(f);
do {
@@ -2227,106 +2234,189 @@ AstNode *parse_proc_type(AstFile *f, String *foreign_name_, String *link_name_)
return make_proc_type(f, proc_token, params, results, tags, cc);
}
void parse_field_prefixes(AstFile *f, u32 flags, i32 *using_count, i32 *no_alias_count) {
while (f->curr_token.kind == Token_using ||
f->curr_token.kind == Token_no_alias) {
if (allow_token(f, Token_using)) {
*using_count += 1;
}
if (allow_token(f, Token_no_alias)) {
*no_alias_count += 1;
}
}
if (*using_count > 1) {
syntax_error(f->curr_token, "Multiple `using` in this field list");
*using_count = 1;
}
if (*no_alias_count > 1) {
syntax_error(f->curr_token, "Multiple `no_alias` in this field list");
*no_alias_count = 1;
}
}
bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) {
if (separator == Token_Semicolon) {
expect_semicolon(f, param);
} else {
if (!allow_token(f, separator)) {
return true;
}
}
return false;
}
AstNodeArray convert_to_ident_list(AstFile *f, AstNodeArray list) {
AstNodeArray idents = {0};
array_init_reserve(&idents, heap_allocator(), list.count);
// Convert to ident list
for_array(i, list) {
AstNode *ident = list.e[i];
switch (ident->kind) {
case AstNode_Ident:
case AstNode_BadExpr:
break;
default:
error_node(ident, "Expected an identifier");
ident = make_ident(f, blank_token);
break;
}
array_add(&idents, ident);
}
return idents;
}
AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) {
Token tok = f->curr_token;
next_token(f);
AstNode *type = parse_identifier_or_type(f);
if (type == NULL) {
error(tok, "variadic field missing type after `...`");
type = make_bad_expr(f, tok, f->curr_token);
}
return make_ellipsis(f, tok, type);
}
AstNode *type = parse_type_attempt(f);
if (type == NULL) {
Token tok = f->curr_token;
error(tok, "Expected a type");
type = make_bad_expr(f, tok, f->curr_token);
}
return type;
}
void check_field_prefixes(AstFile *f, AstNodeArray names, u32 flags, i32 *using_count, i32 *no_alias_count) {
if (names.count > 1 && *using_count > 0) {
syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type");
*using_count = 0;
}
if ((flags&FieldFlag_using) == 0 && *using_count > 0) {
syntax_error(f->curr_token, "`using` is not allowed within this field list");
*using_count = 0;
}
if ((flags&FieldFlag_no_alias) == 0 && *no_alias_count > 0) {
syntax_error(f->curr_token, "`no_alias` is not allowed within this field list");
*no_alias_count = 0;
}
}
u32 field_prefixes_to_flags(i32 using_count, i32 no_alias_count) {
u32 field_flags = 0;
if (using_count > 0) field_flags |= FieldFlag_using;
if (no_alias_count > 0) field_flags |= FieldFlag_no_alias;
return field_flags;
}
AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags,
TokenKind separator, TokenKind follow) {
AstNodeArray params = make_ast_node_array(f);
isize name_count = 0;
AstNodeArray list = make_ast_node_array(f);
isize name_count = 0;
bool allow_ellipsis = flags&FieldFlag_ellipsis;
// TODO(bill): Allow for just a list of types
i32 using_count = 0;
i32 no_alias_count = 0;
parse_field_prefixes(f, flags, &using_count, &no_alias_count);
while (f->curr_token.kind != follow &&
f->curr_token.kind != Token_Colon &&
f->curr_token.kind != Token_EOF) {
i32 using_count = 0;
i32 no_alias_count = 0;
while (f->curr_token.kind == Token_using ||
f->curr_token.kind == Token_no_alias) {
if (allow_token(f, Token_using)) {
using_count++;
}
if (allow_token(f, Token_no_alias)) {
no_alias_count++;
}
}
AstNodeArray names = parse_identifier_list(f);
if (names.count == 0) {
syntax_error(f->curr_token, "Empty field declaration");
AstNode *param = parse_var_type(f, allow_ellipsis);
array_add(&list, param);
if (f->curr_token.kind != Token_Comma) {
break;
}
next_token(f);
}
if (names.count > 1 && using_count > 0) {
syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type");
using_count = 0;
if (f->curr_token.kind == Token_Colon) {
AstNodeArray names = convert_to_ident_list(f, list); // Copy for semantic reasons
if (names.count == 0) {
syntax_error(f->curr_token, "Empty field declaration");
}
if ((flags&FieldFlag_using) == 0 && using_count > 0) {
syntax_error(f->curr_token, "`using` is not allowed within this field list");
using_count = 0;
}
if ((flags&FieldFlag_no_alias) == 0 && no_alias_count > 0) {
syntax_error(f->curr_token, "`no_alias` is not allowed within this field list");
no_alias_count = 0;
}
if (using_count > 1) {
syntax_error(f->curr_token, "Multiple `using` in this field list");
using_count = 1;
}
if (no_alias_count > 1) {
syntax_error(f->curr_token, "Multiple `no_alias` in this field list");
no_alias_count = 1;
}
check_field_prefixes(f, names, flags, &using_count, &no_alias_count);
name_count += names.count;
expect_token_after(f, Token_Colon, "field list");
AstNode *type = NULL;
if ((flags&FieldFlag_ellipsis) != 0 && f->curr_token.kind == Token_Ellipsis) {
Token ellipsis = f->curr_token;
next_token(f);
type = parse_type_attempt(f);
if (type == NULL) {
syntax_error(f->curr_token, "variadic field is missing a type after `..`");
type = make_bad_expr(f, ellipsis, f->curr_token);
} else {
if (names.count > 1) {
syntax_error(f->curr_token, "mutliple variadic fields, only `..`");
} else {
type = make_ellipsis(f, ellipsis, type);
}
}
} else {
type = parse_type_attempt(f);
}
if (type == NULL) {
syntax_error(f->curr_token, "Expected a type for this field declaration");
}
u32 flags = 0;
if (using_count > 0) flags |= FieldFlag_using;
if (no_alias_count > 0) flags |= FieldFlag_no_alias;
AstNode *param = make_field(f, names, type, flags);
AstNode *type = parse_var_type(f, allow_ellipsis);
AstNode *param = make_field(f, names, type, field_prefixes_to_flags(using_count, no_alias_count));
array_add(&params, param);
if (separator == Token_Semicolon) {
expect_semicolon(f, param);
} else {
if (!allow_token(f, separator)) {
parse_expect_separator(f, separator, type);
while (f->curr_token.kind != follow &&
f->curr_token.kind != Token_EOF) {
i32 using_count = 0;
i32 no_alias_count = 0;
parse_field_prefixes(f, flags, &using_count, &no_alias_count);
AstNodeArray names = parse_ident_list(f);
if (names.count == 0) {
syntax_error(f->curr_token, "Empty field declaration");
break;
}
check_field_prefixes(f, names, flags, &using_count, &no_alias_count);
name_count += names.count;
expect_token_after(f, Token_Colon, "field list");
AstNode *type = parse_var_type(f, allow_ellipsis);
AstNode *param = make_field(f, names, type, field_prefixes_to_flags(using_count, no_alias_count));
array_add(&params, param);
if (parse_expect_separator(f, separator, param)) {
break;
}
}
if (name_count_) *name_count_ = name_count;
return params;
}
check_field_prefixes(f, list, flags, &using_count, &no_alias_count);
for_array(i, list) {
AstNodeArray names = {0};
AstNode *type = list.e[i];
Token token = blank_token;
array_init_count(&names, heap_allocator(), 1);
token.pos = ast_node_token(type).pos;
names.e[0] = make_ident(f, token);
AstNode *param = make_field(f, names, list.e[i], field_prefixes_to_flags(using_count, no_alias_count));
array_add(&params, param);
}
if (name_count_) *name_count_ = name_count;
return params;
}
AstNodeArray parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) {
return parse_field_list(f, field_count_, flags, Token_Semicolon, Token_CloseBrace);
return parse_field_list(f, field_count_, flags, Token_Comma, Token_CloseBrace);
}
AstNode *parse_identifier_or_type(AstFile *f) {
@@ -2753,7 +2843,7 @@ AstNode *parse_for_stmt(AstFile *f) {
}
Token token = expect_token(f, Token_for);
AstNodeArray names = parse_identifier_list(f);
AstNodeArray names = parse_ident_list(f);
parse_check_name_list_for_reserves(f, names);
Token colon = expect_token_after(f, Token_Colon, "for name list");
+3 -3
View File
@@ -529,7 +529,7 @@ fraction:
// HACK(bill): This may be inefficient
TokenizerState state = save_tokenizer_state(t);
advance_to_next_rune(t);
if (t->curr_rune == '.') {
if (digit_value(t->curr_rune) >= 10) {
// TODO(bill): Clean up this shit
restore_tokenizer_state(t, &state);
goto end;
@@ -837,9 +837,9 @@ Token tokenizer_get_token(Tokenizer *t) {
case '.':
token.kind = Token_Period; // Default
if (gb_is_between(t->curr_rune, '0', '9')) { // Might be a number
/* if (gb_is_between(t->curr_rune, '0', '9')) { // Might be a number
token = scan_number_to_token(t, true);
} else if (t->curr_rune == '.') { // Could be an ellipsis
} else */ if (t->curr_rune == '.') { // Could be an ellipsis
advance_to_next_rune(t);
if (t->curr_rune == '.') {
advance_to_next_rune(t);
+10 -2
View File
@@ -1759,9 +1759,17 @@ gbString write_type_to_string(gbString str, Type *type) {
Entity *var = type->Tuple.variables[i];
if (var != NULL) {
GB_ASSERT(var->kind == Entity_Variable);
if (i > 0)
if (i > 0) {
str = gb_string_appendc(str, ", ");
str = write_type_to_string(str, var->type);
}
if (var->flags&EntityFlag_Ellipsis) {
Type *slice = base_type(var->type);
str = gb_string_appendc(str, "...");
GB_ASSERT(is_type_slice(var->type));
str = write_type_to_string(str, slice->Slice.elem);
} else {
str = write_type_to_string(str, var->type);
}
}
}
}