Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e698c73
own passes: pin the keep backstop, the 21.2 message and the shared-ta…
ASDAlexander77 Oct 23, 2026
cf8d32e
own passes: the @lends index >= 64 error names the parameter (M4)
ASDAlexander77 Oct 23, 2026
dfa615b
own passes: one placeBase for the place walkers, one MLIRTypeHelper c…
ASDAlexander77 Oct 23, 2026
eccfb27
own passes: OwnershipInferencePass::analyze in named steps (I1)
ASDAlexander77 Oct 23, 2026
1541ae3
own passes: placeOnUnmovedEdges in named steps (I1)
ASDAlexander77 Oct 23, 2026
19b847b
own passes: collectInterfaceFamilies in named steps (I1)
ASDAlexander77 Oct 23, 2026
cf19926
own passes: OwnershipSignaturePass into OwnershipSignaturePass.h (I1)
ASDAlexander77 Oct 23, 2026
f826628
own passes: OwnershipSignatureGlobals.cpp out of OwnershipSignaturePa…
ASDAlexander77 Oct 23, 2026
fccc71b
own passes: OwnershipSignatureInterfaces.cpp out of OwnershipSignatur…
ASDAlexander77 Oct 23, 2026
c4f6e77
own passes: OwnershipSignatureResolve.cpp out of OwnershipSignaturePa…
ASDAlexander77 Oct 23, 2026
a71b953
own passes: OwnershipSignatureClosedWorld.cpp out of OwnershipSignatu…
ASDAlexander77 Oct 23, 2026
f2da2ca
own passes: OwnershipSignatureGenerators.cpp out of OwnershipSignatur…
ASDAlexander77 Oct 23, 2026
7bfebf6
own passes: OwnershipSignatureCallbacks.cpp out of OwnershipSignature…
ASDAlexander77 Oct 23, 2026
a424ff9
own passes: OwnershipSignatureRecords.cpp out of OwnershipSignaturePa…
ASDAlexander77 Oct 23, 2026
989b08a
own passes: OwnershipInferencePass into OwnershipInferencePass.h (I1)
ASDAlexander77 Oct 23, 2026
06e7259
own passes: OwnershipInferenceClosures.cpp out of OwnershipInferenceP…
ASDAlexander77 Oct 23, 2026
98eb693
own passes: OwnershipInferenceKeptParams.cpp out of OwnershipInferenc…
ASDAlexander77 Oct 23, 2026
1a375e9
own passes: OwnershipInferenceBorrowers.cpp out of OwnershipInference…
ASDAlexander77 Oct 23, 2026
67ab92f
own passes: OwnershipInferenceRecords.cpp out of OwnershipInferencePa…
ASDAlexander77 Oct 23, 2026
7df6df8
own passes: OwnershipInferencePlaceReads.cpp out of OwnershipInferenc…
ASDAlexander77 Oct 23, 2026
a4f37d4
own passes: OwnershipInferenceGlobalReads.cpp out of OwnershipInferen…
ASDAlexander77 Oct 23, 2026
ad5bc5b
own passes: OwnershipInferenceMoves.cpp out of OwnershipInferencePass…
ASDAlexander77 Oct 23, 2026
c2fb987
own passes: OwnershipInferenceEdgeReleases.cpp out of OwnershipInfere…
ASDAlexander77 Oct 23, 2026
d39c533
own passes: OwnershipInferenceReadsAfterMove.cpp out of OwnershipInfe…
ASDAlexander77 Oct 23, 2026
4438c05
own passes: OwnershipInferenceDiagnostics.cpp out of OwnershipInferen…
ASDAlexander77 Oct 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1626,7 +1626,7 @@ place, so all four places that report it get the same names. Each message has a
| a global's value, stored, returned or boxed | `'x' borrows the global 'g' and cannot be stored, returned or captured` | `own_err_global_returned` |
| a global's value read into a local | `'x' borrows the global 'g', but -mm=own cannot prove the global keeps its value while 'x' is used: a call or an assignment may overwrite it` | `own_err_global_let_call` |
| a captured variable's value, through its cell | `'x' borrows a captured variable and cannot be stored, returned or captured` | `own_err_captured_returned` |
| an object literal holding a value it does not own outright: a literal nested by value, or one holding a borrowed or `let` field when it is not seen as an interface (§32; since §27, before which any literal given anywhere but an owning local or the result was one) | `an object literal that holds a value it owns is moved only into a local or returned as it is; -mm=own cannot move it here yet` | the interface-typed shapes are borrowing records (§32): `own_err_borrowing_record_*`, `own_literal_moves`, `own_borrowing_record_arg`; `own_err_record_literal_let` (§27) is gone |
| an object literal holding a value it does not own outright: a literal nested by value, or one holding a borrowed or `let` field when it is not seen as an interface (§32; since §27, before which any literal given anywhere but an owning local or the result was one) | `an object literal that holds a value it owns is moved only into a local or returned as it is; -mm=own cannot move it here yet` | `own_err_record_literal_nested` (nested by value), `own_err_record_literal_let_field` (a `let`'s value, not an interface); the interface-typed shapes are borrowing records (§32): `own_err_borrowing_record_*`, `own_literal_moves`, `own_borrowing_record_arg`; `own_err_record_literal_let` (§27) is gone |

The mixed-return message is the largest bucket (17 files). Most are two synthesized helpers:
- `___unbox<string>` (`<string>` of an `any`) returns the box's own string when it holds one, and a
Expand Down Expand Up @@ -2978,7 +2978,9 @@ either:
- or rejected: "the value read from 'G' is kept where its end at an assignment of 'G' cannot be
checked; -mm=own cannot prove it outlives the global's value" - a field of an object, an array,
a global, a callee that keeps it, a capture, a return. A string read is copied where it is kept
(§22), so its keeps are not this check's.
(§22), so its keeps are not this check's. This is a backstop: almost every keep fails an earlier
rule first. One shape is known to reach it alone, `k.w = M.w` with `k: typeof M` (a static kept
through a vtable slot), pinned by `own_err_global_kept_typeof`.

Tests: `own_err_global_let_holder_static` (a static block's loop), `own_err_global_let_destructure`
(`let { v } = { v: K.w }` in a top-level loop) and `own_err_global_let_holder` (a module global),
Expand Down Expand Up @@ -3943,8 +3945,9 @@ is gone; its shape is in `own_borrowing_record_arg`. `own_err_lends_two_params`
of 'pick' names 'a', 'b', but its body returns a borrow of 'a'`. A method's `@lends` reaches its
class's declaration as a 64-bit mask, so since the final review (M4, 2026-10-23) an index past 63 is
an error rather than a borrow an importer silently never learns of: `own_err_lends_index_64` (a
method of 64 parameters lending its last, argument 64), "'@lends' of 'K.pick' names its argument
64, but a method's '@lends' can name only its arguments 0 to 63". The compiler before it accepted
method of 64 parameters lending its last, argument 64), "'@lends' of 'K.pick' names 'p63', but a
method's '@lends' can name only its first 64 arguments, the receiver among them" (the parameter by
its name since the follow-up; it read "names its argument 64" before). The compiler before it accepted
the program; the 63rd parameter (argument 63) is still accepted, under own, rc and gc.

Teeth (temporary `OWN_TEETH` switches, removed): with the per-site routine dropped (every record's
Expand Down
17 changes: 17 additions & 0 deletions tslang/lib/TypeScript/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,24 @@ add_mlir_dialect_library(MLIRTypeScript
RelocateConstantPass.cpp
OwnershipVerifierPass.cpp
OwnershipInferencePass.cpp
OwnershipInferenceBorrowers.cpp
OwnershipInferenceClosures.cpp
OwnershipInferenceDiagnostics.cpp
OwnershipInferenceEdgeReleases.cpp
OwnershipInferenceGlobalReads.cpp
OwnershipInferenceKeptParams.cpp
OwnershipInferenceMoves.cpp
OwnershipInferencePlaceReads.cpp
OwnershipInferenceReadsAfterMove.cpp
OwnershipInferenceRecords.cpp
OwnershipSignaturePass.cpp
OwnershipSignatureCallbacks.cpp
OwnershipSignatureClosedWorld.cpp
OwnershipSignatureGenerators.cpp
OwnershipSignatureGlobals.cpp
OwnershipSignatureInterfaces.cpp
OwnershipSignatureRecords.cpp
OwnershipSignatureResolve.cpp
OwnedReturnConsumptionPass.cpp
StackPromotionPass.cpp
BorrowFactsExportPass.cpp
Expand Down
18 changes: 16 additions & 2 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -12723,8 +12723,22 @@ class MLIRGenImpl
// 64 bits: an index past it would be a borrow an importer never learns of
if (index >= 64)
{
emitError(location) << "'@lends' of '" << funcOp.getSymName() << "' names its argument " << index
<< ", but a method's '@lends' can name only its arguments 0 to 63";
// the parameter by the name `@lends` gave it (processOwnParamMarkers)
auto names = funcOp->getAttrOfType<mlir::ArrayAttr>(OWN_DECLARED_PARAM_NAMES_ATTR_NAME);
auto name = names && index < static_cast<int32_t>(names.size())
? mlir::dyn_cast<mlir::StringAttr>(names[index])
: mlir::StringAttr();
auto diag = emitError(location) << "'@lends' of '" << funcOp.getSymName() << "' names ";
if (name)
{
diag << "'" << name.getValue() << "'";
}
else
{
diag << "its argument " << index;
}

diag << ", but a method's '@lends' can name only its first 64 arguments, the receiver among them";
return false;
}

Expand Down
108 changes: 74 additions & 34 deletions tslang/lib/TypeScript/OwnershipFacts.h
Original file line number Diff line number Diff line change
Expand Up @@ -421,13 +421,60 @@ inline bool isInterfaceField(mlir::Value ref)
return ref.getDefiningOp<mlir_ts::InterfaceSymbolRefOp>() && mlir::isa<mlir_ts::RefType>(ref.getType());
}

// The kinds of place (isPlace), and the value each is reached through (placeBase).
enum class PlaceKind
{
None,
Field, // `o.f`: the object
Element, // `a[i]`: the array
SharedValue, // `s.value`, a handle's payload (spec 23.1): the handle
InterfaceField // `i.f`, a field reached through an interface (spec 28): the interface value
};

struct PlaceBase
{
mlir::Value base;
PlaceKind kind = PlaceKind::None;

explicit operator bool() const
{
return kind != PlaceKind::None;
}
};

// The one definition of a place: every walk from a place up to what holds it starts here, so a
// new kind of place is seen by all of them. Each walk decides for itself what a kind means.
inline PlaceBase placeBase(mlir::Value ref)
{
auto *def = ref.getDefiningOp();
if (auto propertyRefOp = mlir::dyn_cast_or_null<mlir_ts::PropertyRefOp>(def))
{
return {propertyRefOp.getObjectRef(), PlaceKind::Field};
}

if (auto elementRefOp = mlir::dyn_cast_or_null<mlir_ts::ElementRefOp>(def))
{
return {elementRefOp.getArray(), PlaceKind::Element};
}

if (auto sharedValueRefOp = mlir::dyn_cast_or_null<mlir_ts::SharedValueRefOp>(def))
{
return {sharedValueRefOp.getShared(), PlaceKind::SharedValue};
}

if (auto interfaceRefOp = mlir::dyn_cast_or_null<mlir_ts::InterfaceSymbolRefOp>(def); interfaceRefOp && isInterfaceField(ref))
{
return {interfaceRefOp.getInterfaceVal(), PlaceKind::InterfaceField};
}

return {};
}

// A field, an element, or `s.value` - the payload of a handle (spec 23.1) - and a field reached
// through an interface (spec 28).
inline bool isPlace(mlir::Value ref)
{
return mlir::isa_and_nonnull<mlir_ts::PropertyRefOp, mlir_ts::ElementRefOp, mlir_ts::SharedValueRefOp>(
ref.getDefiningOp()) ||
isInterfaceField(ref);
return static_cast<bool>(placeBase(ref));
}

// The storage a reference addresses - a local's `ts.Variable` or a global's `ts.AddressOf` - itself
Expand Down Expand Up @@ -679,22 +726,14 @@ inline bool reachesSharedValue(mlir::Value ref)
continue;
}

if (value.getDefiningOp<mlir_ts::SharedValueRefOp>())
if (auto place = placeBase(value))
{
return true;
}
if (place.kind == PlaceKind::SharedValue)
{
return true;
}

if (auto propertyRefOp = value.getDefiningOp<mlir_ts::PropertyRefOp>())
{
work.push_back(propertyRefOp.getObjectRef());
}
else if (auto elementRefOp = value.getDefiningOp<mlir_ts::ElementRefOp>())
{
work.push_back(elementRefOp.getArray());
}
else if (auto interfaceRefOp = value.getDefiningOp<mlir_ts::InterfaceSymbolRefOp>(); interfaceRefOp && isInterfaceField(value))
{
work.push_back(interfaceRefOp.getInterfaceVal());
work.push_back(place.base);
}
else if (auto loadOp = value.getDefiningOp<mlir_ts::LoadOp>(); loadOp && isPlace(loadOp.getReference()))
{
Expand Down Expand Up @@ -733,11 +772,21 @@ inline mlir_ts::LoadOp slotLoadOf(mlir::Value value)
return loadOp;
}

// The ownership passes ask MLIRTypeHelper about types only, under the default options.
inline MLIRTypeHelper typeHelper(mlir::MLIRContext *context)
{
return MLIRTypeHelper(context, CompileOptions{});
}

// Does a value of this type own a block that a release would destroy?
inline bool ownsHeap(mlir::Location location, mlir::Type type)
{
return typeHelper(type.getContext()).ownsHeapMemory(location, type);
}

inline bool ownsHeap(mlir::Value value)
{
MLIRTypeHelper mth(value.getContext(), CompileOptions{});
return mth.ownsHeapMemory(value.getLoc(), value.getType());
return ownsHeap(value.getLoc(), value.getType());
}

// Holds no block: a number, `null` or `undefined` widened, an empty optional, a string literal.
Expand Down Expand Up @@ -811,8 +860,7 @@ inline bool isCopyableString(mlir::Type type)
}

// only a union that lowers to one pointer: a tagged union is a struct
MLIRTypeHelper mth(type.getContext(), CompileOptions{});
return !mth.isUnionTypeNeedsTag(mlir::UnknownLoc::get(type.getContext()), unionType);
return !typeHelper(type.getContext()).isUnionTypeNeedsTag(mlir::UnknownLoc::get(type.getContext()), unionType);
}

return isText(type);
Expand Down Expand Up @@ -976,7 +1024,7 @@ inline bool isOwningRecord(mlir::Value value)
return false;
}

MLIRTypeHelper mth(value.getContext(), CompileOptions{});
auto mth = typeHelper(value.getContext());
for (auto *user : varOp->getUsers())
{
auto propertyRefOp = mlir::dyn_cast<mlir_ts::PropertyRefOp>(user);
Expand Down Expand Up @@ -1175,19 +1223,11 @@ inline int borrowedParam(mlir::Value value, int depth = 0)
return borrowedParam(unboxOp.getIn(), depth + 1);
}

if (auto propertyRefOp = mlir::dyn_cast_or_null<mlir_ts::PropertyRefOp>(def))
{
return borrowedParam(propertyRefOp.getObjectRef(), depth + 1);
}

if (auto elementRefOp = mlir::dyn_cast_or_null<mlir_ts::ElementRefOp>(def))
{
return borrowedParam(elementRefOp.getArray(), depth + 1);
}

if (auto interfaceRefOp = mlir::dyn_cast_or_null<mlir_ts::InterfaceSymbolRefOp>(def); interfaceRefOp && isInterfaceField(value))
// a field, an element, a field through an interface: the argument's block holds them. A
// handle's payload is any handle's, no parameter's.
if (auto place = placeBase(value); place && place.kind != PlaceKind::SharedValue)
{
return borrowedParam(interfaceRefOp.getInterfaceVal(), depth + 1);
return borrowedParam(place.base, depth + 1);
}

if (auto loadOp = mlir::dyn_cast_or_null<mlir_ts::LoadOp>(def))
Expand Down
Loading
Loading