An alias-based formulation of the borrow checker (2018)
A Rust core developer proposes an alternative, Datalog-based formulation for the borrow checker, aiming to overcome existing NLL (Non-Lexical Lifetimes) analysis shortcomings and improve performance. This deep technical dive, complete with Datalog rules and examples, showcases how redefining regions as 'sets of loans' can enable more precise static analysis and accept programs previously rejected. While a prototype passes NLL tests and handles new cases, performance optimization is an ongoing challenge.
The Lowdown
This post from 2018 introduces an experimental, alternative formulation of the Rust borrow checker. The author's goal is to address limitations of the Non-Lexical Lifetimes (NLL) proposal and potentially achieve faster computation. A prototype has been implemented, successfully passing the full NLL test suite and even handling certain complex cases, like issue #47680, that the NLL analysis could not. Although currently slower, the author expresses optimism for significant performance improvements as optimizations are yet to be applied.
- User Impact: The proposed system aims to be transparent to end-users, with the borrow checker functioning largely the same as under NLL, though subtle internal shifts might influence future language features.
- Core Concept - Regions as Loans: The most significant change redefines a region (e.g.,
'ain&'a i32). Instead of corresponding to a portion of the source code or control-flow graph, a region now represents a set of loans (borrow expressions). If a referencerhas type&'a i32, then invalidating any loan within'awould invalidater. - Subtyping with Loan Sets: Under this new model, subtyping works by enlarging the set of loans. If
'ais a subset of'b('a ⊆ 'b), then&'a u32is a subtype of&'b u32. - Methodology - Datalog: The analysis is defined using Datalog, a declarative logic programming language, which is well-suited for expressing relations and rules. The prototype implementation leverages Frank McSherry's
differential-dataflowcrate for efficient execution. - Abstract Region Variables: Initially, the analysis uses abstract, numbered region variables (e.g.,
'0,'1) to represent regions throughout the program. - Relations between Regions: Type system rules establish 'base subset' relationships (e.g.,
'4: '5) between these abstract regions. These relations signify that one region must 'outlive' or be a 'subset' of another. - Control-Flow Graph: The analysis operates on the Rust Intermediate Representation (MIR) control-flow graph, defining 'points' as
Start(Statement)orMid(Statement). - Tracking Subset Relationships: Datalog rules propagate subset relationships transitively and across control-flow graph edges. These relationships accumulate throughout the program, reflecting data flow.
- Borrow Regions: An input
borrow_region(R, L, P)relates a regionRto an abstract loanLcreated by a borrow expression at a specific program pointP. - Live Regions and Loans: A region
Risregion_live_at(R, P)if a reference of that type might be dereferenced later. A loanLisloan_live_at(L, P)if a live regionR'requires' it. - The 'Requires' Relation:
requires(R, L, P)indicates that regionRdepends on the terms of loanLbeing enforced at pointP. These requirements propagate across control-flow edges unless the loanLiskilled(e.g., by reassignment). - Defining an Error: An error occurs when an action at point
Pinvalidates(P, L)a loanL, and thatloan_live_at(L, P)at that point. - Refined Constraint Propagation: A crucial refinement involves propagating subset and 'requires' relations only for regions that are live at the successor point in the control flow. This allows the analysis to accept more programs by dropping constraints associated with dead regions, as demonstrated by the resolution of issue #47680.
- Error Reporting: The Datalog-based approach offers a promising path for generating detailed error messages in the 'three-point form' (borrow, use, invalidation), which is crucial for user experience.
The author concludes by acknowledging the invaluable contributions of Frank McSherry's differential-dataflow crate and discussions with various researchers that inspired this alias-based approach to borrow checking.