#include "own.h" #include static FeLoc fe_own_no_loc(void) { FeLoc loc; loc.file = 0; loc.line = 0; loc.col = 0; return loc; } static void fe_own_error_note(FeDiags *diags, FeLoc loc, const char *msg, FeLoc note, const char *note_msg) { fe_diag_error(diags, loc, msg); if (note.file) fe_diag_note_src(diags, note, note_msg); } int fe_own_is_copy_type(FeType *type) { unsigned i; if (!type) return 1; if (type->kind == FE_TYPE_OWNED) return 0; if (type->kind == FE_TYPE_REF || type->kind == FE_TYPE_SLICE) return !type->ref_mut; if (type->kind == FE_TYPE_OPTIONAL) return fe_own_is_copy_type(type->elem); if (type->kind == FE_TYPE_ERROR_UNION) return fe_own_is_copy_type(type->error_value); if (type->kind == FE_TYPE_ARRAY) return fe_own_is_copy_type(type->elem); if (type->kind == FE_TYPE_STRUCT) { if (type->has_drop) return 0; for (i = 0; i < type->field_count; ++i) if (!fe_own_is_copy_type(type->fields[i].type)) return 0; } if (type->kind == FE_TYPE_ENUM) { for (i = 0; i < type->variant_count; ++i) { unsigned j; for (j = 0; j < type->variants[i].field_count; ++j) if (!fe_own_is_copy_type(type->variants[i].fields[j].type)) return 0; } } return 1; } int fe_own_is_reference_like(FeType *type) { return type && (type->kind == FE_TYPE_REF || type->kind == FE_TYPE_SLICE || type->kind == FE_TYPE_STR); } static FeNode *fe_own_root_expr(FeNode *expr) { if (!expr) return 0; if (expr->kind == FE_N_IDENT) return expr; if (expr->kind == FE_N_MEMBER || expr->kind == FE_N_INDEX) return fe_own_root_expr(expr->a); if (expr->kind == FE_N_UNARY && expr->text && (strcmp(expr->text, "&") == 0 || strcmp(expr->text, "&mut") == 0)) return fe_own_root_expr(expr->a); return 0; } static int fe_own_expr_has_projection(FeNode *expr) { if (!expr) return 0; if (expr->kind == FE_N_MEMBER || expr->kind == FE_N_INDEX) return 1; if (expr->kind == FE_N_UNARY && expr->text && (strcmp(expr->text, "&") == 0 || strcmp(expr->text, "&mut") == 0)) return fe_own_expr_has_projection(expr->a); return 0; } int fe_own_place_from_expr(FeNode *expr, FeOwnPlace *place) { FeNode *root; if (!place) return 0; place->root = 0; place->root_cname = 0; place->projected = 0; root = fe_own_root_expr(expr); if (!root) return 0; place->root = root; place->root_cname = root->cname ? root->cname : root->text; place->projected = fe_own_expr_has_projection(expr); return place->root_cname != 0; } void fe_own_state_init(FeOwnState *state, int initialized) { unsigned i; if (!state) return; state->move = FE_OWN_AVAILABLE; state->initialized = initialized != 0; state->shared = 0; state->exclusive = 0; state->borrow_conflict = 0; state->move_loc = fe_own_no_loc(); state->borrow_loc = fe_own_no_loc(); for (i = 0; i < FE_OWN_FIELD_MAX; ++i) { state->fields[i].name = 0; state->fields[i].shared = 0; state->fields[i].exclusive = 0; state->fields[i].loc = fe_own_no_loc(); } } static int fe_own_require_value(FeDiags *diags, FeOwnState *state, FeLoc loc) { if (state->move == FE_OWN_MOVED) { fe_own_error_note(diags, loc, "use of moved value", state->move_loc, "value was moved here"); return 0; } if (state->move == FE_OWN_MAYBE_MOVED) { fe_diag_error(diags, loc, "use of possibly moved value"); return 0; } if (!state->initialized) { fe_diag_error(diags, loc, "use of uninitialized variable"); return 0; } return 1; } static int fe_own_require_stable_borrow(FeDiags *diags, FeOwnState *state, FeLoc loc) { if (!state->borrow_conflict) return 1; fe_own_error_note(diags, loc, "incompatible borrow state across control-flow paths", state->borrow_loc, "borrow originated here"); return 0; } /* Whole-value state only: what a field access has to get past before it looks at its own entry. `check` reports and decides; `apply` also records. */ static int fe_own_access_whole(FeDiags *diags, FeOwnState *state, FeOwnAccessKind access, FeLoc loc); static int fe_own_access_whole_check(FeDiags *diags, FeOwnState *state, FeOwnAccessKind access, FeLoc loc); /* The entry for this field, or null. `make` asks for one to be created. */ static FeOwnField *fe_own_field_slot(FeOwnState *state, const char *field, int make) { unsigned i; unsigned free_slot = FE_OWN_FIELD_MAX; if (!state || !field) return 0; for (i = 0; i < FE_OWN_FIELD_MAX; ++i) { if (state->fields[i].name && strcmp(state->fields[i].name, field) == 0) return &state->fields[i]; if (!state->fields[i].name && free_slot == FE_OWN_FIELD_MAX) free_slot = i; } if (!make || free_slot == FE_OWN_FIELD_MAX) return 0; state->fields[free_slot].name = field; state->fields[free_slot].shared = 0; state->fields[free_slot].exclusive = 0; state->fields[free_slot].loc = fe_own_no_loc(); return &state->fields[free_slot]; } /* A live borrow of some field, for the accesses that reach the whole value. */ static const FeOwnField *fe_own_field_live(const FeOwnState *state, int mut_only) { unsigned i; if (!state) return 0; for (i = 0; i < FE_OWN_FIELD_MAX; ++i) { const FeOwnField *f = &state->fields[i]; if (!f->name) continue; if (f->exclusive) return f; if (!mut_only && f->shared) return f; } return 0; } void fe_own_release_shared_field(FeOwnState *state, const char *field) { FeOwnField *f = fe_own_field_slot(state, field, 0); if (!f || !f->shared) { fe_own_release_shared(state); return; } --f->shared; if (!f->shared && !f->exclusive) f->name = 0; } void fe_own_release_exclusive_field(FeOwnState *state, const char *field) { FeOwnField *f = fe_own_field_slot(state, field, 0); if (!f || !f->exclusive) { fe_own_release_exclusive(state); return; } f->exclusive = 0; if (!f->shared) f->name = 0; } int fe_own_access(FeDiags *diags, FeOwnState *state, FeOwnAccessKind access, FeLoc loc) { return fe_own_access_field(diags, state, 0, access, loc); } int fe_own_access_field(FeDiags *diags, FeOwnState *state, const char *field, FeOwnAccessKind access, FeLoc loc) { FeOwnField *f; const FeOwnField *other; if (!state) return 0; if (access == FE_OWN_PROJECTION) return 1; if (!field) { /* Reaching the whole value: a borrow of any part of it is in the way. A shared borrow of a field still lets the whole be read. */ other = fe_own_field_live(state, access == FE_OWN_READ); if (other) { fe_own_error_note(diags, loc, access == FE_OWN_WRITE ? "cannot write while value is borrowed" : access == FE_OWN_MOVE ? "cannot move while value is borrowed" : access == FE_OWN_READ ? "cannot read directly while value is mutably borrowed" : "cannot borrow while a field of the value is borrowed", other->loc, "borrow originated here"); return 0; } return fe_own_access_whole(diags, state, access, loc); } /* Reaching one field: a borrow of the whole value is in the way, and so is a borrow of this same field. A borrow of a different field is not. */ if (!fe_own_access_whole_check(diags, state, access, loc)) return 0; f = fe_own_field_slot(state, field, access == FE_OWN_BORROW_SHARED || access == FE_OWN_BORROW_MUT); if (!f) { /* No room left in the table, so this borrow covers the whole value. That reports more than it has to and never less. */ if (access == FE_OWN_BORROW_SHARED || access == FE_OWN_BORROW_MUT) return fe_own_access_whole(diags, state, access, loc); return 1; } switch (access) { case FE_OWN_READ: if (f->exclusive) { fe_own_error_note(diags, loc, "cannot read directly while value is mutably borrowed", f->loc, "mutable borrow originated here"); return 0; } return 1; case FE_OWN_WRITE: case FE_OWN_MOVE: if (f->shared || f->exclusive) { fe_own_error_note(diags, loc, access == FE_OWN_WRITE ? "cannot write while value is borrowed" : "cannot move while value is borrowed", f->loc, "borrow originated here"); return 0; } return 1; case FE_OWN_BORROW_SHARED: if (f->exclusive) { fe_own_error_note(diags, loc, "cannot create shared borrow while mutable borrow is live", f->loc, "mutable borrow originated here"); return 0; } if (!f->shared) f->loc = loc; ++f->shared; return 1; case FE_OWN_BORROW_MUT: if (f->shared || f->exclusive) { fe_own_error_note(diags, loc, "cannot create mutable borrow while another borrow is live", f->loc, "existing borrow originated here"); return 0; } f->exclusive = 1; f->loc = loc; return 1; default: break; } return 1; } static int fe_own_access_whole_check(FeDiags *diags, FeOwnState *state, FeOwnAccessKind access, FeLoc loc) { if (!fe_own_require_stable_borrow(diags, state, loc)) return 0; if (access == FE_OWN_WRITE) { if (state->shared || state->exclusive) { fe_own_error_note(diags, loc, "cannot write while value is borrowed", state->borrow_loc, "borrow originated here"); return 0; } return 1; } if (!fe_own_require_value(diags, state, loc)) return 0; if (access == FE_OWN_READ || access == FE_OWN_BORROW_SHARED) { if (state->exclusive) { fe_own_error_note(diags, loc, access == FE_OWN_READ ? "cannot read directly while value is mutably borrowed" : "cannot create shared borrow while mutable borrow is live", state->borrow_loc, "mutable borrow originated here"); return 0; } return 1; } if (state->shared || state->exclusive) { fe_own_error_note(diags, loc, access == FE_OWN_MOVE ? "cannot move while value is borrowed" : "cannot create mutable borrow while another borrow is live", state->borrow_loc, "existing borrow originated here"); return 0; } return 1; } static int fe_own_access_whole(FeDiags *diags, FeOwnState *state, FeOwnAccessKind access, FeLoc loc) { if (!state) return 0; if (access == FE_OWN_PROJECTION) return 1; if (!fe_own_require_stable_borrow(diags, state, loc)) return 0; if (access == FE_OWN_WRITE) { if (state->shared || state->exclusive) { fe_own_error_note(diags, loc, "cannot write while value is borrowed", state->borrow_loc, "borrow originated here"); return 0; } state->move = FE_OWN_AVAILABLE; state->initialized = 1; state->move_loc = fe_own_no_loc(); return 1; } if (!fe_own_require_value(diags, state, loc)) return 0; switch (access) { case FE_OWN_READ: if (state->exclusive) { fe_own_error_note(diags, loc, "cannot read directly while value is mutably borrowed", state->borrow_loc, "mutable borrow originated here"); return 0; } return 1; case FE_OWN_MOVE: if (state->shared || state->exclusive) { fe_own_error_note(diags, loc, "cannot move while value is borrowed", state->borrow_loc, "borrow originated here"); return 0; } state->move = FE_OWN_MOVED; state->initialized = 0; state->move_loc = loc; return 1; case FE_OWN_BORROW_SHARED: if (state->exclusive) { fe_own_error_note(diags, loc, "cannot create shared borrow while mutable borrow is live", state->borrow_loc, "mutable borrow originated here"); return 0; } if (!state->shared) state->borrow_loc = loc; ++state->shared; return 1; case FE_OWN_BORROW_MUT: if (state->shared || state->exclusive) { fe_own_error_note(diags, loc, "cannot create mutable borrow while another borrow is live", state->borrow_loc, "existing borrow originated here"); return 0; } state->exclusive = 1; state->borrow_loc = loc; return 1; default: break; } return 1; } int fe_own_call_shared_view(FeDiags *diags, FeOwnState *state, FeLoc loc) { if (!state) return 0; if (!fe_own_require_stable_borrow(diags, state, loc)) return 0; if (!fe_own_require_value(diags, state, loc)) return 0; if (!state->exclusive) { fe_diag_error(diags, loc, "read-only reborrow requires a live mutable borrow"); return 0; } return 1; } void fe_own_release_shared(FeOwnState *state) { if (!state || !state->shared) return; --state->shared; if (!state->shared && !state->exclusive) state->borrow_loc = fe_own_no_loc(); } void fe_own_release_exclusive(FeOwnState *state) { if (!state) return; state->exclusive = 0; if (!state->shared) state->borrow_loc = fe_own_no_loc(); } /* Merging two paths through the code: a borrow that is live on either side is live after, because the checker cannot know which side ran. */ static void fe_own_merge_fields(FeOwnState *out, const FeOwnState *left, const FeOwnState *right) { unsigned i; unsigned j; for (i = 0; i < FE_OWN_FIELD_MAX; ++i) out->fields[i] = left->fields[i]; for (i = 0; i < FE_OWN_FIELD_MAX; ++i) { const FeOwnField *r = &right->fields[i]; if (!r->name) continue; for (j = 0; j < FE_OWN_FIELD_MAX; ++j) { if (out->fields[j].name && strcmp(out->fields[j].name, r->name) != 0) continue; if (!out->fields[j].name) out->fields[j] = *r; else { if (r->shared > out->fields[j].shared) out->fields[j].shared = r->shared; if (r->exclusive && !out->fields[j].exclusive) { out->fields[j].exclusive = 1; out->fields[j].loc = r->loc; } } break; } } } FeOwnState fe_own_merge_state(FeOwnState left, FeOwnState right) { FeOwnState out; fe_own_merge_fields(&out, &left, &right); out.move = left.move == right.move ? left.move : fe_own_merge_move(left.move, right.move); out.initialized = left.initialized && right.initialized; out.shared = left.shared > right.shared ? left.shared : right.shared; out.exclusive = left.exclusive || right.exclusive; out.borrow_conflict = left.borrow_conflict || right.borrow_conflict || (out.shared != 0 && out.exclusive != 0); out.move_loc = left.move != FE_OWN_AVAILABLE ? left.move_loc : right.move_loc; if (left.shared || left.exclusive || left.borrow_conflict) out.borrow_loc = left.borrow_loc; else out.borrow_loc = right.borrow_loc; return out; } int fe_own_state_equal(const FeOwnState *left, const FeOwnState *right) { if (!left || !right) return 0; { unsigned i; for (i = 0; i < FE_OWN_FIELD_MAX; ++i) { const FeOwnField *a = &left->fields[i]; const FeOwnField *b = &right->fields[i]; if (!a->name != !b->name) return 0; if (a->name && strcmp(a->name, b->name) != 0) return 0; if (a->shared != b->shared || a->exclusive != b->exclusive) return 0; } } return left->move == right->move && left->initialized == right->initialized && left->shared == right->shared && left->exclusive == right->exclusive && left->borrow_conflict == right->borrow_conflict; } int fe_own_loop_merge_state(FeOwnState entry, FeOwnState backedge, FeOwnState *merged) { if (!merged) return 0; *merged = fe_own_merge_state(entry, backedge); return fe_own_state_equal(&entry, merged); } FeOwnProvenance fe_own_provenance_static(void) { FeOwnProvenance p; p.kind = FE_OWN_PROV_STATIC; p.param_index = 0; return p; } FeOwnProvenance fe_own_provenance_param(unsigned param_index) { FeOwnProvenance p; p.kind = FE_OWN_PROV_PARAM; p.param_index = param_index; return p; } FeOwnProvenance fe_own_merge_provenance(FeOwnProvenance left, FeOwnProvenance right) { FeOwnProvenance invalid; invalid.kind = FE_OWN_PROV_INVALID; invalid.param_index = 0; if (left.kind == FE_OWN_PROV_INVALID || right.kind == FE_OWN_PROV_INVALID) return invalid; if (left.kind == FE_OWN_PROV_STATIC) return right; if (right.kind == FE_OWN_PROV_STATIC) return left; if (left.param_index == right.param_index) return left; return invalid; } void fe_own_liveness_init(FeOwnLiveness *live, FeArena *arena) { if (!live) return; live->arena = arena; live->items = 0; live->count = 0; live->capacity = 0; live->ordinal = 0; } static FeOwnLastUse *fe_own_live_find(FeOwnLiveness *live, const char *cname) { unsigned i; if (!live || !cname) return 0; for (i = 0; i < live->count; ++i) if (live->items[i].cname == cname || strcmp(live->items[i].cname, cname) == 0) return &live->items[i]; return 0; } static int fe_own_live_add(FeOwnLiveness *live, FeNode *decl) { FeOwnLastUse *items; FeOwnLastUse *slot; unsigned capacity; const char *cname; if (!live || !decl || !live->arena) return 1; cname = decl->cname ? decl->cname : decl->text; if (!cname || fe_own_live_find(live, cname)) return 1; if (live->count == live->capacity) { capacity = live->capacity ? live->capacity * 2U : 8U; items = (FeOwnLastUse *)fe_arena_alloc(live->arena, capacity * sizeof(FeOwnLastUse)); if (!items) return 0; if (live->items) memcpy(items, live->items, live->count * sizeof(FeOwnLastUse)); live->items = items; live->capacity = capacity; } slot = &live->items[live->count++]; slot->cname = cname; slot->decl = decl; slot->last_node = 0; slot->last_ordinal = 0; slot->defer_extended = 0; return 1; } static unsigned long fe_own_node_weight(FeNode *node); static unsigned long fe_own_list_weight(FeNode *node) { unsigned long count; count = 0; while (node) { count += fe_own_node_weight(node); node = node->next; } return count; } static unsigned long fe_own_node_weight(FeNode *node) { unsigned long count; if (!node) return 0; count = 1; count += fe_own_node_weight(node->a); count += fe_own_node_weight(node->b); count += fe_own_node_weight(node->c); count += fe_own_list_weight(node->children); return count; } static int fe_own_live_visit(FeOwnLiveness *live, FeNode *node, unsigned long block_end, unsigned long defer_until); static int fe_own_live_visit_list(FeOwnLiveness *live, FeNode *node, unsigned long block_end, unsigned long defer_until) { while (node) { if (!fe_own_live_visit(live, node, block_end, defer_until)) return 0; node = node->next; } return 1; } static int fe_own_live_visit(FeOwnLiveness *live, FeNode *node, unsigned long block_end, unsigned long defer_until) { FeOwnLastUse *slot; unsigned long end; unsigned long effective; if (!node) return 1; ++live->ordinal; if (node->kind == FE_N_IDENT) { slot = fe_own_live_find(live, node->cname ? node->cname : node->text); if (slot) { effective = defer_until ? defer_until : live->ordinal; if (effective >= slot->last_ordinal) { slot->last_ordinal = effective; slot->last_node = node; if (defer_until) slot->defer_extended = 1; } } return 1; } if (node->kind == FE_N_BLOCK) { end = live->ordinal + fe_own_list_weight(node->children); return fe_own_live_visit_list(live, node->children, end, defer_until); } if (node->kind == FE_N_DEFER) { effective = defer_until ? defer_until : block_end; return fe_own_live_visit(live, node->a, block_end, effective); } if (!fe_own_live_visit(live, node->a, block_end, defer_until)) return 0; if (!fe_own_live_visit(live, node->b, block_end, defer_until)) return 0; if (!fe_own_live_visit(live, node->c, block_end, defer_until)) return 0; if (!fe_own_live_visit_list(live, node->children, block_end, defer_until)) return 0; if (node->kind == FE_N_LET || node->kind == FE_N_VAR || node->kind == FE_N_CONST) return fe_own_live_add(live, node); return 1; } int fe_own_collect_last_uses(FeOwnLiveness *live, FeNode *fn) { FeNode *param; if (!live || !fn || fn->kind != FE_N_FN) return 0; live->items = 0; live->count = 0; live->capacity = 0; live->ordinal = 0; for (param = fn->a ? fn->a->children : 0; param; param = param->next) if (!fe_own_live_add(live, param)) return 0; return fe_own_live_visit(live, fn->c, 0, 0); } const FeOwnLastUse *fe_own_last_use(const FeOwnLiveness *live, const char *cname) { unsigned i; if (!live || !cname) return 0; for (i = 0; i < live->count; ++i) if (live->items[i].cname == cname || strcmp(live->items[i].cname, cname) == 0) return &live->items[i]; return 0; } static int fe_own_replace_unwrap(FeNode *expr) { FeNode *call; FeNode *member; if (!expr || expr->kind != FE_N_MEMBER || !expr->text || strcmp(expr->text,".?") != 0) return 0; call=expr->a; if (!call || call->kind != FE_N_CALL || !call->a || call->a->kind != FE_N_MEMBER) return 0; member=call->a; return member->a && member->a->kind==FE_N_IDENT && member->a->text && strcmp(member->a->text,"mem")==0 && member->b && member->b->text && strcmp(member->b->text,"replace")==0; } void fe_own_mark_consumed(FeDiags *diags, int *state, FeNode *decl, FeNode *expr, FeType *type, int in_defer) { if (!expr || !type || fe_own_is_copy_type(type)) return; if (expr->kind == FE_N_INDEX && type->kind == FE_TYPE_SLICE && (expr->c || !expr->b)) return; if ((expr->kind == FE_N_MEMBER || expr->kind == FE_N_INDEX) && !fe_own_replace_unwrap(expr)) { fe_diag_error(diags, expr->loc, "cannot move a non-Copy value out of a projection; use mem.replace"); return; } if (expr->kind != FE_N_IDENT || !state) return; if (in_defer) { if (decl) decl->flags |= FE_OWN_NODE_DEFER_CAPTURE; return; } *state = FE_OWN_MOVED; expr->flags |= FE_OWN_NODE_CONSUMED; } void fe_own_check_use(FeDiags *diags, int state, FeLoc loc) { if (state == FE_OWN_MOVED) fe_diag_error(diags, loc, "use of moved value"); else if (state == FE_OWN_MAYBE_MOVED) fe_diag_error(diags, loc, "use of possibly moved value"); } int fe_own_merge_move(int left, int right) { if (left == FE_OWN_MOVED && right == FE_OWN_MOVED) return FE_OWN_MOVED; if (left != FE_OWN_AVAILABLE || right != FE_OWN_AVAILABLE) return FE_OWN_MAYBE_MOVED; return FE_OWN_AVAILABLE; } int fe_own_loop_entry(int before, int after) { if (before == after) return before; return FE_OWN_MAYBE_MOVED; } int fe_own_loop_exit(int state, int after) { if (after != FE_OWN_AVAILABLE) return FE_OWN_MAYBE_MOVED; return state; }