The previous debug output only showed numeric IDs for origins, making it
difficult to understand what each origin represented. This change makes
the debug output more informative by showing what kind of entity each
origin refers to (declaration or expression) and additional details like
declaration names or expression class names. This improved output makes
it easier to debug and understand the lifetime safety analysis.
Implement use-after-free detection in the lifetime safety analysis with two warning levels.
- Added a `LifetimeSafetyReporter` interface for reporting lifetime safety issues
- Created two warning levels:
- Definite errors (reported with `-Wexperimental-lifetime-safety-permissive`)
- Potential errors (reported with `-Wexperimental-lifetime-safety-strict`)
- Implemented a `LifetimeChecker` class that analyzes loan propagation and expired loans to detect use-after-free issues.
- Added tracking of use sites through a new `UseFact` class.
- Enhanced the `ExpireFact` to track the expressions where objects are destroyed.
- Added test cases for both definite and potential use-after-free scenarios.
The implementation now tracks pointer uses and can determine when a pointer is dereferenced after its loan has been expired, with appropriate diagnostics.
The two warning levels provide flexibility - definite errors for high-confidence issues and potential errors for cases that depend on control flow.
Fix a crash in the lifetime safety dataflow analysis when handling null CFG blocks.
Added a null check for adjacent blocks in the dataflow analysis algorithm to prevent dereferencing null pointers. This occurs when processing CFG blocks with unreachable successors or predecessors.
Original crash: https://compiler-explorer.com/z/qfzfqG5vM
Fixes https://github.com/llvm/llvm-project/issues/150095
This PR adds the `ExpiredLoansAnalysis` class to track which loans have expired. The analysis uses a dataflow lattice (`ExpiredLattice`) to maintain the set of expired loans at each program point.
This is a very light weight dataflow analysis and is expected to reach fixed point in ~2 iterations.
In principle, this does not need a dataflow analysis but is used for convenience in favour of lean code.
Refactor the Lifetime Safety Analysis infrastructure to support unit testing.
- Created a public API class `LifetimeSafetyAnalysis` that encapsulates the analysis functionality
- Added support for test points via a special `TestPointFact` that can be used to mark specific program points
- Added unit tests that verify loan propagation in various code patterns
Add per-program-point state tracking to the dataflow analysis framework.
- Added a `ProgramPoint` type representing a pair of a CFGBlock and a Fact within that block
- Added a `PerPointStates` map to store lattice states at each program point
- Modified the `transferBlock` method to store intermediate states after each fact is processed
- Added a `getLoans` method to the `LoanPropagationAnalysis` class that uses program points
This change enables more precise analysis by tracking program state at each individual program point rather than just at block boundaries. This is necessary for answering queries about the state of loans, origins, and other properties at specific points in the program, which is required for error reporting in the lifetime safety analysis.
Generalize the dataflow analysis to support both forward and backward analyses.
Some program analyses would be expressed as backward dataflow problems (like liveness analysis). This change enables the framework to support both forward analyses (like the loan propagation analysis) and backward analyses with the same infrastructure.
Refactored the lifetime safety analysis to use a generic dataflow framework with a policy-based design.
### Changes
- Introduced a generic `DataflowAnalysis` template class that can be specialized for different analyses
- Renamed `LifetimeLattice` to `LoanPropagationLattice` to better reflect its purpose
- Created a `LoanPropagationAnalysis` class that inherits from the generic framework
- Moved transfer functions from the standalone `Transferer` class into the analysis class
- Restructured the code to separate the dataflow engine from the specific analysis logic
- Updated debug output and test expectations to use the new class names
### Motivation
In order to add more analyses, e.g. [loan expiry](https://github.com/llvm/llvm-project/pull/148712) and origin liveness, the previous implementation would have separate, nearly identical dataflow runners for each analysis. This change creates a single, reusable component, which will make it much simpler to add subsequent analyses without repeating boilerplate code.
This is quite close to the existing dataflow framework!
This patch introduces the core dataflow analysis infrastructure for the C++ Lifetime Safety checker. This change implements the logic to propagate "loan" information across the control-flow graph. The primary goal is to compute a fixed-point state that accurately models which pointer (Origin) can hold which borrow (Loan) at any given program point.
Key components
* `LifetimeLattice`: Defines the dataflow state, mapping an `OriginID` to a `LoanSet` using `llvm::ImmutableMap`.
* `Transferer`: Implements the transfer function, which updates the `LifetimeLattice` by applying the lifetime facts (Issue, AssignOrigin, etc.) generated for each basic block.
* `LifetimeDataflow`: A forward dataflow analysis driver that uses a worklist algorithm to iterate over the CFG until the lattice state converges.
The existing test suite has been extended to check the final dataflow results.
This work is a prerequisite for the final step of the analysis: consuming these results to identify and report lifetime violations.
This patch introduces the initial implementation of the
intra-procedural, flow-sensitive lifetime analysis for Clang, as
proposed in the recent RFC:
https://discourse.llvm.org/t/rfc-intra-procedural-lifetime-analysis-in-clang/86291
The primary goal of this initial submission is to establish the core
dataflow framework and gather feedback on the overall design, fact
representation, and testing strategy. The focus is on the dataflow
mechanism itself rather than exhaustively covering all C++ AST edge
cases, which will be addressed in subsequent patches.
#### Key Components
* **Conceptual Model:** Introduces the fundamental concepts of `Loan`,
`Origin`, and `Path` to model memory borrows and the lifetime of
pointers.
* **Fact Generation:** A frontend pass traverses the Clang CFG to
generate a representation of lifetime-relevant events, such as pointer
assignments, taking an address, and variables going out of scope.
* **Testing:** `llvm-lit` tests validate the analysis by checking the
generated facts.
### Next Steps
*(Not covered in this PR but planned for subsequent patches)*
The following functionality is planned for the upcoming patches to build
upon this foundation and make the analysis usable in practice:
* **Dataflow Lattice:** A dataflow lattice used to map each pointer's
symbolic `Origin` to the set of `Loans` it may contain at any given
program point.
* **Fixed-Point Analysis:** A worklist-based, flow-sensitive analysis
that propagates the lattice state across the CFG to a fixed point.
* **Placeholder Loans:** Introduce placeholder loans to represent the
lifetimes of function parameters, forming the basis for analysis
involving function calls.
* **Annotation and Opaque Call Handling:** Use placeholder loans to
correctly model **function calls**, both by respecting
`[[clang::lifetimebound]]` annotations and by conservatively handling
opaque/un-annotated functions.
* **Error Reporting:** Implement the final analysis phase that consumes
the dataflow results to generate user-facing diagnostics. This will
likely require liveness analysis to identify live origins holding
expired loans.
* **Strict vs. Permissive Modes:** Add the logic to support both
high-confidence (permissive) and more comprehensive (strict) warning
levels.
* **Expanded C++ Coverage:** Broaden support for common patterns,
including the lifetimes of temporary objects and pointers within
aggregate types (structs/containers).
* Performance benchmarking
* Capping number of iterations or number of times a CFGBlock is
processed.
---------
Co-authored-by: Baranov Victor <bar.victor.2002@gmail.com>