bool expr_while(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, cond, body, else_clause); ast_t* cond_type = ast_type(cond); ast_t* body_type = ast_type(body); ast_t* else_type = ast_type(else_clause); if(is_typecheck_error(cond_type)) return false; if(!is_bool(cond_type)) { ast_error(cond, "condition must be a Bool"); return false; } if(is_typecheck_error(body_type) || is_typecheck_error(else_type)) return false; // All consumes have to be in scope when the loop body finishes. if(!ast_all_consumes_in_scope(body, body)) return false; // Union with any existing type due to a break expression. ast_t* type = ast_type(ast); // No symbol status is inherited from the loop body. Nothing from outside the // loop body can be consumed, and definitions in the body may not occur. if(!is_control_type(body_type)) type = control_type_add_branch(type, body); if(!is_control_type(else_type)) { type = control_type_add_branch(type, else_clause); ast_inheritbranch(ast, body); // Use a branch count of two instead of one. This means we will pick up any // consumes, but not any definitions, since definitions may not occur. ast_consolidate_branches(ast, 2); } if(type == NULL) type = ast_from(ast, TK_WHILE); ast_settype(ast, type); ast_inheritflags(ast); literal_unify_control(ast, opt); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), ast); return true; }
static bool member_access(typecheck_t* t, ast_t* ast, bool partial) { // Left is a postfix expression, right is an id. AST_GET_CHILDREN(ast, left, right); assert(ast_id(right) == TK_ID); ast_t* type = ast_type(left); if(is_typecheck_error(type)) return false; ast_t* find = lookup(t, ast, type, ast_name(right)); if(find == NULL) return false; bool ret = true; switch(ast_id(find)) { case TK_TYPEPARAM: ast_error(right, "can't look up a typeparam on an expression"); ret = false; break; case TK_FVAR: if(!expr_fieldref(t, ast, find, TK_FVARREF)) return false; break; case TK_FLET: if(!expr_fieldref(t, ast, find, TK_FLETREF)) return false; break; case TK_NEW: case TK_BE: case TK_FUN: ret = method_access(ast, find); break; default: assert(0); ret = false; break; } ast_free_unattached(find); if(!partial) ast_inheritflags(ast); return ret; }
// Process the method provided to the given entity. // Stage 2. static bool provided_methods(ast_t* entity) { assert(entity != NULL); ast_t* provides = ast_childidx(entity, 3); ast_t* entity_members = ast_childidx(entity, 4); ast_t* last_member = ast_childlast(entity_members); bool r = true; // Run through our provides list for(ast_t* p = ast_child(provides); p != NULL; p = ast_sibling(p)) { ast_t* trait_def = (ast_t*)ast_data(p); assert(trait_def != NULL); ast_t* type_args = ast_childidx(p, 2); ast_t* type_params = ast_childidx(trait_def, 1); ast_t* members = ast_childidx(trait_def, 4); // Run through the methods of each provided type for(ast_t* m = ast_child(members); m != NULL; m = ast_sibling(m)) { // Check behaviour compatability if(ast_id(m) == TK_BE) { if(ast_id(entity) == TK_PRIMITIVE || ast_id(entity) == TK_CLASS) { ast_error(entity, "%s can't provide traits that have behaviours", (ast_id(entity) == TK_CLASS) ? "classes" : "primitives"); r = false; continue; } } if(is_method(m)) { // We have a provided method // Reify the method with the type parameters from trait definition and type // arguments from trait reference ast_t* reified = reify(type_args, m, type_params, type_args); if(reified == NULL) // Reification error, already reported return false; if(!provided_method(entity, reified, m, &last_member)) r = false; } } } return r; }
static ast_result_t syntax_thistype(typecheck_t* t, ast_t* ast) { assert(ast != NULL); ast_t* parent = ast_parent(ast); assert(parent != NULL); ast_result_t r = AST_OK; if(ast_id(parent) != TK_ARROW) { ast_error(ast, "in a type, 'this' can only be used as a viewpoint"); r = AST_ERROR; } if(t->frame->method == NULL) { ast_error(ast, "can only use 'this' for a viewpoint in a method"); r = AST_ERROR; } return r; }
static bool check_fields_defined(ast_t* ast) { assert(ast_id(ast) == TK_NEW); ast_t* members = ast_parent(ast); ast_t* member = ast_child(members); bool result = true; while(member != NULL) { switch(ast_id(member)) { case TK_FVAR: case TK_FLET: case TK_EMBED: { sym_status_t status; ast_t* id = ast_child(member); ast_t* def = ast_get(ast, ast_name(id), &status); if((def != member) || (status != SYM_DEFINED)) { ast_error(def, "field left undefined in constructor"); result = false; } break; } default: {} } member = ast_sibling(member); } if(!result) ast_error(ast, "constructor with undefined fields is here"); return result; }
static bool check_finaliser(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, cap, id, typeparams, params, result, can_error, body); if(strcmp(ast_name(id), "_final")) return true; bool ok = true; if((ast_id(opt->check.frame->type) == TK_PRIMITIVE) && (ast_id(ast_childidx(opt->check.frame->type, 1)) != TK_NONE)) { ast_error(opt->check.errors, ast, "a primitive with type parameters cannot have a _final"); ok = false; } if(ast_id(ast) != TK_FUN) { ast_error(opt->check.errors, ast, "_final must be a function"); ok = false; } if(ast_id(cap) != TK_BOX) { ast_error(opt->check.errors, cap, "_final must be box"); ok = false; } if(ast_id(typeparams) != TK_NONE) { ast_error(opt->check.errors, typeparams, "_final must not be polymorphic"); ok = false; } if(ast_childcount(params) != 0) { ast_error(opt->check.errors, params, "_final must not have parameters"); ok = false; } if(!is_none(result)) { ast_error(opt->check.errors, result, "_final must return None"); ok = false; } if(ast_id(can_error) != TK_NONE) { ast_error(opt->check.errors, can_error, "_final cannot raise an error"); ok = false; } return ok; }
// Resolve the default body to use for the given method, if any. // Return the body to use, NULL if none found or BODY_ERROR on error. static ast_t* resolve_default_body(ast_t* entity, ast_t* method, method_t* info, const char* name, bool concrete) { assert(entity != NULL); assert(method != NULL); assert(info != NULL); assert(name != NULL); if(info->default_body_src_2 != NULL) { // Ambiguous body, store info for use by any types that provide this one assert(info->default_body_src_1 != NULL); ast_t* err = ast_childidx(method, 5); ast_t* body = ast_childidx(method, 6); // Ditch whatever body we have, if any ast_erase(body); ast_setdata(body, info->default_body_src_1); ast_setdata(err, info->default_body_src_2); if(!concrete) return NULL; ast_error(entity, "multiple possible bodies for method %s, local " "disambiguation required", name); ast_error(info->default_body_src_1, "one possible body here"); ast_error(info->default_body_src_2, "another possible body here"); return BODY_ERROR; } if(info->default_body_src_1 == NULL) // No default body found return NULL; // We have a default body, use it assert(info->reified_default != NULL); info->body_donor = (ast_t*)ast_data(info->default_body_src_1); return ast_childidx(info->reified_default, 6); }
static bool check_nonsendable_recover(pass_opt_t* opt, ast_t* ast) { if(opt->check.frame->recover != NULL) { AST_GET_CHILDREN(ast, positional, namedargs, question, lhs); ast_t* type = ast_type(lhs); AST_GET_CHILDREN(type, cap, typeparams, params, result); // If the method is tag, the call is always safe. if(ast_id(cap) == TK_TAG) return true; ast_t* receiver = ast_child(lhs); // Dig through function qualification. if((ast_id(receiver) == TK_FUNREF) || (ast_id(receiver) == TK_FUNAPP) || (ast_id(receiver) == TK_FUNCHAIN)) receiver = ast_child(receiver); if(!is_receiver_safe(&opt->check, receiver)) { ast_t* arg = ast_child(positional); bool args_sendable = true; while(arg != NULL) { if(ast_id(arg) != TK_NONE) { // Don't typecheck arg_type, this was already done in // auto_recover_call. ast_t* arg_type = ast_type(arg); if(!sendable(arg_type)) { args_sendable = false; break; } } arg = ast_sibling(arg); } if(!args_sendable || !sendable(result)) { ast_error(opt->check.errors, ast, "can't call method on non-sendable " "object inside of a recover expression"); ast_error_continue(opt->check.errors, ast, "this would be possible if " "the arguments and return value were all sendable"); return false; } } } return true; }
static ast_result_t syntax_compile_intrinsic(pass_opt_t* opt, ast_t* ast) { ast_t* parent = ast_parent(ast); assert(ast_id(parent) == TK_SEQ); ast_t* method = ast_parent(parent); switch(ast_id(method)) { case TK_NEW: case TK_BE: case TK_FUN: // OK break; default: ast_error(opt->check.errors, ast, "a compile intrinsic must be a method body"); return AST_ERROR; } ast_t* child = ast_child(parent); // Allow a docstring before the compile_instrinsic. if(ast_id(child) == TK_STRING) child = ast_sibling(child); // Compile intrinsic has a value child, but it must be empty ast_t* value = ast_child(ast); if(child != ast || ast_sibling(child) != NULL || ast_id(value) != TK_NONE) { ast_error(opt->check.errors, ast, "a compile intrinsic must be the entire body"); return AST_ERROR; } return AST_OK; }
static ast_result_t syntax_ellipsis(ast_t* ast) { assert(ast != NULL); ast_result_t r = AST_OK; ast_t* fn = ast_parent(ast_parent(ast)); assert(fn != NULL); if(ast_id(fn) != TK_FFIDECL) { ast_error(ast, "... may only appear in FFI declarations"); r = AST_ERROR; } if(ast_sibling(ast) != NULL) { ast_error(ast, "... must be the last parameter"); r = AST_ERROR; } return r; }
ast_t* type_builtin(pass_opt_t* opt, ast_t* from, const char* name) { ast_t* ast = type_base(from, NULL, name); if(!names_nominal(opt, from, &ast, false)) { ast_error(from, "unable to validate '%s'", name); ast_free(ast); return NULL; } return ast; }
static ast_result_t syntax_lambda_capture(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, name, type, value); if(ast_id(type) != TK_NONE && ast_id(value) == TK_NONE) { ast_error(opt->check.errors, ast, "value missing for lambda expression " "capture (cannot specify type without value)"); return AST_ERROR; } return AST_OK; }
bool expr_dontcare(ast_t* ast) { // We are a tuple element. That tuple must either be a pattern or the LHS // of an assignment. It can be embedded in other tuples, which may appear // in sequences. ast_t* tuple = ast_parent(ast); assert(ast_id(tuple) == TK_TUPLE); ast_t* parent = ast_parent(tuple); while((ast_id(parent) == TK_TUPLE) || (ast_id(parent) == TK_SEQ)) { tuple = parent; parent = ast_parent(tuple); } switch(ast_id(parent)) { case TK_ASSIGN: { AST_GET_CHILDREN(parent, right, left); if(tuple == left) { ast_settype(ast, ast); return true; } break; } case TK_CASE: { AST_GET_CHILDREN(parent, pattern, guard, body); if(tuple == pattern) { ast_settype(ast, ast); return true; } break; } default: {} } ast_error(ast, "the don't care token can only appear in a tuple, either on " "the LHS of an assignment or in a pattern"); return false; }
static ast_result_t syntax_ffi(ast_t* ast, bool return_optional) { assert(ast != NULL); AST_GET_CHILDREN(ast, id, typeargs, args, named_args); ast_result_t r = AST_OK; // We don't check FFI names are legal, if the lexer allows it so do we if((ast_child(typeargs) == NULL && !return_optional) || ast_childidx(typeargs, 1) != NULL) { ast_error(typeargs, "FFIs must specify a single return type"); r = AST_ERROR; } for(ast_t* p = ast_child(args); p != NULL; p = ast_sibling(p)) { if(ast_id(p) == TK_PARAM) { ast_t* def_val = ast_childidx(p, 2); assert(def_val != NULL); if(ast_id(def_val) != TK_NONE) { ast_error(def_val, "FFIs parameters cannot have default values"); r = AST_ERROR; } } } if(ast_id(named_args) != TK_NONE) { ast_error(typeargs, "FFIs cannot take named arguments"); r = AST_ERROR; } return r; }
ast_result_t flatten_typeparamref(pass_opt_t* opt, ast_t* ast) { ast_t* cap_ast = cap_fetch(ast); token_id cap = ast_id(cap_ast); typeparam_set_cap(ast); token_id set_cap = ast_id(cap_ast); if((cap != TK_NONE) && (cap != set_cap)) { ast_t* def = (ast_t*)ast_data(ast); ast_t* constraint = typeparam_constraint(ast); if(constraint != NULL) { ast_error(opt->check.errors, cap_ast, "can't specify a capability on a " "type parameter that differs from the constraint"); ast_error_continue(opt->check.errors, constraint, "constraint definition is here"); if(ast_parent(constraint) != def) { ast_error_continue(opt->check.errors, def, "type parameter definition is here"); } } else { ast_error(opt->check.errors, cap_ast, "a type parameter with no " "constraint can only have #any as its capability"); ast_error_continue(opt->check.errors, def, "type parameter definition is here"); } return AST_ERROR; } return AST_OK; }
static LLVMValueRef dispatch_function(compile_t* c, ast_t* from, gentype_t* g, LLVMValueRef l_value, const char* method_name, ast_t* typeargs) { LLVMValueRef func; if(g->use_type == c->object_ptr) { // Virtual, get the function by selector colour. uint32_t index = genfun_vtable_index(c, g, method_name, typeargs); assert(index != (uint32_t)-1); // Get the function from the vtable. func = gendesc_vtable(c, l_value, index); // Cast to the right function type. LLVMTypeRef f_type = genfun_sig(c, g, method_name, typeargs); if(f_type == NULL) { ast_error(from, "couldn't create a signature for '%s'", method_name); return NULL; } f_type = LLVMPointerType(f_type, 0); func = LLVMBuildBitCast(c->builder, func, f_type, "method"); } else { // Static, get the actual function. func = genfun_proto(c, g, method_name, typeargs); if(func == NULL) { ast_error(from, "couldn't locate '%s'", method_name); return NULL; } } return func; }
bool expr_ffi(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, name, return_typeargs, args, namedargs, question); assert(name != NULL); ast_t* decl; if(!ffi_get_decl(&opt->check, ast, &decl, opt)) return false; if(decl != NULL) // We have a declaration return declared_ffi(opt, ast, decl); // We do not have a declaration for(ast_t* arg = ast_child(args); arg != NULL; arg = ast_sibling(arg)) { ast_t* a_type = ast_type(arg); if((a_type != NULL) && is_type_literal(a_type)) { ast_error(opt->check.errors, arg, "Cannot pass number literals as unchecked FFI arguments"); return false; } } ast_t* return_type = ast_child(return_typeargs); if(return_type == NULL) { ast_error(opt->check.errors, name, "FFIs without declarations must specify return type"); return false; } ast_settype(ast, return_type); return true; }
static bool package_access(pass_opt_t* opt, ast_t** astp) { ast_t* ast = *astp; // Left is a packageref, right is an id. ast_t* left = ast_child(ast); ast_t* right = ast_sibling(left); assert(ast_id(left) == TK_PACKAGEREF); assert(ast_id(right) == TK_ID); // Must be a type in a package. const char* package_name = ast_name(ast_child(left)); ast_t* package = ast_get(left, package_name, NULL); if(package == NULL) { ast_error(opt->check.errors, right, "can't access package '%s'", package_name); return false; } assert(ast_id(package) == TK_PACKAGE); const char* type_name = ast_name(right); ast_t* type = ast_get(package, type_name, NULL); if(type == NULL) { ast_error(opt->check.errors, right, "can't find type '%s' in package '%s'", type_name, package_name); return false; } ast_settype(ast, type_sugar(ast, package_name, type_name)); ast_setid(ast, TK_TYPEREF); return expr_typeref(opt, astp); }
static bool names_applycap(ast_t* ast, ast_t* cap, ast_t* ephemeral) { switch(ast_id(ast)) { case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_TUPLETYPE: { if(ast_id(cap) != TK_NONE) { ast_error(cap, "can't specify a capability for an alias to a type expression"); return false; } if(ast_id(ephemeral) != TK_NONE) { ast_error(ephemeral, "can't specify ephemerality for an alias to a type expression"); return false; } return true; } case TK_NOMINAL: names_applycap_index(ast, cap, ephemeral, 3); return true; case TK_ARROW: return names_applycap(ast_childidx(ast, 1), cap, ephemeral); default: {} } assert(0); return false; }
static LLVMValueRef special_case_platform(compile_t* c, ast_t* ast) { AST_GET_CHILDREN(ast, positional, named, postfix); AST_GET_CHILDREN(postfix, receiver, method); const char* method_name = ast_name(method); bool is_target; if(os_is_target(method_name, c->opt->release, &is_target, c->opt)) return LLVMConstInt(c->ibool, is_target ? 1 : 0, false); ast_error(c->opt->check.errors, ast, "unknown Platform setting"); return NULL; }
static ast_result_t syntax_semi(pass_opt_t* opt, ast_t* ast) { assert(ast_parent(ast) != NULL); assert(ast_id(ast_parent(ast)) == TK_SEQ); if(ast_checkflag(ast, AST_FLAG_BAD_SEMI)) { ast_error(opt->check.errors, ast, "Unexpected semicolon, only use to " "separate expressions on the same line"); return AST_ERROR; } return AST_OK; }
bool expr_local(typecheck_t* t, ast_t* ast) { assert(t != NULL); assert(ast != NULL); AST_GET_CHILDREN(ast, id, type); assert(type != NULL); if(ast_id(type) == TK_NONE) { // No type specified, check we can infer if(!is_assigned_to(ast, false)) { if(t->frame->pattern != NULL) ast_error(ast, "cannot infer type of capture variables"); else ast_error(ast, "locals must specify a type or be assigned a value"); return false; } type = ast_from(id, TK_INFERTYPE); } else if(ast_id(ast) == TK_LET && t->frame->pattern == NULL) { // Let, check we have a value assigned if(!is_assigned_to(ast, false)) { ast_error(ast, "can't declare a let local without assigning to it"); return false; } } ast_settype(id, type); ast_settype(ast, type); return true; }
static bool check_return_type(ast_t* ast) { assert(ast_id(ast) == TK_FUN); AST_GET_CHILDREN(ast, cap, id, typeparams, params, type, can_error, body); ast_t* body_type = ast_type(body); if(is_typecheck_error(body_type)) return false; // The last statement is an error, and we've already checked any return // expressions in the method. if(is_control_type(body_type)) return true; // If it's a compiler intrinsic, ignore it. if(ast_id(body_type) == TK_COMPILER_INTRINSIC) return true; // The body type must match the return type, without subsumption, or an alias // of the body type must be a subtype of the return type. ast_t* a_type = alias(type); ast_t* a_body_type = alias(body_type); bool ok = true; if(!is_subtype(body_type, type) || !is_subtype(a_body_type, a_type)) { ast_t* last = ast_childlast(body); ast_error(last, "function body isn't the result type"); ast_error(type, "function return type: %s", ast_print_type(type)); ast_error(body_type, "function body type: %s", ast_print_type(body_type)); ok = false; } ast_free_unattached(a_type); ast_free_unattached(a_body_type); return ok; }
static ast_result_t syntax_thistype(pass_opt_t* opt, ast_t* ast) { assert(ast != NULL); ast_t* parent = ast_parent(ast); assert(parent != NULL); ast_result_t r = AST_OK; if(ast_id(parent) != TK_ARROW) { ast_error(opt->check.errors, ast, "in a type, 'this' can only be used as a viewpoint"); r = AST_ERROR; } if(opt->check.frame->method == NULL) { ast_error(opt->check.errors, ast, "can only use 'this' for a viewpoint in a method"); r = AST_ERROR; } else { ast_t* cap = ast_child(opt->check.frame->method); switch(ast_id(cap)) { case TK_BOX: case TK_NONE: break; default: ast_error(opt->check.errors, ast, "can only use 'this' for a viewpoint in a box function"); r = AST_ERROR; } } return r; }
// Infer the types of any literals in the pattern of the given case static bool infer_pattern_type(ast_t* pattern, ast_t* match_expr_type, pass_opt_t* opt) { assert(pattern != NULL); assert(match_expr_type != NULL); if(is_type_literal(match_expr_type)) { ast_error(match_expr_type, "cannot infer type for literal match expression"); return false; } return coerce_literals(&pattern, match_expr_type, opt); }
/** * Make sure the definition of something occurs before its use. This is for * both fields and local variable. */ bool def_before_use(pass_opt_t* opt, ast_t* def, ast_t* use, const char* name) { if((ast_line(def) > ast_line(use)) || ((ast_line(def) == ast_line(use)) && (ast_pos(def) > ast_pos(use)))) { ast_error(opt->check.errors, use, "declaration of '%s' appears after use", name); ast_error_continue(opt->check.errors, def, "declaration of '%s' appears here", name); return false; } return true; }
static ast_result_t sugar_module(ast_t* ast) { ast_t* docstring = ast_child(ast); ast_t* package = ast_parent(ast); assert(ast_id(package) == TK_PACKAGE); if(strcmp(package_name(package), "$0") != 0) { // Every module not in builtin has an implicit use builtin command. // Since builtin is always the first package processed it is $0. BUILD(builtin, ast, NODE(TK_USE, NONE STRING(stringtab("builtin")) NONE)); ast_add(ast, builtin); } if((docstring == NULL) || (ast_id(docstring) != TK_STRING)) return AST_OK; ast_t* package_docstring = ast_childlast(package); if(ast_id(package_docstring) == TK_STRING) { ast_error(docstring, "the package already has a docstring"); ast_error(package_docstring, "the existing docstring is here"); return AST_ERROR; } ast_append(package, docstring); ast_remove(docstring); return AST_OK; }
// Check that all the methods in the given list are compatible with the given // method static bool methods_compatible(ast_t* list, ast_t* method, const char* name, ast_t* entity) { assert(list != NULL); assert(method != NULL); assert(name != NULL); assert(entity != NULL); bool r = true; for(ast_t* p = ast_child(list); p != NULL; p = ast_sibling(p)) { if(!is_subtype(method, p)) { ast_error(method, "Clashing type for method %s provided by trait to %s %s", name, ast_get_print(entity), ast_name(ast_child(entity))); ast_error(p, "clashing method here"); r = false; } } return r; }
bool expr_recover(ast_t* ast) { AST_GET_CHILDREN(ast, cap, expr); ast_t* type = ast_type(expr); if(is_typecheck_error(type)) return false; ast_t* r_type = recover_type(type, ast_id(cap)); if(r_type == NULL) { ast_error(ast, "can't recover to this capability"); ast_error(expr, "expression type is %s", ast_print_type(type)); return false; } ast_settype(ast, r_type); ast_inheritflags(ast); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), ast); return true; }
static ast_result_t flatten_noconstraint(typecheck_t* t, ast_t* ast) { if(t->frame->constraint != NULL) { switch(ast_id(ast)) { case TK_TUPLETYPE: ast_error(ast, "tuple types can't be used as constraints"); return AST_ERROR; case TK_ARROW: if(t->frame->method == NULL) { ast_error(ast, "arrow types can't be used as type constraints"); return AST_ERROR; } break; default: {} } } return AST_OK; }