diff options
| author | Ginger Bill <bill@gingerbill.org> | 2017-06-22 01:14:45 +0100 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2017-06-22 01:14:45 +0100 |
| commit | 8197c02dcf964be404cd199858e84e9e8f3905e5 (patch) | |
| tree | 177393f565eddb1178423d776cb275d56cfcffee /src/check_stmt.cpp | |
| parent | 9faf0020cc04c9c27fc7a7e7e41edce6df31376e (diff) | |
Default result values for procedure types; Named result values in `return` statements
Diffstat (limited to 'src/check_stmt.cpp')
| -rw-r--r-- | src/check_stmt.cpp | 148 |
1 files changed, 126 insertions, 22 deletions
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c4ad2570e..fcd601446 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -786,37 +786,141 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { break; } + bool first_is_field_value = false; + if (rs->results.count > 0) { + bool fail = false; + first_is_field_value = (rs->results[0]->kind == AstNode_FieldValue); + for_array(i, rs->results) { + AstNode *arg = rs->results[i]; + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != AstNode_FieldValue; + } else { + mix = arg->kind == AstNode_FieldValue; + } + if (mix) { + error(arg, "Mixture of `field = value` and value elements in a procedure all is not allowed"); + fail = true; + } + } + + if (fail) { + return; + } + } + Type *proc_type = c->proc_stack[c->proc_stack.count-1]; + TypeProc *pt = &proc_type->Proc; isize result_count = 0; - if (proc_type->Proc.results) { + if (pt->results) { result_count = proc_type->Proc.results->Tuple.variable_count; } - if (result_count > 0) { - Entity **variables = NULL; - if (proc_type->Proc.results != NULL) { - TypeTuple *tuple = &proc_type->Proc.results->Tuple; - variables = tuple->variables; + + isize result_count_excluding_defaults = result_count; + for (isize i = result_count-1; i >= 0; i--) { + Entity *e = pt->results->Tuple.variables[i]; + if (e->kind == Entity_TypeName) { + break; + } + + GB_ASSERT(e->kind == Entity_Variable); + if (e->Variable.default_value.kind != ExactValue_Invalid || + e->Variable.default_is_nil) { + result_count_excluding_defaults--; + continue; + } + break; + } + + Array<Operand> operands = {}; + defer (array_free(&operands)); + + if (first_is_field_value) { + array_init_count(&operands, heap_allocator(), rs->results.count); + for_array(i, rs->results) { + AstNode *arg = rs->results[i]; + ast_node(fv, FieldValue, arg); + check_expr(c, &operands[i], fv->value); } - if (rs->results.count == 0) { - error(node, "Expected %td return values, got 0", result_count); + } else { + array_init(&operands, heap_allocator(), 2*rs->results.count); + check_unpack_arguments(c, -1, &operands, rs->results, false); + } + + + if (first_is_field_value) { + bool *visited = gb_alloc_array(c->allocator, bool, result_count); + + for_array(i, rs->results) { + AstNode *arg = rs->results[i]; + ast_node(fv, FieldValue, arg); + if (fv->field->kind != AstNode_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name `%s` in return statement", expr_str); + gb_string_free(expr_str); + continue; + } + String name = fv->field->Ident.string; + isize index = lookup_procedure_result(pt, name); + if (index < 0) { + error(arg, "No result named `%.*s` for this procedure type", LIT(name)); + continue; + } + if (visited[index]) { + error(arg, "Duplicate result `%.*s` in return statement", LIT(name)); + continue; + } + + visited[index] = true; + Operand *o = &operands[i]; + Entity *e = pt->results->Tuple.variables[index]; + check_assignment(c, &operands[i], e->type, str_lit("return statement")); + } + + for (isize i = 0; i < result_count; i++) { + if (!visited[i]) { + Entity *e = pt->results->Tuple.variables[i]; + if (e->token.string == "_") { + continue; + } + GB_ASSERT(e->kind == Entity_Variable); + if (e->Variable.default_value.kind != ExactValue_Invalid) { + continue; + } + + if (e->Variable.default_is_nil) { + continue; + } + + gbString str = type_to_string(e->type); + error(node, "Return value `%.*s` of type `%s` is missing in return statement", + LIT(e->token.string), str); + gb_string_free(str); + } + } + } else { + // TODO(bill): Cleanup this checking of variables + if (result_count == 0 && rs->results.count > 0) { + error(rs->results[0], "No return values expected"); + } else if (operands.count > result_count) { + error(node, "Expected a maximum of %td return values, got %td", result_count, operands.count); + } else if (operands.count < result_count_excluding_defaults) { + error(node, "Expected %td return values, got %td", result_count_excluding_defaults, operands.count); + } else if (result_count_excluding_defaults == 0) { + return; + } else if (rs->results.count == 0) { + error(node, "Expected %td return values, got 0", result_count_excluding_defaults); } else { - // TokenPos pos = rs->token.pos; - // if (pos.line == 10) { - // gb_printf_err("%s\n", type_to_string(variables[0]->type)); - // } - check_init_variables(c, variables, result_count, - rs->results, str_lit("return statement")); - // if (pos.line == 10) { - // AstNode *x = rs->results[0]; - // gb_printf_err("%s\n", expr_to_string(x)); - // gb_printf_err("%s\n", type_to_string(type_of_expr(&c->info, x))); - // } - } - } else if (rs->results.count > 0) { - error(rs->results[0], "No return values expected"); + isize max_count = rs->results.count; + for (isize i = 0; i < max_count; i++) { + Entity *e = pt->results->Tuple.variables[i]; + check_assignment(c, &operands[i], e->type, str_lit("return statement")); + } + } } + case_end; case_ast_node(fs, ForStmt, node); |