bool expr_consume(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, cap, term); ast_t* type = ast_type(term); if(is_typecheck_error(type)) return false; const char* name = NULL; switch(ast_id(term)) { case TK_VARREF: case TK_LETREF: case TK_PARAMREF: { ast_t* id = ast_child(term); name = ast_name(id); break; } case TK_THIS: { name = stringtab("this"); break; } default: ast_error(opt->check.errors, ast, "consume must take 'this', a local, or a parameter"); return false; } // Can't consume from an outer scope while in a loop condition. if((opt->check.frame->loop_cond != NULL) && !ast_within_scope(opt->check.frame->loop_cond, ast, name)) { ast_error(opt->check.errors, ast, "can't consume from an outer scope in a loop condition"); return false; } ast_setstatus(ast, name, SYM_CONSUMED); token_id tcap = ast_id(cap); ast_t* c_type = consume_type(type, tcap); if(c_type == NULL) { ast_error(opt->check.errors, ast, "can't consume to this capability"); ast_error_continue(opt->check.errors, term, "expression type is %s", ast_print_type(type)); return false; } ast_settype(ast, c_type); return true; }
bool ast_all_consumes_in_scope(ast_t* outer, ast_t* inner, errorframe_t* errorf) { ast_t* from = inner; bool ok = true; do { if(inner->symtab != NULL) { size_t i = HASHMAP_BEGIN; symbol_t* sym; while((sym = symtab_next(inner->symtab, &i)) != NULL) { // If it's consumed, and has a null value, it's from outside of this // scope. We need to know if it's outside the loop scope. if((sym->status == SYM_CONSUMED) && (sym->def == NULL)) { if(!ast_within_scope(outer, inner, sym->name)) { ast_t* def = ast_get(inner, sym->name, NULL); if(errorf != NULL) { ast_error_frame(errorf, from, "identifier '%s' is consumed when the loop exits", sym->name); ast_error_frame(errorf, def, "consumed identifier is defined here"); } ok = false; } } } } if(inner == outer) break; inner = inner->parent; } while((inner != NULL) && (token_get_id(inner->t) != TK_PROGRAM)); return ok; }