Implicit Values: context; Fix lvalue selector assignments; Fix offset_of* for using fields.

This commit is contained in:
Ginger Bill
2016-10-10 10:27:50 +01:00
parent 90babbfbf3
commit f5318c46d1
15 changed files with 226 additions and 6908 deletions
+38 -39
View File
@@ -24,7 +24,6 @@ String const addressing_mode_strings[] = {
#undef ADDRESSING_MODE
};
struct Operand {
AddressingMode mode;
Type *type;
@@ -32,7 +31,6 @@ struct Operand {
AstNode *expr;
BuiltinProcId builtin_id;
};
b32 is_operand_nil(Operand *o) {
return o->mode == Addressing_Value && o->type == t_untyped_nil;
}
@@ -58,33 +56,6 @@ struct DeclInfo {
};
void init_declaration_info(DeclInfo *d, Scope *scope) {
d->scope = scope;
map_init(&d->deps, gb_heap_allocator());
}
DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) {
DeclInfo *d = gb_alloc_item(a, DeclInfo);
init_declaration_info(d, scope);
return d;
}
void destroy_declaration_info(DeclInfo *d) {
map_destroy(&d->deps);
}
b32 has_init(DeclInfo *d) {
if (d->init_expr != NULL)
return true;
if (d->proc_decl != NULL) {
ast_node(pd, ProcDecl, d->proc_decl);
if (pd->body != NULL)
return true;
}
return false;
}
struct ExpressionInfo {
b32 is_lhs; // Debug info
@@ -94,11 +65,7 @@ struct ExpressionInfo {
};
ExpressionInfo make_expression_info(b32 is_lhs, AddressingMode mode, Type *type, ExactValue value) {
ExpressionInfo ei = {};
ei.is_lhs = is_lhs;
ei.mode = mode;
ei.type = type;
ei.value = value;
ExpressionInfo ei = {is_lhs, mode, type, value};
return ei;
}
@@ -126,6 +93,7 @@ struct Scope {
b32 is_init;
AstFile *file;
};
gb_global Scope *universal_scope = NULL;
enum ExprKind {
Expr_Expr,
@@ -253,12 +221,13 @@ struct Checker {
b32 in_defer; // TODO(bill): Actually handle correctly
};
gb_global Scope *universal_scope = NULL;
struct CycleChecker {
Array<Entity *> path; // Entity_TypeName
};
CycleChecker *cycle_checker_add(CycleChecker *cc, Entity *e) {
if (cc == NULL) {
return NULL;
@@ -278,6 +247,36 @@ void cycle_checker_destroy(CycleChecker *cc) {
}
void init_declaration_info(DeclInfo *d, Scope *scope) {
d->scope = scope;
map_init(&d->deps, gb_heap_allocator());
}
DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) {
DeclInfo *d = gb_alloc_item(a, DeclInfo);
init_declaration_info(d, scope);
return d;
}
void destroy_declaration_info(DeclInfo *d) {
map_destroy(&d->deps);
}
b32 decl_info_has_init(DeclInfo *d) {
if (d->init_expr != NULL)
return true;
if (d->proc_decl != NULL) {
ast_node(pd, ProcDecl, d->proc_decl);
if (pd->body != NULL)
return true;
}
return false;
}
Scope *make_scope(Scope *parent, gbAllocator allocator) {
Scope *s = gb_alloc_item(allocator, Scope);
@@ -489,8 +488,6 @@ void add_global_constant(gbAllocator a, String name, Type *type, ExactValue valu
void init_universal_scope(void) {
// NOTE(bill): No need to free these
gbAllocator a = gb_heap_allocator();
@@ -508,7 +505,7 @@ void init_universal_scope(void) {
add_global_constant(a, make_string("true"), t_untyped_bool, make_exact_value_bool(true));
add_global_constant(a, make_string("false"), t_untyped_bool, make_exact_value_bool(false));
add_global_entity(make_entity_nil(a, NULL, make_token_ident(make_string("nil")), t_untyped_nil));
add_global_entity(make_entity_nil(a, make_string("nil"), t_untyped_nil));
// Builtin Procedures
for (isize i = 0; i < gb_count_of(builtin_procs); i++) {
@@ -947,6 +944,8 @@ void init_preload_types(Checker *c) {
}
t_context = e->type;
t_context_ptr = make_type_pointer(c->allocator, t_context);
add_global_entity(make_entity_implicit_value(gb_heap_allocator(), make_string("context"), t_context));
}
}
+25 -22
View File
@@ -1,5 +1,6 @@
struct Scope;
struct Checker;
struct Type;
enum BuiltinProcId;
#define ENTITY_KINDS \
@@ -11,6 +12,7 @@ enum BuiltinProcId;
ENTITY_KIND(Builtin), \
ENTITY_KIND(ImportName), \
ENTITY_KIND(Nil), \
ENTITY_KIND(ImplicitValue), \
ENTITY_KIND(Count),
@@ -26,33 +28,27 @@ String const entity_strings[] = {
#undef ENTITY_KIND
};
typedef struct Type Type;
struct Entity {
EntityKind kind;
Scope * scope;
Token token;
Type * type;
AstNode * identifier; // Can be NULL
Entity * using_parent;
AstNode * using_expr;
union {
struct {
ExactValue value;
} Constant;
struct {
b8 visited; // Cycle detection
b8 used; // Variable is used
b8 anonymous; // Variable is an anonymous
b8 is_using; // `using` variable
b8 visited; // Cycle detection
b8 used; // Variable is used
b8 anonymous; // Variable is an anonymous
b8 field; // Is Record field
b8 param; // Is procedure parameter
i32 field_index;
i32 field_src_index;
b8 is_field; // Is struct field
} Variable;
struct {
b32 used;
@@ -69,8 +65,8 @@ struct Entity {
Scope *scope;
b32 used;
} ImportName;
struct {
} Nil;
struct {} Nil;
struct {} ImplicitValue;
};
};
@@ -105,7 +101,7 @@ Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, T
GB_ASSERT(parent != NULL);
Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type);
entity->using_parent = parent;
entity->Variable.is_using = true;
entity->Variable.anonymous = true;
return entity;
}
@@ -121,20 +117,20 @@ Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *ty
return entity;
}
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, b32 is_anonymous) {
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, b32 anonymous) {
Entity *entity = make_entity_variable(a, scope, token, type);
entity->Variable.used = true;
entity->Variable.anonymous = cast(b8)is_anonymous;
entity->Variable.anonymous = cast(b8)anonymous;
entity->Variable.param = true;
return entity;
}
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, b32 is_anonymous, i32 field_src_index) {
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, b32 anonymous, i32 field_src_index) {
Entity *entity = make_entity_variable(a, scope, token, type);
entity->Variable.field_src_index = field_src_index;
entity->Variable.field_index = field_src_index;
entity->Variable.is_field = true;
entity->Variable.anonymous = cast(b8)is_anonymous;
entity->Variable.is_using = cast(b8)is_anonymous;
entity->Variable.field = true;
entity->Variable.anonymous = cast(b8)anonymous;
return entity;
}
@@ -158,8 +154,15 @@ Entity *make_entity_import_name(gbAllocator a, Scope *scope, Token token, Type *
return entity;
}
Entity *make_entity_nil(gbAllocator a, Scope *scope, Token token, Type *type) {
Entity *entity = alloc_entity(a, Entity_Nil, scope, token, type);
Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
Token token = make_token_ident(name);
Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type);
return entity;
}
Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type) {
Token token = make_token_ident(name);
Entity *entity = alloc_entity(a, Entity_ImplicitValue, NULL, token, type);
return entity;
}
+53 -19
View File
@@ -973,6 +973,10 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
o->mode = Addressing_Value;
break;
case Entity_ImplicitValue:
o->mode = Addressing_Value;
break;
default:
compiler_error("Compiler error: Unknown EntityKind");
break;
@@ -1708,7 +1712,7 @@ String check_down_cast_name(Type *dst_, Type *src_) {
GB_ASSERT(is_type_struct(dst_s) || is_type_raw_union(dst_s));
for (isize i = 0; i < dst_s->Record.field_count; i++) {
Entity *f = dst_s->Record.fields[i];
GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
GB_ASSERT(f->kind == Entity_Variable && f->Variable.field);
if (f->Variable.anonymous) {
if (are_types_identical(f->type, src_)) {
return f->token.string;
@@ -2216,6 +2220,8 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
b32 check_op_expr = true;
Entity *entity = NULL;
Selection sel = {}; // NOTE(bill): Not used if it's an import name
AstNode *op_expr = se->expr;
AstNode *selector = unparen_expr(se->selector);
@@ -2267,8 +2273,10 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
}
}
if (entity == NULL) {
entity = lookup_field(c->allocator, operand->type, selector->Ident.string, operand->mode == Addressing_Type).entity;
sel = lookup_field(c->allocator, operand->type, selector->Ident.string, operand->mode == Addressing_Type);
entity = sel.entity;
}
if (entity == NULL) {
gbString op_str = expr_to_string(op_expr);
@@ -2284,15 +2292,16 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
add_entity_use(c, selector, entity);
operand->type = entity->type;
operand->expr = node;
switch (entity->kind) {
case Entity_Constant:
operand->mode = Addressing_Constant;
operand->value = entity->Constant.value;
break;
case Entity_Variable:
operand->mode = Addressing_Variable;
// TODO(bill): This is the rule I need?
if (sel.indirect || operand->mode != Addressing_Value) {
operand->mode = Addressing_Variable;
}
break;
case Entity_TypeName:
operand->mode = Addressing_Type;
@@ -2304,8 +2313,19 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
operand->mode = Addressing_Builtin;
operand->builtin_id = entity->Builtin.id;
break;
// NOTE(bill): These cases should never be hit but are here for sanity reasons
case Entity_Nil:
operand->mode = Addressing_Value;
break;
case Entity_ImplicitValue:
operand->mode = Addressing_Value;
break;
}
operand->type = entity->type;
operand->expr = node;
return entity;
error:
@@ -2473,10 +2493,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
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 struct type for `offset_of`");
return false;
}
AstNode *field_arg = unparen_expr(ce->args[1]);
if (field_arg == NULL ||
@@ -2484,6 +2500,10 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
error(ast_node_token(field_arg), "Expected an identifier for field argument");
return false;
}
if (is_type_array(type) || is_type_vector(type)) {
error(ast_node_token(field_arg), "Invalid type for `offset_of`");
return false;
}
ast_node(arg, Ident, field_arg);
@@ -2495,10 +2515,16 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
"`%s` has no field named `%.*s`", type_str, LIT(arg->string));
return false;
}
if (sel.indirect) {
gbString type_str = type_to_string(bt);
defer (gb_string_free(type_str));
error(ast_node_token(ce->args[0]),
"Field `%.*s` is embedded via a pointer in `%s`", LIT(arg->string), type_str);
return false;
}
operand->mode = Addressing_Constant;
// IMPORTANT TODO(bill): Fix for anonymous fields
operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, sel.index[0]));
operand->value = make_exact_value_integer(type_offset_of_from_selection(c->sizes, c->allocator, type, sel));
operand->type = t_int;
} break;
@@ -2523,13 +2549,11 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
type = p->Pointer.elem;
}
}
if (!is_type_struct(type)) {
error(ast_node_token(ce->args[0]), "Expected a struct type for `offset_of_val`");
if (is_type_array(type) || is_type_vector(type)) {
error(ast_node_token(arg), "Invalid 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);
if (sel.entity == NULL) {
@@ -2538,10 +2562,18 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
"`%s` has no field named `%.*s`", type_str, LIT(i->string));
return false;
}
if (sel.indirect) {
gbString type_str = type_to_string(type);
defer (gb_string_free(type_str));
error(ast_node_token(ce->args[0]),
"Field `%.*s` is embedded via a pointer in `%s`", LIT(i->string), type_str);
return false;
}
operand->mode = Addressing_Constant;
// IMPORTANT TODO(bill): Fix for anonymous fields
operand->value = make_exact_value_integer(type_offset_of(c->sizes, c->allocator, type, sel.index[0]));
operand->value = make_exact_value_integer(type_offset_of_from_selection(c->sizes, c->allocator, type, sel));
operand->type = t_int;
} break;
@@ -3227,7 +3259,7 @@ Entity *find_using_index_expr(Type *t) {
for (isize i = 0; i < t->Record.field_count; i++) {
Entity *f = t->Record.fields[i];
if (f->kind == Entity_Variable &&
f->Variable.is_field && f->Variable.anonymous) {
f->Variable.field && f->Variable.anonymous) {
if (is_type_indexable(f->type)) {
return f;
}
@@ -3632,8 +3664,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
if (o->mode == Addressing_Constant) {
*max_count = o->value.value_string.len;
}
if (o->mode != Addressing_Variable)
if (o->mode != Addressing_Variable) {
o->mode = Addressing_Value;
}
o->type = t_u8;
return true;
}
@@ -3700,8 +3733,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
PROF_SCOPED("check__expr_base - SliceExpr");
check_expr(c, o, se->expr);
if (o->mode == Addressing_Invalid)
if (o->mode == Addressing_Invalid) {
goto error;
}
b32 valid = false;
i64 max_count = -1;
+13 -5
View File
@@ -1,5 +1,4 @@
// Statements and Declarations
enum StmtFlag : u32 {
Stmt_BreakAllowed = GB_BIT(0),
Stmt_ContinueAllowed = GB_BIT(1),
@@ -268,10 +267,10 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
}
switch (op_b.mode) {
case Addressing_Variable:
break;
case Addressing_Invalid:
return NULL;
case Addressing_Variable:
break;
default: {
if (op_b.expr->kind == AstNode_SelectorExpr) {
// NOTE(bill): Extra error checks
@@ -282,7 +281,15 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
gbString str = expr_to_string(op_b.expr);
defer (gb_string_free(str));
error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
switch (op_b.mode) {
case Addressing_Value:
error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
break;
default:
error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
break;
}
} break;
}
@@ -507,8 +514,9 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
for (isize i = 0; i < params->variable_count; i++) {
Entity *e = params->variables[i];
GB_ASSERT(e->kind == Entity_Variable);
if (!e->Variable.anonymous)
if (!e->Variable.anonymous) {
continue;
}
String name = e->token.string;
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
+30 -7
View File
@@ -812,6 +812,8 @@ Selection lookup_field(gbAllocator a, Type *type_, String field_name, b32 is_typ
Type *type = type_deref(type_);
b32 is_ptr = type != type_;
sel.indirect = sel.indirect || is_ptr;
type = base_type(type);
if (type->kind == Type_Basic) {
@@ -918,7 +920,6 @@ Selection lookup_field(gbAllocator a, Type *type_, String field_name, b32 is_typ
String str = f->token.string;
if (field_name == str) {
Selection sel = {};
sel.entity = f;
selection_add_index(&sel, i);
return sel;
@@ -932,7 +933,6 @@ Selection lookup_field(gbAllocator a, Type *type_, String field_name, b32 is_typ
String str = f->token.string;
if (field_name == str) {
Selection sel = {};
sel.entity = f;
selection_add_index(&sel, i);
return sel;
@@ -955,7 +955,7 @@ Selection lookup_field(gbAllocator a, Type *type_, String field_name, b32 is_typ
} else if (!is_type_enum(type) && !is_type_union(type)) {
for (isize i = 0; i < type->Record.field_count; i++) {
Entity *f = type->Record.fields[i];
GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field);
GB_ASSERT(f->kind == Entity_Variable && f->Variable.field);
String str = f->token.string;
if (field_name == str) {
selection_add_index(&sel, i); // HACK(bill): Leaky memory
@@ -970,8 +970,9 @@ Selection lookup_field(gbAllocator a, Type *type_, String field_name, b32 is_typ
sel = lookup_field(a, f->type, field_name, is_type, sel);
if (sel.entity != NULL) {
if (is_type_pointer(f->type))
if (is_type_pointer(f->type)) {
sel.indirect = true;
}
return sel;
}
sel.index.count = prev_count;
@@ -1199,26 +1200,48 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
}
i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) {
t = base_type(t);
if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) {
type_set_offsets(s, allocator, t);
if (gb_is_between(index, 0, t->Record.field_count-1)) {
return t->Record.struct_offsets[index];
}
} else if (t->kind == Type_Basic) {
gb_printf_err("here!!\n");
if (t->Basic.kind == Basic_string) {
switch (index) {
case 0: return 0;
case 1: return s.word_size;
}
} else if (t->Basic.kind == Basic_any) {
switch (index) {
case 0: return 0;
case 1: return s.word_size;
}
}
} else if (t->kind == Type_Slice) {
switch (index) {
case 0: return 0;
case 1: return 1*s.word_size;
case 2: return 2*s.word_size;
}
}
return 0;
}
i64 type_offset_of_from_selection(BaseTypeSizes s, gbAllocator allocator, Type *t, Selection sel) {
GB_ASSERT(sel.indirect == false);
i64 offset = 0;
for_array(i, sel.index) {
isize index = sel.index[i];
t = base_type(t);
offset += type_offset_of(s, allocator, t, index);
if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) {
type_set_offsets(s, allocator, t);
GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
offset += t->Record.struct_offsets[index];
t = t->Record.fields[index]->type;
} else {
// NOTE(bill): string/any/slices don't have record fields so this case doesn't need to be handled
}
}
return offset;