static ast_result_t sugar_new(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; AST_GET_CHILDREN(ast, cap, id, typeparams, params, result); // Return type default to ref^ for classes, val^ for primitives, and // tag^ for actors. if(ast_id(result) == TK_NONE) { token_id tcap = ast_id(cap); if(tcap == TK_NONE) { switch(ast_id(t->frame->type)) { case TK_PRIMITIVE: tcap = TK_VAL; break; case TK_ACTOR: tcap = TK_TAG; break; default: tcap = TK_REF; break; } ast_setid(cap, tcap); } ast_replace(&result, type_for_this(opt, ast, tcap, TK_EPHEMERAL, false)); } sugar_docstring(ast); return check_method(ast); }
// Process the given provided method for the given entity. // The method passed should be reified already and will be freed by this // function. static bool provided_method(pass_opt_t* opt, ast_t* entity, ast_t* reified_method, ast_t* raw_method, ast_t** last_method) { assert(entity != NULL); assert(reified_method != NULL); assert(last_method != NULL); AST_GET_CHILDREN(reified_method, cap, id, typeparams, params, result, can_error, body, doc); if(ast_id(reified_method) == TK_BE || ast_id(reified_method) == TK_NEW) { // Modify return type to the inheriting type ast_t* this_type = type_for_this(opt, entity, ast_id(cap), TK_EPHEMERAL, true); ast_replace(&result, this_type); } // Ignore docstring if(ast_id(doc) == TK_STRING) { ast_set_name(doc, ""); ast_setid(doc, TK_NONE); } // Check for existing method of the same name const char* name = ast_name(id); assert(name != NULL); ast_t* existing = ast_get(entity, name, NULL); if(existing != NULL && is_field(existing)) { ast_error(existing, "field '%s' clashes with provided method", name); ast_error(raw_method, "method is defined here"); ast_free_unattached(reified_method); return false; } existing = add_method(entity, existing, reified_method, last_method); if(existing == NULL) { ast_free_unattached(reified_method); return false; } method_t* info = (method_t*)ast_data(existing); assert(info != NULL); if(!record_default_body(reified_method, raw_method, info)) ast_free_unattached(reified_method); return true; }
static ast_result_t sugar_be(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, cap, id, typeparams, params, result, can_error, body); ast_setid(cap, TK_TAG); if(ast_id(result) == TK_NONE) { // Return type is This tag ast_replace(&result, type_for_this(opt, ast, TK_TAG, TK_NONE, false)); } sugar_docstring(ast); return check_method(ast); }
bool expr_provides(pass_opt_t* opt, ast_t* ast) { // Check that the type actually provides everything it declares. // Since the traits pass has completed, all method imports have already // happened. At this point, we need to check that the type is a structural // subtype of all traits and interfaces it declares as provided. AST_GET_CHILDREN(ast, id, typeparams, cap, provides); ast_t* type = type_for_this(opt, ast, TK_REF, TK_NONE, true); errorframe_t err = NULL; if(!check_provides(opt, type, provides, &err)) { errorframe_t err2 = NULL; ast_error_frame(&err2, ast, "type does not implement its provides list"); errorframe_append(&err2, &err); errorframe_report(&err2, opt->check.errors); return false; } return true; }
bool expr_this(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; if(t->frame->def_arg != NULL) { ast_error(ast, "can't reference 'this' in a default argument"); return false; } sym_status_t status; ast_get(ast, stringtab("this"), &status); if(status == SYM_CONSUMED) { ast_error(ast, "can't use a consumed 'this' in an expression"); return false; } assert(status == SYM_NONE); token_id cap = cap_for_this(t); if(!cap_sendable(cap) && (t->frame->recover != NULL)) cap = TK_TAG; bool make_arrow = false; if(cap == TK_BOX) { cap = TK_REF; make_arrow = true; } ast_t* type = type_for_this(opt, ast, cap, TK_NONE, false); if(make_arrow) { BUILD(arrow, ast, NODE(TK_ARROW, NODE(TK_THISTYPE) TREE(type))); type = arrow; } // Get the nominal type, which may be the right side of an arrow type. ast_t* nominal; bool arrow; if(ast_id(type) == TK_NOMINAL) { nominal = type; arrow = false; } else { nominal = ast_childidx(type, 1); arrow = true; } ast_t* typeargs = ast_childidx(nominal, 2); ast_t* typearg = ast_child(typeargs); while(typearg != NULL) { if(!expr_nominal(opt, &typearg)) { ast_error(ast, "couldn't create a type for 'this'"); ast_free(type); return false; } typearg = ast_sibling(typearg); } if(!expr_nominal(opt, &nominal)) { ast_error(ast, "couldn't create a type for 'this'"); ast_free(type); return false; } if(arrow) type = ast_parent(nominal); else type = nominal; ast_settype(ast, type); return true; }
bool expr_this(pass_opt_t* opt, ast_t* ast) { typecheck_t* t = &opt->check; if(t->frame->def_arg != NULL) { ast_error(opt->check.errors, ast, "can't reference 'this' in a default argument"); return false; } sym_status_t status; ast_get(ast, stringtab("this"), &status); if(status == SYM_CONSUMED) { ast_error(opt->check.errors, ast, "can't use a consumed 'this' in an expression"); return false; } assert(status == SYM_NONE); token_id cap = cap_for_this(t); if(!cap_sendable(cap) && (t->frame->recover != NULL)) { ast_t* parent = ast_parent(ast); if(ast_id(parent) != TK_DOT) cap = TK_TAG; } bool make_arrow = false; if(cap == TK_BOX) { cap = TK_REF; make_arrow = true; } ast_t* type = type_for_this(opt, ast, cap, TK_NONE, false); if(make_arrow) { BUILD(arrow, ast, NODE(TK_ARROW, NODE(TK_THISTYPE) TREE(type))); type = arrow; } // Get the nominal type, which may be the right side of an arrow type. ast_t* nominal; bool arrow; if(ast_id(type) == TK_NOMINAL) { nominal = type; arrow = false; } else { nominal = ast_childidx(type, 1); arrow = true; } ast_t* typeargs = ast_childidx(nominal, 2); ast_t* typearg = ast_child(typeargs); while(typearg != NULL) { if(!expr_nominal(opt, &typearg)) { ast_error(opt->check.errors, ast, "couldn't create a type for 'this'"); ast_free(type); return false; } typearg = ast_sibling(typearg); } if(!expr_nominal(opt, &nominal)) { ast_error(opt->check.errors, ast, "couldn't create a type for 'this'"); ast_free(type); return false; } // Unless this is a field lookup, treat an incomplete `this` as a tag. ast_t* parent = ast_parent(ast); bool incomplete_ok = false; if((ast_id(parent) == TK_DOT) && (ast_child(parent) == ast)) { ast_t* right = ast_sibling(ast); assert(ast_id(right) == TK_ID); ast_t* find = lookup_try(opt, ast, nominal, ast_name(right)); if(find != NULL) { switch(ast_id(find)) { case TK_FVAR: case TK_FLET: case TK_EMBED: incomplete_ok = true; break; default: {} } } } if(!incomplete_ok && is_this_incomplete(t, ast)) { ast_t* tag_type = set_cap_and_ephemeral(nominal, TK_TAG, TK_NONE); ast_replace(&nominal, tag_type); } if(arrow) type = ast_parent(nominal); else type = nominal; ast_settype(ast, type); return true; }