// Check the extra information. This includes: // * legal data pointer // * legal scope symbol table // * no extra children static check_res_t check_extras(ast_t* ast, check_state_t* state) { assert(ast != NULL); assert(state != NULL); if(state->child != NULL) { printf("Internal error: AST node id %d, child %ld (id %d) unexpected\n", ast_id(ast), state->child_index, ast_id(state->child)); return CHK_ERROR; } if(ast_data(ast) != NULL && !state->has_data) { printf("Internal error: AST node id %d, unexpected data %p\n", ast_id(ast), ast_data(ast)); return CHK_ERROR; } if(ast_has_scope(ast) && !state->is_scope) { printf("Internal error: AST node id %d unexpectedly has scope\n", ast_id(ast)); return CHK_ERROR; } return CHK_OK; }
// Sort out symbol table for copied method body static ast_result_t rescope(ast_t** astp, pass_opt_t* options) { (void)options; ast_t* ast = *astp; if(ast_has_scope(ast)) ast_clear_local(ast); switch(ast_id(ast)) { case TK_FVAR: case TK_FLET: case TK_PARAM: case TK_TYPEPARAM: assert(ast_child(ast) != NULL); ast_set(ast, ast_name(ast_child(ast)), ast, SYM_DEFINED); break; case TK_LET: case TK_VAR: { ast_t* scope = ast_parent(ast); ast_t* id = ast_child(ast); ast_set(scope, ast_name(id), id, SYM_DEFINED); break; } default: {} } return AST_OK; }
// Sort out symbol table for copied method body. static ast_result_t rescope(ast_t** astp, pass_opt_t* options) { (void)options; ast_t* ast = *astp; if(ast_has_scope(ast)) ast_clear_local(ast); switch(ast_id(ast)) { case TK_FVAR: case TK_FLET: case TK_EMBED: case TK_PARAM: case TK_TYPEPARAM: { assert(ast_child(ast) != NULL); ast_set(ast, ast_name(ast_child(ast)), ast, SYM_DEFINED, true); break; } case TK_LET: case TK_VAR: { ast_t* scope = ast_parent(ast); ast_t* id = ast_child(ast); ast_set(scope, ast_name(id), id, SYM_UNDEFINED, true); break; } case TK_MATCH_CAPTURE: { ast_t* scope = ast_parent(ast); ast_t* id = ast_child(ast); ast_set(scope, ast_name(id), id, SYM_DEFINED, true); break; } case TK_TYPEPARAMREF: { assert(ast_child(ast) != NULL); ast_t* def = ast_get(ast, ast_name(ast_child(ast)), NULL); ast_setdata(ast, def); break; } default: {} } return AST_OK; }
LLVMValueRef gen_expr(compile_t* c, ast_t* ast) { LLVMValueRef ret; bool has_scope = ast_has_scope(ast); bool has_source = codegen_hassource(c); if(has_scope) { codegen_pushscope(c); // Dwarf a new lexical scope, if necessary. if(has_source) dwarf_lexicalscope(&c->dwarf, ast); } switch(ast_id(ast)) { case TK_SEQ: ret = gen_seq(c, ast); break; case TK_FVARREF: case TK_FLETREF: ret = gen_fieldload(c, ast); break; case TK_PARAMREF: ret = gen_param(c, ast); break; case TK_VAR: case TK_LET: ret = gen_localdecl(c, ast); break; case TK_VARREF: case TK_LETREF: ret = gen_localload(c, ast); break; case TK_IF: ret = gen_if(c, ast); break; case TK_WHILE: ret = gen_while(c, ast); break; case TK_REPEAT: ret = gen_repeat(c, ast); break; case TK_TRY: case TK_TRY_NO_CHECK: ret = gen_try(c, ast); break; case TK_MATCH: ret = gen_match(c, ast); break; case TK_CALL: ret = gen_call(c, ast); break; case TK_CONSUME: ret = gen_expr(c, ast_childidx(ast, 1)); break; case TK_RECOVER: ret = gen_expr(c, ast_childidx(ast, 1)); break; case TK_BREAK: ret = gen_break(c, ast); break; case TK_CONTINUE: ret = gen_continue(c, ast); break; case TK_RETURN: ret = gen_return(c, ast); break; case TK_ERROR: ret = gen_error(c, ast); break; case TK_IS: ret = gen_is(c, ast); break; case TK_ISNT: ret = gen_isnt(c, ast); break; case TK_ASSIGN: ret = gen_assign(c, ast); break; case TK_THIS: ret = gen_this(c, ast); break; case TK_TRUE: ret = LLVMConstInt(c->i1, 1, false); break; case TK_FALSE: ret = LLVMConstInt(c->i1, 0, false); break; case TK_INT: ret = gen_int(c, ast); break; case TK_FLOAT: ret = gen_float(c, ast); break; case TK_STRING: ret = gen_string(c, ast); break; case TK_TUPLE: ret = gen_tuple(c, ast); break; case TK_FFICALL: ret = gen_ffi(c, ast); break; case TK_AMP: ret = gen_addressof(c, ast); break; case TK_IDENTITY: ret = gen_identity(c, ast); break; case TK_DONTCARE: ret = GEN_NOVALUE; break; case TK_COMPILER_INTRINSIC: ast_error(ast, "unimplemented compiler intrinsic"); LLVMBuildUnreachable(c->builder); ret = GEN_NOVALUE; break; default: ast_error(ast, "not implemented (codegen unknown)"); return NULL; } if(has_scope) { codegen_popscope(c); if(has_source) dwarf_finish(&c->dwarf); } return ret; }
LLVMValueRef gen_expr(compile_t* c, ast_t* ast) { LLVMValueRef ret; bool has_scope = ast_has_scope(ast); if(has_scope) codegen_pushscope(c, ast); switch(ast_id(ast)) { case TK_SEQ: ret = gen_seq(c, ast); break; case TK_FVARREF: case TK_FLETREF: ret = gen_fieldload(c, ast); break; case TK_EMBEDREF: ret = gen_fieldptr(c, ast); break; case TK_PARAMREF: ret = gen_param(c, ast); break; case TK_VAR: case TK_LET: case TK_MATCH_CAPTURE: ret = gen_localdecl(c, ast); break; case TK_VARREF: case TK_LETREF: ret = gen_localload(c, ast); break; case TK_IF: ret = gen_if(c, ast); break; case TK_WHILE: ret = gen_while(c, ast); break; case TK_REPEAT: ret = gen_repeat(c, ast); break; case TK_TRY: case TK_TRY_NO_CHECK: ret = gen_try(c, ast); break; case TK_MATCH: ret = gen_match(c, ast); break; case TK_CALL: ret = gen_call(c, ast); break; case TK_CONSUME: ret = gen_expr(c, ast_childidx(ast, 1)); break; case TK_RECOVER: ret = gen_expr(c, ast_childidx(ast, 1)); break; case TK_BREAK: ret = gen_break(c, ast); break; case TK_CONTINUE: ret = gen_continue(c, ast); break; case TK_RETURN: ret = gen_return(c, ast); break; case TK_ERROR: ret = gen_error(c, ast); break; case TK_IS: ret = gen_is(c, ast); break; case TK_ISNT: ret = gen_isnt(c, ast); break; case TK_ASSIGN: ret = gen_assign(c, ast); break; case TK_THIS: ret = gen_this(c, ast); break; case TK_TRUE: ret = LLVMConstInt(c->i1, 1, false); break; case TK_FALSE: ret = LLVMConstInt(c->i1, 0, false); break; case TK_INT: ret = gen_int(c, ast); break; case TK_FLOAT: ret = gen_float(c, ast); break; case TK_STRING: ret = gen_string(c, ast); break; case TK_TUPLE: ret = gen_tuple(c, ast); break; case TK_FFICALL: ret = gen_ffi(c, ast); break; case TK_ADDRESS: ret = gen_addressof(c, ast); break; case TK_DIGESTOF: ret = gen_digestof(c, ast); break; case TK_DONTCARE: ret = GEN_NOVALUE; break; case TK_COMPILE_INTRINSIC: ast_error(c->opt->check.errors, ast, "unimplemented compile intrinsic"); return NULL; case TK_COMPILE_ERROR: { ast_t* reason_seq = ast_child(ast); ast_t* reason = ast_child(reason_seq); ast_error(c->opt->check.errors, ast, "compile error: %s", ast_name(reason)); return NULL; } default: ast_error(c->opt->check.errors, ast, "not implemented (codegen unknown)"); return NULL; } if(has_scope) codegen_popscope(c); return ret; }
static bool compare_asts(ast_t* prev, ast_t* expected, ast_t* actual, bool check_siblings) { assert(prev != NULL); if(expected == NULL && actual == NULL) return true; if(actual == NULL) { ast_error(expected, "Expected AST %s not found", ast_get_print(expected)); return false; } if(expected == NULL) { ast_error(prev, "Unexpected AST node found, %s", ast_get_print(actual)); return false; } token_id expected_id = ast_id(expected); token_id actual_id = ast_id(actual); if(expected_id != actual_id) { ast_error(expected, "AST ID mismatch, got %d (%s), expected %d (%s)", ast_id(actual), ast_get_print(actual), ast_id(expected), ast_get_print(expected)); return false; } if(ast_id(expected) == TK_ID && ast_name(actual)[0] == '$' && strcmp(ast_name(expected), "hygid") == 0) { // Allow expected "hygid" to match any hygenic ID } else if(strcmp(ast_get_print(expected), ast_get_print(actual)) != 0) { ast_error(expected, "AST text mismatch, got %s, expected %s", ast_get_print(actual), ast_get_print(expected)); return false; } if(ast_has_scope(expected) && !ast_has_scope(actual)) { ast_error(expected, "AST missing scope"); return false; } if(!ast_has_scope(expected) && ast_has_scope(actual)) { ast_error(actual, "Unexpected AST scope"); return false; } if(!compare_asts(expected, ast_child(expected), ast_child(actual), true) || !compare_asts(expected, ast_type(expected), ast_type(actual), true)) return false; return !check_siblings || compare_asts(expected, ast_sibling(expected), ast_sibling(actual), true); }
bool expr_seq(pass_opt_t* opt, ast_t* ast) { bool ok = true; // Any expression other than the last that is still literal is an error for(ast_t* p = ast_child(ast); ast_sibling(p) != NULL; p = ast_sibling(p)) { ast_t* p_type = ast_type(p); if(is_typecheck_error(p_type)) { ok = false; } else if(is_type_literal(p_type)) { ast_error(p, "Cannot infer type of unused literal"); ok = false; } } // We might already have a type due to a return expression. ast_t* type = ast_type(ast); ast_t* last = ast_childlast(ast); if((type != NULL) && !coerce_literals(&last, type, opt)) return false; // Type is unioned with the type of the last child. type = control_type_add_branch(type, last); ast_settype(ast, type); ast_inheritflags(ast); if(!ast_has_scope(ast)) return ok; ast_t* parent = ast_parent(ast); switch(ast_id(parent)) { case TK_TRY: case TK_TRY_NO_CHECK: { // Propagate consumes forward in a try expression. AST_GET_CHILDREN(parent, body, else_clause, then_clause); if(body == ast) { // Push our consumes, but not defines, to the else clause. ast_inheritbranch(else_clause, body); ast_consolidate_branches(else_clause, 2); } else if(else_clause == ast) { // Push our consumes, but not defines, to the then clause. This // includes the consumes from the body. ast_inheritbranch(then_clause, else_clause); ast_consolidate_branches(then_clause, 2); } } default: {} } return ok; }
// Check the extra information. This includes: // * legal data pointer // * legal scope symbol table // * no extra children static check_res_t check_extras(ast_t* ast, check_state_t* state, errors_t* errors) { assert(ast != NULL); assert(state != NULL); ast_t* type_field = ast_type(ast); if(type_field != NULL) { if(state->type == NULL) { error_preamble(ast); printf("unexpected type\n"); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } check_res_t r = state->type(type_field, errors); if(r == CHK_ERROR) // Propogate error return CHK_ERROR; if(r == CHK_NOT_FOUND) { error_preamble(ast); printf("type field has invalid id %d\n", ast_id(type_field)); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } } if(state->child != NULL) { error_preamble(ast); printf("child " __zu " (id %d, %s) unexpected\n", state->child_index, ast_id(state->child), ast_get_print(state->child)); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } if(ast_data(ast) != NULL && !state->has_data) { error_preamble(ast); printf("unexpected data %p\n", ast_data(ast)); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } if(ast_has_scope(ast) && !state->is_scope) { error_preamble(ast); printf("unexpected scope\n"); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } if(!ast_has_scope(ast) && state->is_scope) { error_preamble(ast); printf("expected scope not found\n"); ast_error(state->errors, ast, "Here"); ast_print(ast); #ifdef IMMEDIATE_FAIL assert(false); #endif return CHK_ERROR; } return CHK_OK; }