diff options
| author | Ginger Bill <bill@gingerbill.org> | 2016-12-30 15:45:10 +0000 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2016-12-30 15:45:10 +0000 |
| commit | 23d32f34e526cfb657a72e5b2dab86d1df765f0f (patch) | |
| tree | 7998bcf40ca9f581be6296bf4f68c102a5d234c8 /src/checker | |
| parent | d714bece47ea058e482389452cd428dad9c28fd0 (diff) | |
Block Expressions and `give`
Diffstat (limited to 'src/checker')
| -rw-r--r-- | src/checker/checker.c | 22 | ||||
| -rw-r--r-- | src/checker/decl.c | 14 | ||||
| -rw-r--r-- | src/checker/expr.c | 138 | ||||
| -rw-r--r-- | src/checker/stmt.c | 24 | ||||
| -rw-r--r-- | src/checker/types.c | 59 |
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; +} + |