mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-19 21:10:30 +00:00
Fix give expressions
This commit is contained in:
@@ -12,11 +12,10 @@ import {
|
||||
}
|
||||
|
||||
proc main() {
|
||||
var cond = true;
|
||||
var msg = if cond {
|
||||
give "hello";
|
||||
var x = if false {
|
||||
give 123;
|
||||
} else {
|
||||
give "goodbye";
|
||||
give 321;
|
||||
};
|
||||
fmt.println(msg);
|
||||
fmt.println(x);
|
||||
}
|
||||
|
||||
@@ -3657,34 +3657,32 @@ bool check_set_index_data(Operand *o, Type *t, i64 *max_count) {
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
if (es->expr->kind == AstNode_GiveExpr) {
|
||||
if (give_expr) *give_expr = es->expr;
|
||||
return true;
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(ye, GiveExpr, node);
|
||||
case_ast_node(ge, 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);
|
||||
// Iterate backwards
|
||||
for (isize n = be->stmts.count-1; n >= 0; n--) {
|
||||
AstNode *stmt = be->stmts.e[n];
|
||||
if (stmt->kind == AstNode_EmptyStmt) {
|
||||
continue;
|
||||
}
|
||||
if (stmt->kind == AstNode_ExprStmt && stmt->ExprStmt.expr->kind == AstNode_GiveExpr) {
|
||||
if (give_expr) *give_expr = stmt->ExprStmt.expr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
}
|
||||
|
||||
@@ -3742,6 +3740,79 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
||||
o->type = proc_type;
|
||||
case_end;
|
||||
|
||||
case_ast_node(ge, GiveExpr, node);
|
||||
if (c->proc_stack.count == 0) {
|
||||
error_node(node, "A give expression is only allowed within a procedure");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ge->results.count == 0) {
|
||||
error_node(node, "`give` has no results");
|
||||
goto error;
|
||||
}
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
|
||||
Array(Operand) operands;
|
||||
array_init_reserve(&operands, c->tmp_allocator, 2*ge->results.count);
|
||||
|
||||
for_array(i, ge->results) {
|
||||
AstNode *rhs = ge->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (operands.count == 0) {
|
||||
error_node(node, "`give` has no value");
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
goto error;
|
||||
} else if (operands.count == 1) {
|
||||
Operand operand = operands.e[0];
|
||||
if (type_hint != NULL) {
|
||||
convert_to_typed(c, &operand, type_hint, 0);
|
||||
}
|
||||
o->type = default_type(operand.type);
|
||||
o->mode = Addressing_Value;
|
||||
} 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) {
|
||||
Operand operand = operands.e[i];
|
||||
// Type *type = default_type(operand.type);
|
||||
Type *type = operand.type;
|
||||
switch (operand.mode) {
|
||||
case Addressing_Constant:
|
||||
variables[variable_index++] = make_entity_constant(c->allocator, NULL, empty_token, type, operand.value);
|
||||
break;
|
||||
default:
|
||||
variables[variable_index++] = make_entity_param(c->allocator, NULL, empty_token, type, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
tuple->Tuple.variables = variables;
|
||||
tuple->Tuple.variable_count = operands.count;
|
||||
|
||||
o->type = tuple;
|
||||
o->mode = Addressing_Value;
|
||||
}
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
case_end;
|
||||
|
||||
case_ast_node(be, BlockExpr, node);
|
||||
if (c->proc_stack.count == 0) {
|
||||
error_node(node, "A block expression is only allowed within a procedure");
|
||||
@@ -3755,26 +3826,34 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
||||
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;
|
||||
}
|
||||
|
||||
check_open_scope(c, node);
|
||||
check_stmt_list(c, be->stmts, Stmt_GiveAllowed);
|
||||
check_close_scope(c);
|
||||
|
||||
AstNode *give_node = NULL;
|
||||
if (!check_is_giving(node, &give_node) || give_node == NULL) {
|
||||
error_node(node, "Missing give statement at");
|
||||
error_node(node, "Missing give statement at end of block expression");
|
||||
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;
|
||||
Type *type = type_of_expr(&c->info, give_node);
|
||||
if (type == NULL) {
|
||||
goto error;
|
||||
} else if (type == t_invalid) {
|
||||
o->type = t_invalid;
|
||||
o->mode = Addressing_Invalid;
|
||||
} else {
|
||||
o->type = type;
|
||||
o->mode = Addressing_Value;
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(ie, IfExpr, node);
|
||||
@@ -3795,17 +3874,20 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
||||
error_node(ie->cond, "Non-boolean condition in if expression");
|
||||
}
|
||||
|
||||
|
||||
Operand x = {Addressing_Invalid};
|
||||
Operand y = {Addressing_Invalid};
|
||||
Type *if_type = NULL;
|
||||
Type *else_type = NULL;
|
||||
check_expr(c, &operand, ie->body);
|
||||
if_type = operand.type;
|
||||
check_expr(c, &x, ie->body);
|
||||
if_type = x.type;
|
||||
|
||||
if (ie->else_expr != NULL) {
|
||||
switch (ie->else_expr->kind) {
|
||||
case AstNode_IfExpr:
|
||||
case AstNode_BlockExpr:
|
||||
check_expr(c, &operand, ie->else_expr);
|
||||
else_type = operand.type;
|
||||
check_expr(c, &y, ie->else_expr);
|
||||
else_type = y.type;
|
||||
break;
|
||||
default:
|
||||
error_node(ie->else_expr, "Invalid else expression in if expression");
|
||||
@@ -3819,87 +3901,35 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
||||
|
||||
check_close_scope(c);
|
||||
|
||||
GB_ASSERT(if_type != NULL &&
|
||||
else_type != NULL);
|
||||
if (if_type == NULL || if_type == t_invalid ||
|
||||
else_type == NULL || else_type == t_invalid) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
convert_to_typed(c, &x, y.type, 0);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
goto error;
|
||||
}
|
||||
convert_to_typed(c, &y, x.type, 0);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
x.mode = Addressing_Invalid;
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
if (!are_types_identical(if_type, else_type)) {
|
||||
gbString its = type_to_string(if_type);
|
||||
gbString ets = type_to_string(else_type);
|
||||
error_node(node, "if expression type and else expression type do not match, %s != %s", its, ets);
|
||||
error_node(node, "Mismatched types in if expression, %s vs %s", its, ets);
|
||||
gb_string_free(ets);
|
||||
gb_string_free(its);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
o->type = if_type;
|
||||
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 within 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);
|
||||
Type *type = type_hint;
|
||||
bool ellipsis_array = false;
|
||||
@@ -4491,6 +4521,13 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
|
||||
str = gb_string_appendc(str, "}");
|
||||
case_end;
|
||||
|
||||
case_ast_node(be, BlockExpr, node);
|
||||
str = gb_string_appendc(str, "block expression");
|
||||
case_end;
|
||||
case_ast_node(ie, IfExpr, node);
|
||||
str = gb_string_appendc(str, "if expression");
|
||||
case_end;
|
||||
|
||||
case_ast_node(te, TagExpr, node);
|
||||
str = gb_string_appendc(str, "#");
|
||||
str = string_append_token(str, te->name);
|
||||
|
||||
@@ -6,7 +6,7 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
|
||||
check_scope_decls(c, stmts, 1.2*stmts.count, NULL);
|
||||
|
||||
bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
|
||||
u32 f = flags & (~Stmt_FallthroughAllowed);
|
||||
flags &= ~Stmt_FallthroughAllowed;
|
||||
|
||||
isize max = stmts.count;
|
||||
for (isize i = stmts.count-1; i >= 0; i--) {
|
||||
@@ -20,16 +20,21 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
|
||||
if (n->kind == AstNode_EmptyStmt) {
|
||||
continue;
|
||||
}
|
||||
u32 new_flags = f;
|
||||
u32 new_flags = flags;
|
||||
if (ft_ok && i+1 == max) {
|
||||
new_flags |= Stmt_FallthroughAllowed;
|
||||
}
|
||||
|
||||
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");
|
||||
if (i+1 < max) {
|
||||
switch (n->kind) {
|
||||
case AstNode_ReturnStmt:
|
||||
error_node(n, "Statements after this `return` are never executed");
|
||||
break;
|
||||
case AstNode_ExprStmt:
|
||||
if (n->ExprStmt.expr->kind == AstNode_GiveExpr) {
|
||||
error_node(n, "A `give` must be the last statement in a block");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -763,6 +763,9 @@ bool are_types_identical(Type *x, Type *y) {
|
||||
|
||||
|
||||
Type *default_type(Type *type) {
|
||||
if (type == NULL) {
|
||||
return t_invalid;
|
||||
}
|
||||
if (type->kind == Type_Basic) {
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_UntypedBool: return t_bool;
|
||||
|
||||
54
src/parser.c
54
src/parser.c
@@ -1275,27 +1275,36 @@ void expect_semicolon(AstFile *f, AstNode *s) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s != NULL && prev_token.pos.line != f->curr_token.pos.line) {
|
||||
switch (s->kind) {
|
||||
case AstNode_ProcDecl:
|
||||
return;
|
||||
case AstNode_GenericDecl:
|
||||
if (s->GenericDecl.close.kind == Token_CloseBrace) {
|
||||
if (s != NULL) {
|
||||
if (prev_token.pos.line != f->curr_token.pos.line) {
|
||||
switch (s->kind) {
|
||||
case AstNode_ProcDecl:
|
||||
return;
|
||||
} else if (s->GenericDecl.token.kind == Token_type) {
|
||||
case AstNode_GenericDecl:
|
||||
if (s->GenericDecl.close.kind == Token_CloseBrace) {
|
||||
return;
|
||||
} else if (s->GenericDecl.token.kind == Token_type) {
|
||||
if (f->prev_token.kind == Token_CloseBrace) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AstNode_TypeSpec:
|
||||
if (f->prev_token.kind == Token_CloseBrace) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case AstNode_TypeSpec:
|
||||
if (f->prev_token.kind == Token_CloseBrace) {
|
||||
return;
|
||||
} else {
|
||||
switch (s->kind) {
|
||||
case AstNode_GiveExpr:
|
||||
if (f->curr_token.kind == Token_CloseBrace) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
syntax_error(prev_token, "Expected `;` after %.*s, got %.*s",
|
||||
LIT(ast_node_strings[s->kind]), LIT(token_strings[prev_token.kind]));
|
||||
} else {
|
||||
@@ -2752,10 +2761,11 @@ AstNode *parse_return_stmt(AstFile *f) {
|
||||
}
|
||||
|
||||
Token token = expect_token(f, Token_return);
|
||||
AstNodeArray results = make_ast_node_array(f);
|
||||
|
||||
AstNodeArray results;
|
||||
if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace) {
|
||||
results = parse_rhs_expr_list(f);
|
||||
} else {
|
||||
results = make_ast_node_array(f);
|
||||
}
|
||||
|
||||
expect_semicolon(f, results.e[0]);
|
||||
@@ -2774,9 +2784,15 @@ AstNode *parse_give_stmt(AstFile *f) {
|
||||
}
|
||||
|
||||
Token token = expect_token(f, Token_give);
|
||||
AstNodeArray results = parse_rhs_expr_list(f);
|
||||
expect_semicolon(f, results.e[0]);
|
||||
return make_expr_stmt(f, make_give_expr(f, token, results));
|
||||
AstNodeArray results;
|
||||
if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace) {
|
||||
results = parse_rhs_expr_list(f);
|
||||
} else {
|
||||
results = make_ast_node_array(f);
|
||||
}
|
||||
AstNode *ge = make_give_expr(f, token, results);
|
||||
expect_semicolon(f, ge);
|
||||
return make_expr_stmt(f, ge);
|
||||
}
|
||||
|
||||
AstNode *parse_for_stmt(AstFile *f) {
|
||||
|
||||
@@ -2665,7 +2665,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
ssaValue *cond = ssa_build_cond(proc, ie->cond, then, else_);
|
||||
proc->curr_block = then;
|
||||
|
||||
|
||||
ssa_open_scope(proc);
|
||||
array_add(&edges, ssa_build_expr(proc, ie->body));
|
||||
ssa_close_scope(proc, ssaDeferExit_Default, NULL);
|
||||
@@ -2721,7 +2720,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
ssaValue *field = ssa_emit_struct_ep(proc, v, i);
|
||||
ssa_emit_store(proc, field, res);
|
||||
}
|
||||
|
||||
v = ssa_emit_load(proc, v);
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
|
||||
@@ -180,10 +180,8 @@ void init_global_error_collector(void) {
|
||||
gb_mutex_init(&global_error_collector.mutex);
|
||||
}
|
||||
|
||||
|
||||
void warning_va(Token token, char *fmt, va_list va) {
|
||||
gb_mutex_lock(&global_error_collector.mutex);
|
||||
|
||||
global_error_collector.warning_count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_eq(global_error_collector.prev, token.pos)) {
|
||||
@@ -198,7 +196,6 @@ void warning_va(Token token, char *fmt, va_list va) {
|
||||
|
||||
void error_va(Token token, char *fmt, va_list va) {
|
||||
gb_mutex_lock(&global_error_collector.mutex);
|
||||
|
||||
global_error_collector.count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_eq(global_error_collector.prev, token.pos)) {
|
||||
@@ -213,7 +210,6 @@ void error_va(Token token, char *fmt, va_list va) {
|
||||
|
||||
void syntax_error_va(Token token, char *fmt, va_list va) {
|
||||
gb_mutex_lock(&global_error_collector.mutex);
|
||||
|
||||
global_error_collector.count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_eq(global_error_collector.prev, token.pos)) {
|
||||
|
||||
Reference in New Issue
Block a user