bool expr_qualify(pass_opt_t* opt, ast_t** astp) { // Left is a postfix expression, right is a typeargs. ast_t* ast = *astp; AST_GET_CHILDREN(ast, left, right); ast_t* type = ast_type(left); assert(ast_id(right) == TK_TYPEARGS); if(is_typecheck_error(type)) return false; switch(ast_id(left)) { case TK_TYPEREF: { // Qualify the type. assert(ast_id(type) == TK_NOMINAL); // If the type isn't polymorphic or the type is already qualified, // sugar .apply(). ast_t* def = names_def(opt, type); ast_t* typeparams = ast_childidx(def, 1); if((ast_id(typeparams) == TK_NONE) || (ast_id(ast_childidx(type, 2)) != TK_NONE)) { if(!expr_nominal(opt, &type)) return false; break; } type = ast_dup(type); ast_t* typeargs = ast_childidx(type, 2); ast_replace(&typeargs, right); ast_settype(ast, type); ast_setid(ast, TK_TYPEREF); return expr_typeref(opt, astp); } case TK_NEWREF: case TK_NEWBEREF: case TK_BEREF: case TK_FUNREF: case TK_NEWAPP: case TK_BEAPP: case TK_FUNAPP: { // Qualify the function. assert(ast_id(type) == TK_FUNTYPE); ast_t* typeparams = ast_childidx(type, 1); if(!reify_defaults(typeparams, right, true, opt)) return false; if(!check_constraints(left, typeparams, right, true, opt)) return false; type = reify(type, typeparams, right, opt); typeparams = ast_childidx(type, 1); ast_replace(&typeparams, ast_from(typeparams, TK_NONE)); ast_settype(ast, type); ast_setid(ast, ast_id(left)); ast_inheritflags(ast); return true; } default: {} } // Sugar .apply() ast_t* dot = ast_from(left, TK_DOT); ast_add(dot, ast_from_string(left, "apply")); ast_swap(left, dot); ast_add(dot, left); if(!expr_dot(opt, &dot)) return false; return expr_qualify(opt, astp); }
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; }
bool expr_typeref(pass_opt_t* opt, ast_t** astp) { ast_t* ast = *astp; assert(ast_id(ast) == TK_TYPEREF); ast_t* type = ast_type(ast); if(is_typecheck_error(type)) return false; switch(ast_id(ast_parent(ast))) { case TK_QUALIFY: // Doesn't have to be valid yet. break; case TK_DOT: // Has to be valid. if(!expr_nominal(opt, &type)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } break; case TK_CALL: { // Has to be valid. if(!expr_nominal(opt, &type)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } // Transform to a default constructor. ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_from_string(ast, "create")); ast_swap(ast, dot); *astp = dot; ast_add(dot, ast); if(!expr_dot(opt, astp)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } ast_t* ast = *astp; // If the default constructor has no parameters, transform to an apply // call. if((ast_id(ast) == TK_NEWREF) || (ast_id(ast) == TK_NEWBEREF)) { type = ast_type(ast); if(is_typecheck_error(type)) return false; assert(ast_id(type) == TK_FUNTYPE); AST_GET_CHILDREN(type, cap, typeparams, params, result); if(ast_id(params) == TK_NONE) { // Add a call node. ast_t* call = ast_from(ast, TK_CALL); ast_add(call, ast_from(call, TK_NONE)); // Named ast_add(call, ast_from(call, TK_NONE)); // Positional ast_swap(ast, call); ast_append(call, ast); if(!expr_call(opt, &call)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } // Add a dot node. ast_t* apply = ast_from(call, TK_DOT); ast_add(apply, ast_from_string(call, "apply")); ast_swap(call, apply); ast_add(apply, call); if(!expr_dot(opt, &apply)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } } } return true; } default: { // Has to be valid. if(!expr_nominal(opt, &type)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } // Transform to a default constructor. ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_from_string(ast, "create")); ast_swap(ast, dot); ast_add(dot, ast); // Call the default constructor with no arguments. ast_t* call = ast_from(ast, TK_CALL); ast_swap(dot, call); ast_add(call, dot); // Receiver comes last. ast_add(call, ast_from(ast, TK_NONE)); // Named args. ast_add(call, ast_from(ast, TK_NONE)); // Positional args. *astp = call; if(!expr_dot(opt, &dot)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } if(!expr_call(opt, astp)) { ast_settype(ast, ast_from(type, TK_ERRORTYPE)); ast_free_unattached(type); return false; } break; } } return true; }
ast_result_t pass_expr(ast_t** astp, pass_opt_t* options) { typecheck_t* t = &options->check; ast_t* ast = *astp; bool r = true; switch(ast_id(ast)) { case TK_NOMINAL: r = expr_nominal(options, astp); break; case TK_FVAR: case TK_FLET: case TK_PARAM: r = expr_field(options, ast); break; case TK_NEW: case TK_BE: case TK_FUN: r = expr_fun(options, ast); break; case TK_SEQ: r = expr_seq(ast); break; case TK_VAR: case TK_LET: r = expr_local(t, ast); break; case TK_BREAK: r = expr_break(t, ast); break; case TK_CONTINUE: r = expr_continue(t, ast); break; case TK_RETURN: r = expr_return(options, ast); break; case TK_IS: case TK_ISNT: r = expr_identity(options, ast); break; case TK_ASSIGN: r = expr_assign(options, ast); break; case TK_CONSUME: r = expr_consume(t, ast); break; case TK_RECOVER: r = expr_recover(ast); break; case TK_DOT: r = expr_dot(options, astp); break; case TK_TILDE: r = expr_tilde(options, astp); break; case TK_QUALIFY: r = expr_qualify(options, astp); break; case TK_CALL: r = expr_call(options, astp); break; case TK_IF: r = expr_if(options, ast); break; case TK_WHILE: r = expr_while(options, ast); break; case TK_REPEAT: r = expr_repeat(options, ast); break; case TK_TRY_NO_CHECK: case TK_TRY: r = expr_try(options, ast); break; case TK_MATCH: r = expr_match(options, ast); break; case TK_CASES: r = expr_cases(ast); break; case TK_CASE: r = expr_case(options, ast); break; case TK_TUPLE: r = expr_tuple(ast); break; case TK_ARRAY: r = expr_array(options, astp); break; case TK_REFERENCE: r = expr_reference(options, astp); break; case TK_THIS: r = expr_this(options, ast); break; case TK_TRUE: case TK_FALSE: r = expr_literal(options, ast, "Bool"); break; case TK_ERROR: r = expr_error(ast); break; case TK_COMPILER_INTRINSIC: r = expr_compiler_intrinsic(t, ast); break; case TK_POSITIONALARGS: case TK_NAMEDARGS: case TK_NAMEDARG: case TK_UPDATEARG: ast_inheritflags(ast); break; case TK_AMP: r = expr_addressof(options, ast); break; case TK_DONTCARE: r = expr_dontcare(ast); break; case TK_INT: // Integer literals can be integers or floats make_literal_type(ast); break; case TK_FLOAT: make_literal_type(ast); break; case TK_STRING: if(ast_id(ast_parent(ast)) == TK_PACKAGE) return AST_OK; r = expr_literal(options, ast, "String"); break; case TK_FFICALL: return expr_ffi(options, ast); default: {} } if(!r) { assert(get_error_count() > 0); return AST_ERROR; } // Can't use ast here, it might have changed symtab_t* symtab = ast_get_symtab(*astp); if(symtab != NULL && !symtab_check_all_defined(symtab)) return AST_ERROR; return AST_OK; }