feat(obs): require an explicit borrow to create a reference
`b: &U = a` now fails with a borrow hint; only `&value` (or `&ownedPtr`) / another reference may initialize a reference. Copying a reference extends the original borrow to the new binding.
This commit is contained in:
1 parent
6f45fc016b
commit
e650d8dd45
2 files changed
+36
-4
No files matched your search
@@ -1064,15 +1064,16 @@ novirtual class AssignGenMixin:
|
||||
return self.Gen.TypeGenResolve.ref_kind(annot)
|
||||
|
||||
def _BorrowOwnerName(self, expr: ast.AST*) -> str:
|
||||
"""Borrowed variable of `r: &T = x` / `r: &T = &x`; None when the source is not a binding."""
|
||||
"""Borrowed variable of `r: &T = &x` / `r: &T = &*p`; None when the source is not a binding."""
|
||||
if expr is None: return None
|
||||
if expr.kind() == ast.ASTKind.Name:
|
||||
return (ast.Name*)(expr).id
|
||||
if expr.kind() == ast.ASTKind.UnaryOp:
|
||||
uo_b: ast.UnaryOp* = ast.UnaryOp*(expr)
|
||||
if uo_b.op == ast.OpKind.AddrOf and uo_b.operand is not None \
|
||||
and uo_b.operand.kind() == ast.ASTKind.Name:
|
||||
return (ast.Name*)(uo_b.operand).id
|
||||
if uo_b.op == ast.OpKind.AddrOf:
|
||||
return self._BorrowOwnerName(uo_b.operand)
|
||||
if uo_b.op == ast.OpKind.Deref:
|
||||
return self._BorrowOwnerName(uo_b.operand)
|
||||
return None
|
||||
|
||||
def _move_out_assigned_source(self, trans: HT.Translator*, rhs: ast.AST*,
|
||||
@@ -2746,7 +2747,26 @@ novirtual class AssignGenMixin:
|
||||
rk_decl: int = self._RefKindOf(annot)
|
||||
if rk_decl != 0:
|
||||
self.Gen.VarGen.set_var_ref_kind(trans.SymTab, nm.id, rk_decl)
|
||||
# A reference must be created from an explicit borrow (`&value` / `&*ptr`) or
|
||||
# another reference; `b: &U = a` must not silently take the address.
|
||||
ok_ref: int = 0
|
||||
if aa.value is not None:
|
||||
if aa.value.kind() == ast.ASTKind.UnaryOp:
|
||||
uo_i: ast.UnaryOp* = ast.UnaryOp*(aa.value)
|
||||
if uo_i.op == ast.OpKind.AddrOf: ok_ref = 1
|
||||
elif aa.value.kind() == ast.ASTKind.Name:
|
||||
nm_i: ast.Name* = ast.Name*(aa.value)
|
||||
if nm_i.id is not None and self.Gen.VarGen.var_ref_kind(trans.SymTab, nm_i.id) != 0:
|
||||
ok_ref = 1
|
||||
if ok_ref == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(aa.value, f"a reference must be initialized from a borrow: write `{nm.id}: &T = &value` (or `&ownedPtr`)", "TypeError")
|
||||
owner_b: str = self._BorrowOwnerName(aa.value)
|
||||
if aa.value is not None and aa.value.kind() == ast.ASTKind.Name:
|
||||
# Copying a reference extends the original borrow to the new binding.
|
||||
nm_r: ast.Name* = ast.Name*(aa.value)
|
||||
if nm_r.id is not None:
|
||||
o_ref: str = self.Gen.StmtGen._BorrowOwnerOf(trans, nm_r.id)
|
||||
if o_ref is not None: owner_b = o_ref
|
||||
if owner_b is not None:
|
||||
self.Gen.StmtGen._BorrowAdd(trans, owner_b, nm.id)
|
||||
# Store the annotation's class name; pass pool so generic Subscript yields the mangled name
|
||||
|
||||
@@ -85,6 +85,18 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if trans is None: return 0
|
||||
return _BorrowTableDrop(trans._cur_func, borrower)
|
||||
|
||||
def _BorrowOwnerOf(self, trans: HT.Translator*, borrower: str) -> str:
|
||||
"""Owner borrowed by a reference variable (None when it holds no registered borrow)."""
|
||||
global _g_borrow_count
|
||||
if trans is None or borrower is None or _g_borrow_funcs is None: return None
|
||||
i: int = 0
|
||||
while i < _g_borrow_count:
|
||||
if _g_borrow_funcs[i] == sizet(trans._cur_func) and _g_borrow_borrowers[i] is not None \
|
||||
and string.strcmp(_g_borrow_borrowers[i], borrower) == 0:
|
||||
return _g_borrow_owners[i]
|
||||
i += 1
|
||||
return None
|
||||
|
||||
def _BorrowLiveBorrower(self, trans: HT.Translator*, owner: str) -> str:
|
||||
"""Name of a still-declared reference that borrows `owner` (None when none)."""
|
||||
global _g_borrow_count
|
||||
|
||||
Reference in new issue
Block a user