Borrowed locals, calls and fixes
Borrowed locals
A local is borrowed when it is ref r = x;, var p = &x;, a typed T* or a ref T r local, or a var that takes a native pointer from a call. A native pointer to a handle type is not borrowed. A pointer field of a native class with a weak source is a checked pointer, not a borrowed value (see “Pointer fields” in borrowing.md). A lambda that uses a borrowed local gives the capture error.
A ref local has more rules about its lifetime. A ref cannot bind to a temporary. A ref to a container element must not live over a shape change of the container, such as push_back. A ref cannot cross an await. A ref return must return a place that outlives the call. These rules are always errors. See “Lifetime of a ref” in Type design.
using "Actor.h"; using "Armory.h";
class Scan : Actor {
void Run(ref Armory a, ref Vec3 pos) {
var p = &pos; // borrowed: address
ref r = pos.x; // borrowed: reference
var best = a.Best(); // borrowed: native Weapon*
var copy = r; // a value ...
var ok = () => { var v = copy; }; // ... so a lambda can capture it
}
}
Known gaps of the checker: a call can still store its borrowed argument in a collection that a field holds, for example items.push_back(&a) where items is a field of a pointer collection type. The checker does not follow a native call into the callee. a lambda that captures an iterator (var it = xs.begin();) or an std.optional<T&> is not found.
Borrowed values in calls
A borrowed value is valid for the whole call. Every call form takes it as an argument. The callee must not keep it.
| Call form | Example |
|---|---|
| Native method, static function, free function | a.Use(&x), Util.Sum(r), Distance(v, *p) |
| Script method, also through a handle | Show(v), buddy.Show(&x) |
Constructor, new, base(...) |
new $Hero(&x), Hero(ref Vec3 r) : base(r) |
| Lambda call | f(v), g(&x) |
| Nested call | f(g(&x)) |
| Choice, cast, dereference | f(c ? &a : &b), f((Vec3*)&a), f(*(c ? &a : &b)) |
| Parts | f(&a.field), f(&arr[i]), f(&*p) |
| Borrowed names | f(p), f(r), a foreach variable, a catch variable |
var p = c ? &a : &b; and var q = (Vec3*)&a; are borrowed locals, like var p = &a;.
A call can return a pointer or a reference to its argument. The translator does not follow this alias. A var that takes a native pointer from a call is borrowed. A var that takes a reference from a call is a copy. Do not store the result of such a call.
using "Actor.h"; using "Util.h";
class Probe {
void Take(Vec3* p) { }
void See(ref Vec3 v) { }
float Run(ref Vec3 v, Vec3* p, bool c) {
if (p == null) return 0;
Vec3 a; Vec3 b;
Take(c ? &a : &b); // every call form takes it ...
See(*(c ? &a : &b));
var f = (ref Vec3 w) => { See(w); };
f(v);
return Distance(v, *p);
} // ... but nothing keeps it
}
Escape hatch
hotc --no-strict-borrowed makes the field, return, collection element, &x and pointer lambda capture findings warnings. The backends have strict_borrowed = false. These stay errors: all rules of “Lifetime of a ref” (including a ref lambda capture), a T* of a script class, &h on a handle, this to a T& and a move-only field.
Quick fixes
| Error starts with | Write instead |
|---|---|
"&" is not a type suffix |
ref T x for a parameter or a local. A field cannot be a reference: use T* or a value |
a field cannot hold a pointer to the value type |
A value T, or a pointer to the object that owns it |
a pointer field is already a weak reference |
Remove [weak] |
a field, a return value, a collection element |
A value T, a handle, or a copy such as std.string. For a field of a native class: add a weak source (enable_shared_from_this or a stub with handle) and keep T* |
a '&' value cannot be stored |
Pass &x to the call, or store a copy of x |
binds to a temporary value |
var r = ...; to copy the value |
can move or remove the elements |
var r = ...;, or end the ref before the call |
is live across 'await' |
Copy the value before the await |
the lambda captures |
Copy first: var c = r;, then use c |
field of move-only type |
A handle: a stub with handle |
'&' on the handle |
h, &*h or *h |
a pointer to the script class |
$Hero |
'this' cannot bind |
$Hero me = this; |