aboutsummaryrefslogtreecommitdiff
path: root/src/checker
diff options
context:
space:
mode:
authorGinger Bill <bill@gingerbill.org>2016-12-30 15:45:10 +0000
committerGinger Bill <bill@gingerbill.org>2016-12-30 15:45:10 +0000
commit23d32f34e526cfb657a72e5b2dab86d1df765f0f (patch)
tree7998bcf40ca9f581be6296bf4f68c102a5d234c8 /src/checker
parentd714bece47ea058e482389452cd428dad9c28fd0 (diff)
Block Expressions and `give`
Diffstat (limited to 'src/checker')
-rw-r--r--src/checker/checker.c22
-rw-r--r--src/checker/decl.c14
-rw-r--r--src/checker/expr.c138
-rw-r--r--src/checker/stmt.c24
-rw-r--r--src/checker/types.c59
5 files changed, 241 insertions, 16 deletions
diff --git a/src/checker/checker.c b/src/checker/checker.c
index 2159eb95b..c61884253 100644
--- a/src/checker/checker.c
+++ b/src/checker/checker.c
@@ -32,6 +32,16 @@ typedef struct TypeAndValue {
ExactValue value;
} TypeAndValue;
+bool is_operand_value(Operand o) {
+ switch (o.mode) {
+ case Addressing_Value:
+ case Addressing_Variable:
+ case Addressing_Constant:
+ return true;
+ }
+ return false;
+}
+
typedef struct DeclInfo {
@@ -92,6 +102,14 @@ typedef enum ExprKind {
Expr_Stmt,
} ExprKind;
+// Statements and Declarations
+typedef enum StmtFlag {
+ Stmt_BreakAllowed = 1<<0,
+ Stmt_ContinueAllowed = 1<<1,
+ Stmt_FallthroughAllowed = 1<<2,
+ Stmt_GiveAllowed = 1<<3,
+} StmtFlag;
+
typedef enum BuiltinProcId {
BuiltinProc_Invalid,
@@ -357,9 +375,11 @@ void add_scope(Checker *c, AstNode *node, Scope *scope) {
void check_open_scope(Checker *c, AstNode *node) {
GB_ASSERT(node != NULL);
+ node = unparen_expr(node);
GB_ASSERT(node->kind == AstNode_Invalid ||
is_ast_node_stmt(node) ||
- is_ast_node_type(node));
+ is_ast_node_type(node) ||
+ node->kind == AstNode_BlockExpr);
Scope *scope = make_scope(c->context.scope, c->allocator);
add_scope(c, node, scope);
if (node->kind == AstNode_ProcType) {
diff --git a/src/checker/decl.c b/src/checker/decl.c
index cc2c48f78..0c4d3f075 100644
--- a/src/checker/decl.c
+++ b/src/checker/decl.c
@@ -92,7 +92,14 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
}
if (rhs_count > 0 && lhs_count != rhs_count) {
- error(lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count);
+ error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
+ }
+
+ if (lhs[0]->kind == Entity_Variable &&
+ lhs[0]->Variable.is_let) {
+ if (lhs_count != rhs_count) {
+ error(lhs[0]->token, "`let` variables must be initialized, `%td` = `%td`", lhs_count, rhs_count);
+ }
}
gb_temp_arena_memory_end(tmp);
@@ -350,6 +357,11 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
}
+ if (proc_type->Proc.calling_convention != ProcCC_Odin) {
+ error_node(d->proc_decl, "An internal procedure may only have the Odin calling convention");
+ proc_type->Proc.calling_convention = ProcCC_Odin;
+ }
+
d->scope = c->context.scope;
GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
diff --git a/src/checker/expr.c b/src/checker/expr.c
index 299dd55c9..390979cbb 100644
--- a/src/checker/expr.c
+++ b/src/checker/expr.c
@@ -14,6 +14,10 @@ void check_entity_decl (Checker *c, Entity *e, DeclInfo *decl, Type
void check_const_decl (Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr, Type *named_type);
void check_proc_body (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body);
void update_expr_type (Checker *c, AstNode *e, Type *type, bool final);
+bool check_is_terminating (AstNode *node);
+bool check_has_break (AstNode *stmt, bool implicit);
+void check_stmt (Checker *c, AstNode *node, u32 flags);
+void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags);
gb_inline Type *check_type(Checker *c, AstNode *expression) {
@@ -3652,6 +3656,42 @@ bool check_set_index_data(Operand *o, Type *t, i64 *max_count) {
return false;
}
+
+bool check_is_giving(AstNode *node, AstNode **give_expr);
+
+bool check_giving_list(AstNodeArray stmts, AstNode **give_expr) {
+ // Iterate backwards
+ for (isize n = stmts.count-1; n >= 0; n--) {
+ AstNode *stmt = stmts.e[n];
+ if (stmt->kind != AstNode_EmptyStmt) {
+ return check_is_giving(stmt, give_expr);
+ }
+ }
+
+ if (give_expr) *give_expr = NULL;
+ return false;
+}
+
+bool check_is_giving(AstNode *node, AstNode **give_expr) {
+ switch (node->kind) {
+ case_ast_node(es, ExprStmt, node);
+ return check_is_giving(es->expr, give_expr);
+ case_end;
+
+ case_ast_node(ye, GiveExpr, node);
+ if (give_expr) *give_expr = node;
+ return true;
+ case_end;
+
+ case_ast_node(be, BlockExpr, node);
+ return check_giving_list(be->stmts, give_expr);
+ case_end;
+ }
+
+ if (give_expr) *give_expr = NULL;
+ return false;
+}
+
ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) {
ExprKind kind = Expr_Stmt;
@@ -3700,7 +3740,105 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
o->mode = Addressing_Value;
o->type = proc_type;
+ case_end;
+
+ case_ast_node(be, BlockExpr, node);
+ if (c->proc_stack.count == 0) {
+ error_node(node, "A block expression is only allowed withing a procedure");
+ goto error;
+ }
+
+ for (isize i = be->stmts.count-1; i >= 0; i--) {
+ if (be->stmts.e[i]->kind != AstNode_EmptyStmt) {
+ break;
+ }
+ be->stmts.count--;
+ }
+
+ check_open_scope(c, node);
+ check_stmt_list(c, be->stmts, Stmt_GiveAllowed);
+ check_close_scope(c);
+
+
+ if (be->stmts.count == 0) {
+ error_node(node, "Empty block expression");
+ goto error;
+ }
+ AstNode *give_node = NULL;
+ if (!check_is_giving(node, &give_node) || give_node == NULL) {
+ error_node(node, "Missing give statement at");
+ goto error;
+ }
+ GB_ASSERT(give_node != NULL && give_node->kind == AstNode_GiveExpr);
+ be->give_node = give_node;
+
+ o->type = type_of_expr(&c->info, give_node);
+ o->mode = Addressing_Value;
+ case_end;
+
+ case_ast_node(ye, GiveExpr, node);
+ if (c->proc_stack.count == 0) {
+ error_node(node, "A give expression is only allowed withing a procedure");
+ goto error;
+ }
+
+ if (ye->results.count == 0) {
+ error_node(node, "No give values");
+ goto error;
+ }
+
+ gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
+
+ Array(Operand) operands;
+ array_init_reserve(&operands, c->tmp_allocator, 2*ye->results.count);
+
+ for_array(i, ye->results) {
+ AstNode *rhs = ye->results.e[i];
+ Operand o = {0};
+ check_multi_expr(c, &o, rhs);
+ if (!is_operand_value(o)) {
+ error_node(rhs, "Expected a value for a `give`");
+ continue;
+ }
+ if (o.type->kind != Type_Tuple) {
+ array_add(&operands, o);
+ } else {
+ TypeTuple *tuple = &o.type->Tuple;
+ for (isize j = 0; j < tuple->variable_count; j++) {
+ o.type = tuple->variables[j]->type;
+ array_add(&operands, o);
+ }
+ }
+ }
+
+ Type *give_type = NULL;
+
+ if (operands.count == 0) {
+ error_node(node, "`give` has no type");
+ gb_temp_arena_memory_end(tmp);
+ goto error;
+ } else if (operands.count == 1) {
+ give_type = default_type(operands.e[0].type);
+ } else {
+ Type *tuple = make_type_tuple(c->allocator);
+
+ Entity **variables = gb_alloc_array(c->allocator, Entity *, operands.count);
+ isize variable_index = 0;
+ for_array(i, operands) {
+ Type *type = default_type(operands.e[i].type);
+ variables[variable_index++] = make_entity_param(c->allocator, NULL, empty_token, type, false);
+ }
+ tuple->Tuple.variables = variables;
+ tuple->Tuple.variable_count = operands.count;
+
+ give_type = tuple;
+ }
+ gb_temp_arena_memory_end(tmp);
+
+
+ o->type = give_type;
+ o->mode = Addressing_Value;
case_end;
case_ast_node(cl, CompoundLit, node);
diff --git a/src/checker/stmt.c b/src/checker/stmt.c
index 7ae8355d2..fd10fab31 100644
--- a/src/checker/stmt.c
+++ b/src/checker/stmt.c
@@ -1,16 +1,3 @@
-bool check_is_terminating(AstNode *node);
-bool check_has_break (AstNode *stmt, bool implicit);
-void check_stmt (Checker *c, AstNode *node, u32 flags);
-
-
-// Statements and Declarations
-typedef enum StmtFlag {
- Stmt_BreakAllowed = GB_BIT(0),
- Stmt_ContinueAllowed = GB_BIT(1),
- Stmt_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
-} StmtFlag;
-
-
void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
if (stmts.count == 0) {
return;
@@ -40,6 +27,10 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
if (n->kind == AstNode_ReturnStmt && i+1 < max) {
error_node(n, "Statements after this `return` are never executed");
+ } else if (n->kind == AstNode_ExprStmt && i+1 < max) {
+ if (n->ExprStmt.expr->kind == AstNode_GiveExpr) {
+ error_node(n, "A `give` must be the last statement in a block");
+ }
}
check_stmt(c, n, new_flags);
@@ -48,7 +39,6 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
}
bool check_is_terminating_list(AstNodeArray stmts) {
-
// Iterate backwards
for (isize n = stmts.count-1; n >= 0; n--) {
AstNode *stmt = stmts.e[n];
@@ -355,6 +345,12 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
if (operand.expr->kind == AstNode_CallExpr) {
return;
}
+ if (operand.expr->kind == AstNode_GiveExpr) {
+ if ((flags&Stmt_GiveAllowed) != 0) {
+ return;
+ }
+ error_node(node, "Illegal use of `give`");
+ }
gbString expr_str = expr_to_string(operand.expr);
error_node(node, "Expression is not used: `%s`", expr_str);
gb_string_free(expr_str);
diff --git a/src/checker/types.c b/src/checker/types.c
index c3fff7e01..e58876025 100644
--- a/src/checker/types.c
+++ b/src/checker/types.c
@@ -775,6 +775,65 @@ Type *default_type(Type *type) {
return type;
}
+// NOTE(bill): Valid Compile time execution #run type
+bool is_type_cte_safe(Type *type) {
+ type = default_type(base_type(type));
+ switch (type->kind) {
+ case Type_Basic:
+ switch (type->Basic.kind) {
+ case Basic_rawptr:
+ case Basic_any:
+ return false;
+ }
+ return true;
+
+ case Type_Pointer:
+ return false;
+
+ case Type_Array:
+ return is_type_cte_safe(type->Array.elem);
+
+ case Type_Vector: // NOTE(bill): This should always to be true but this is for sanity reasons
+ return is_type_cte_safe(type->Vector.elem);
+
+ case Type_Slice:
+ return false;
+
+ case Type_Maybe:
+ return is_type_cte_safe(type->Maybe.elem);
+
+ case Type_Record: {
+ if (type->Record.kind != TypeRecord_Struct) {
+ return false;
+ }
+ for (isize i = 0; i < type->Record.field_count; i++) {
+ Entity *v = type->Record.fields[i];
+ if (!is_type_cte_safe(v->type)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case Type_Tuple: {
+ for (isize i = 0; i < type->Tuple.variable_count; i++) {
+ Entity *v = type->Tuple.variables[i];
+ if (!is_type_cte_safe(v->type)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case Type_Proc:
+ // TODO(bill): How should I handle procedures in the CTE stage?
+ // return type->Proc.calling_convention == ProcCC_Odin;
+ return false;
+ }
+
+ return false;
+}
+