Add detection for use-after-invalidation of container references and
iterators.
This change improves the lifetime safety analysis by detecting a common
class of bugs where references or iterators to container elements are
used after operations that invalidate them (like `push_back`, `insert`,
`resize`, etc.). Example:
```cpp
#include <vector>
void test() {
std::vector<int> v = {1, 2, 3};
int &ref = v[0];
v.push_back(4); // This invalidates references
int x = ref; // This should trigger the warning
}
```
- Added a new `InvalidateOriginFact` to track when container elements
are invalidated
- Added a new diagnostic warning for use-after-invalidation scenarios
- Implemented detection for container methods that invalidate
references/iterators
- Added logic to check for uses of references after container
invalidation
- Added the new warning to the `lifetime-safety-strict` diagnostic group
Compile with `-Wlifetime-safety-strict` or
`-Wlifetime-safety-invalidation` to see the new warning.
Current limitations (not yet detected):
* **Field member invalidations** - When invalidation happens on a field
member of a struct:
```cpp
void Invalidate1Use1IsInvalid() {
S s;
auto it = s.strings1.begin();
s.strings1.push_back("1"); // Should invalidate but doesn't
*it; // Should warn but doesn't
}
```
* **Iterator from pointer to container** - When the iterator is obtained
through a pointer:
```cpp
void IteratorFromPointerToContainerIsInvalidated() {
std::vector<std::string> s;
std::vector<std::string>* p = &s;
auto it = p->begin();
p->push_back("1"); // Should invalidate but doesn't
*it; // Should warn but doesn't
}
```
We need to distinguish between the loans to `s` and `s.strings` using
more granular `Path`. Destruction/Invalidation of a prefix of path
destroys/invalidates the complete path.
We would also need some mechanism to differentiate the storage of
between Containers and the owned buffer. This could be done for
`gsl::Owner` and `lifetimebound` member functions.
Improve lifetime safety analysis by tracking moved objects and providing
more precise warnings for potentially false positive cases.
- Added support for detecting moves in function calls with rvalue
reference parameters
- Added a new `MovedOriginFact` to track when objects are moved
- Modified the lifetime checker to detect when a loan's storage has been
moved
- Added new diagnostic messages that indicate when a warning might be a
false positive due to moved storage
- Added notes in diagnostics to show where objects were potentially
moved
Detect dangling field references when stack memory escapes to class
fields. This change extends lifetime safety analysis to detect a common
class of temporal memory safety bugs where local variables or parameters
are stored in class fields but outlive their scope.
- Added a new `FieldEscapeFact` class to represent when an origin
escapes via assignment to a field
- Refactored `OriginEscapesFact` into a base class with specialized
subclasses for different escape scenarios
- Added detection for stack memory escaping to fields in constructors
and member functions
- Implemented new diagnostic for dangling field references with
appropriate warning messages
Importantly,
- Added `AddParameterDtors` option to CFG to add parameter dtors and
lifetime ends behind an option. In principle, parameters ctors and dtors
do not belong in the function context but in the caller context. This
becomes incorrect to include in function's CFG when we have inlined CFGs
like some analyses in the analyzer (produces double dtors for
arguments). Therefore this provides a way to opt-in to know about
destructed params on function exits.
This PR implements support for automatically suggesting and inferring
`[[clang::lifetimebound]]` annotations for the implicit `this`.
The analysis now models the implicit this object as an implicit
parameter that carries a "placeholder loan" from the caller's scope. By
tracking this loan through the method body, the analysis can identify if
this reaches an escape point, such as a return statement.
Key Changes:
- Updated `PlaceholderLoan` to hold a union of `ParmVarDecl*` and
`CXXMethodDecl*`
- Extended `OriginManager` to handle `CXXThisExpr` and create a
dedicated origin list for the implicit this parameter
- Modified `FactsGenerator::issuePlaceholderLoans` to create a
placeholder loan for this at the entry of non-static member functions
- Updated `implicitObjectParamIsLifetimeBound` in
`LifetimeAnnotations.cpp` to check for the attribute on the method
declaration itself
- Added logic to skip implicit methods and standard assignment operators
to avoid redundant warnings on boilerplate code
Example:
```cpp
struct ReturnsSelf {
const ReturnsSelf& get() const {
return *this;
}
};
void test_get_on_temporary() {
const ReturnsSelf& s_ref = ReturnsSelf().get();
(void)s_ref;
}
```
Warning:
```
a7.cpp:5:33: warning: implict this in intra-TU function should be marked [[clang::lifetimebound]] [-Wexperimental-lifetime-safety-intra-tu-suggestions]
5 | const ReturnsSelf& get() const {
| ~~~ ^
| [[clang::lifetimebound]]
a7.cpp:6:12: note: param returned here
6 | return *this;
| ^~~~~
a7.cpp:11:30: warning: temporary bound to local reference 's_ref' will be destroyed at the end of the full-expression [-Wdangling]
11 | const ReturnsSelf& s_ref = ReturnsSelf().get();
| ^~~~~~~~~~~~~
```
Fixes https://github.com/llvm/llvm-project/issues/169941
Remove the "experimental-" prefix from lifetime safety diagnostic groups
and command-line options. This enables the analysis in `-Wall`.
We are now in a pretty stable state with no crashes. This change
indicates that lifetime safety analysis is no longer considered
experimental and is now a stable feature. By removing the
"experimental-" prefix, we're signaling to users that this functionality
is ready for use.
- Renamed diagnostic groups from `experimental-lifetime-safety*` to
`lifetime-safety*`
- Updated command-line options from `-fexperimental-lifetime-safety*` to
`-flifetime-safety*` and this is now ON by default.
- Added a check to only enable lifetime safety analysis when relevant
diagnostics are enabled
- Updated test files to use the new flag names
Enable implicit and temporary destructors in lifetime safety analysis CFG build options.
Updated test cases to verify the changes:
- Added a new test run configuration in `warn-lifetime-analysis-nocfg.cpp` that includes the experimental lifetime safety inference and TU analysis flags
- Modified `warn-lifetime-safety.cpp` to also verify both function-only and TU-level analysis
Note that we miss instantiation chain in the diagnostics in intra-TU mode of the analysis!
PR #173096 extended -Wunsafe-buffer-usage-in-libc-call to apply to all
functions with the 'format' attribute.
This change moves those warnings behind a separate
-Wunsafe-buffer-usage-in-format-attr-call flag (implicitly enabled by
-Wunsafe-buffer-usage), allowing projects to decide whether they want to
opt in to this or not.
This PR adds support for toggling on/off warnings around static sized
arrays. This supports / addresses
https://github.com/llvm/llvm-project/issues/87284, for those who use
-fsanitize=array-bounds which inserts checks for fixed sized arrays
already.
Add functionality to analyze functions in the post-order of the call
graph.
The PR includes the following changes:
1. **Call Graph Generation**: Uses `clang::CallGraph` and
`addToCallGraph` to generate a call graph.
2. **Topological Traversal**: Uses `llvm::post_order` to iterate through
the CallGraph.
3. **New Frontend Flag**: The post-order analysis is enabled via a new
frontend flag `-fexperimental-lifetime-safety-inference-post-order`
Example:
```css
#include <iostream>
#include <string>
std::string_view f_1(std::string_view a);
std::string_view f_2(std::string_view a);
std::string_view f_f(std::string_view a);
std::string_view f_f(std::string_view a) {
std::string stack = "something on stack";
std::string_view res = f_2(stack);
return res;
}
std::string_view f_2(std::string_view a) {
return f_1(a);
}
std::string_view f_1(std::string_view a) {
return a;
}
```
Ouput:
```
s.cpp:20:26: warning: parameter in intra-TU function should be marked [[clang::lifetimebound]] [-Wexperimental-lifetime-safety-intra-tu-suggestions]
20 | std::string_view f_1(std::string_view a)
| ^~~~~~~~~~~~~~~~~~
| [[clang::lifetimebound]]
s.cpp:22:12: note: param returned here
22 | return a;
| ^
s.cpp:15:26: warning: parameter in intra-TU function should be marked [[clang::lifetimebound]] [-Wexperimental-lifetime-safety-intra-tu-suggestions]
15 | std::string_view f_2(std::string_view a)
| ^~~~~~~~~~~~~~~~~~
| [[clang::lifetimebound]]
s.cpp:17:12: note: param returned here
17 | return f_1(a);
| ^~~~~~
s.cpp:11:32: warning: address of stack memory is returned later [-Wexperimental-lifetime-safety-permissive]
11 | std::string_view res = f_2(stack);
| ^~~~~
s.cpp:12:12: note: returned here
12 | return res;
```
Fixes https://github.com/llvm/llvm-project/issues/172862
---------
Co-authored-by: Utkarsh Saxena <usx@google.com>
This PR adds the implementation for printing missing origin stats for
lifetime analysis.
**Purpose:**
This capability is added to track the expression types with missing
origin. While retrieving the origins from origin manager, some
expressions show missing origins. Currently these are created on the fly
using getOrCreate function. For analysing the coverage of the check, it
will be necessary to see what kind of expressions have a missing origin.
It prints the counts in this form: `QualType : count` and `StmtClassName
: count`.
**Approach:**
1. The signature of the runLifetimeAnalysis function is changed to
return the LifetimeAnalysis object which will be used to get the origin
manager which can be used for finding the count of missing origins.
2. The count of missing origins is kept in origin manager while the CFG
is visited as part of the analysis.
Example output:
For the file llvm-project/llvm/lib/Demangle/Demangle.cpp:
```
*** LifetimeSafety Missing Origin per QualType: (QualType : count) :
value_type : 1
char * : 3
*** LifetimeSafety Missing Origin per StmtClassName: (StmtClassName : count) :
BinaryOperator : 3
UnaryOperator : 1
Total missing origins: 4
```
Differentiate between cross-TU and intra-TU annotation suggestions.
This PR refines the lifetime annotation suggestion feature by
introducing a distinction between cross-TU and intra-TU annotation
suggestions. The primary purpose of this change is to differentiate
between suggestions which can be replaced by inference and other
cross-tu suggestions where inference is not sufficient to replace the
need of explicit annotations.
We have introduced two new warning groups under Lifetime Safety
Suggestions:
- `-Wexperimental-lifetime-safety-cross-tu-suggestions`: These are for
functions whose declarations are in header, definition in source file.
These are higher priority as it affects callers in other translation
units. Inference cannot replace these annotations across TU boundaries
(even in its most powerful form). Annotation suggestion is on the header
declaration.
- `-Wexperimental-lifetime-safety-intra-tu-suggestions`: For suggestions
on functions which are in the same TU. These can be considered
lower-priority suggestions as inference can potentially handle these
within the same TU.
Example:
```cpp
// Header file r.h
#include <iostream>
#include <string>
std::string_view public_func(std::string_view a);
inline std::string_view inline_header(std::string_view a) {
return a;
}
```
```cpp
// Source (cpp file)
#include <iostream>
#include <string>
#include "r.h"
std::string_view public_func(std::string_view a) {
return a;
}
namespace {
std::string_view private_func(std::string_view a) {
return a;
}
}
```
The warnings generated are:
```
./r.h:6:39: warning: parameter in intra-TU function should be marked [[clang::lifetimebound]] [-Wexperimental-lifetime-safety-intra-tu-suggestions]
6 | inline std::string_view inline_header(std::string_view a) {
| ^~~~~~~~~~~~~~~~~~
| [[clang::lifetimebound]]
./r.h:7:12: note: param returned here
7 | return a;
./r.h:4:30: warning: parameter in cross-TU function should be marked [[clang::lifetimebound]] [-Wexperimental-lifetime-safety-cross-tu-suggestions]
4 | std::string_view public_func(std::string_view a);
| ^~~~~~~~~~~~~~~~~~
| [[clang::lifetimebound]]
r.cpp:6:10: note: param returned here
6 | return a;
| ^
r.cpp:10:33: warning: parameter in intra-TU function should be marked [[clang::lifetimebound]] [-Wexperimental-lifetime-safety-intra-tu-suggestions]
10 | std::string_view private_func(std::string_view a) {
| ^~~~~~~~~~~~~~~~~~
| [[clang::lifetimebound]]
r.cpp:11:10: note: param returned here
11 | return a;
| ^
```
This PR implements a **multi-level origin model** for the lifetime
safety analysis, replacing the previous single-origin-per-variable
approach with an `OriginList` that captures multiple levels of
indirection.
### KeyChanges
1. **OriginList Structure**: Each origin now represents a single level
of indirection, organized into Lists:
- `int* p` → length 2 (pointer variable + pointee)
- `int** pp` → length 3 (variable + first pointee + second pointee)
- `std::string_view&` → length 2 (reference + view object)
2. **Reference Type Understanding**: The analysis now properly
distinguishes:
- Pointer types vs references-to-pointer types (`string_view` vs
`string_view&`)
- References as aliases: `int a; int& b = a; int& c = b;` correctly
recognizes both `b` and `c` alias `a`
- References don't add storage and they reuse the underlying
declaration's origins.
3. **Type-Driven Origin Creation**: Origins are created based on type
structure via `buildListForType`, ensuring origins match the type's
indirection levels.
4. **Multi-Level Flow Propagation**: The `flow` function propagates
origins through all depths of the lists with a critical assertion:
`assert(Dst->getDepth() == Src->getDepth() && "Lists must have the same
length");` This ensures type safety in origin propagation during
expression handling. (Rant with relief: This `assert` was quite hard to
get right but it helped make the right changes).
5. `Lifetimebound` **Semantics**: For reference return types,
`lifetimebound` now propagates only the outermost origin, not inner
pointee origins.
We are also deleting many tests in lifetime-safety-dataflow.cpp related
to control flow which are better tested in unit tests and the other lit
test.
Fixes: https://github.com/llvm/llvm-project/issues/169758
Fixes: https://github.com/llvm/llvm-project/issues/162834
Fixes: https://github.com/llvm/llvm-project/issues/169940
Add lifetime annotation suggestion in lifetime analysis.
This PR introduces a new feature to Clang's lifetime analysis to detect
and suggest missing `[[clang::lifetimebound]]` annotations on function
parameters.
It introduces the concept of `placeholder loans`. At the entry of a
function, a special placeholder loan is created for each pointer or
reference parameter. The analysis then tracks these loans using
`OriginFlow` facts. If an `OriginEscapesFact` shows that an origin
holding a placeholder loan escapes the function's scope (e.g., via a
return statement), a new warning is issued.
This warning, controlled by the warning flag
`-Wexperimental-lifetime-safety-suggestions`, suggests adding the
`[[clang::lifetimebound]]` attribute to the corresponding parameter.
Example:
```cpp
std::string_view foo(std::string_view a) {
return a;
}
```
Facts:
```
Function: foo
Block B2:
Issue (0 (Placeholder loan) , ToOrigin: 0 (Decl: a))
End of Block
Block B1:
Use (0 (Decl: a), Read)
OriginFlow (Dest: 1 (Expr: ImplicitCastExpr), Src: 0 (Decl: a))
OriginFlow (Dest: 2 (Expr: CXXConstructExpr), Src: 1 (Expr: ImplicitCastExpr))
OriginEscapes (2 (Expr: CXXConstructExpr))
End of Block
Block B0:
End of Block
```
Sample warning:
```
o.cpp:61:39: warning: param should be marked [[clang::lifetimebound]] [-Wexperimental-lifetime-safety-suggestions]
61 | std::string_view foo(std::string_view a) {
| ~~~~~~~~~~~~~~~~~^
| [[clang::lifetimebound]]
o.cpp:62:9: note: param escapes here
62 | return a;
```
Fixes: https://github.com/llvm/llvm-project/issues/169939
Adding "use-after-return" in Lifetime Analysis.
Detecting when a function returns a reference to its own stack memory:
[UAR Design
Doc](https://docs.google.com/document/d/1Wxjn_rJD_tuRdejP81dlb9VOckTkCq5-aE1nGcerb_o/edit?usp=sharing)
Consider the following example:
```cpp
std::string_view foo() {
std::string_view a;
std::string str = "small scoped string";
a = str;
return a;
}
```
The code adds a new Fact "OriginEscape" in the end of the CFG to
determine any loan that is escaping the function as shown below:
```
Function: foo
Block B2:
End of Block
Block B1:
OriginFlow (Dest: 0 (Decl: a), Src: 1 (Expr: CXXConstructExpr))
OriginFlow (Dest: 2 (Expr: ImplicitCastExpr), Src: 3 (Expr: StringLiteral))
Issue (0 (Path: operator=), ToOrigin: 4 (Expr: DeclRefExpr))
OriginFlow (Dest: 5 (Expr: ImplicitCastExpr), Src: 4 (Expr: DeclRefExpr))
Use (0 (Decl: a), Write)
Issue (1 (Path: str), ToOrigin: 6 (Expr: DeclRefExpr))
OriginFlow (Dest: 7 (Expr: ImplicitCastExpr), Src: 6 (Expr: DeclRefExpr))
OriginFlow (Dest: 8 (Expr: CXXMemberCallExpr), Src: 7 (Expr: ImplicitCastExpr))
OriginFlow (Dest: 9 (Expr: ImplicitCastExpr), Src: 8 (Expr: CXXMemberCallExpr))
OriginFlow (Dest: 10 (Expr: ImplicitCastExpr), Src: 9 (Expr: ImplicitCastExpr))
OriginFlow (Dest: 11 (Expr: MaterializeTemporaryExpr), Src: 10 (Expr: ImplicitCastExpr))
OriginFlow (Dest: 0 (Decl: a), Src: 11 (Expr: MaterializeTemporaryExpr))
Use (0 (Decl: a), Read)
OriginFlow (Dest: 12 (Expr: ImplicitCastExpr), Src: 0 (Decl: a))
OriginFlow (Dest: 13 (Expr: CXXConstructExpr), Src: 12 (Expr: ImplicitCastExpr))
Expire (1 (Path: str))
OriginEscapes (13 (Expr: CXXConstructExpr))
End of Block
Block B0:
End of Block
```
The confidence of the report is determined by checking if at least one
of the loans returned is not expired (strict). If all loans are expired
it is considered permissive.
More information [UAR Design
Doc](https://docs.google.com/document/d/1Wxjn_rJD_tuRdejP81dlb9VOckTkCq5-aE1nGcerb_o/edit?usp=sharing)
Restructure the C++ Lifetime Safety Analysis into modular components
with clear separation of concerns.
This PR reorganizes the C++ Lifetime Safety Analysis code by:
1. Breaking up the monolithic `LifetimeSafety.cpp` (1500+ lines) into
multiple smaller, focused files
2. Creating a dedicated `LifetimeSafety` directory with a clean
component structure
3. Introducing header files for each component with proper documentation
4. Moving existing code into the appropriate component files:
- `Checker.h/cpp`: Core lifetime checking logic
- `Dataflow.h`: Generic dataflow analysis framework
- `Facts.h`: Lifetime-relevant events and fact management
- `FactsGenerator.h/cpp`: AST traversal for fact generation
- `LiveOrigins.h/cpp`: Backward dataflow analysis for origin liveness
- `LoanPropagation.h/cpp`: Forward dataflow analysis for loan tracking
- `Loans.h`: Loan and access path definitions
- `Origins.h`: Origin management
- `Reporter.h`: Interface for reporting lifetime violations
- `Utils.h`: Common utilities for the analysis
The code functionality remains the same, but is now better organized
with clearer interfaces between components.
This PR fixes a bug in the lifetime safety analysis where `ImplicitCastExpr` nodes were causing duplicate loan generation. The changes:
1. Remove the recursive `Visit(ICE->getSubExpr())` call in `VisitImplicitCastExpr` to prevent duplicate processing of the same expression
2. Ensure the CFG build options are properly configured for lifetime safety analysis by moving the flag check earlier
3. Enhance the unit test infrastructure to properly handle multiple loans per variable
4. Add a test case that verifies implicit casts to const don't create duplicate loans
5. Add a test case for ternary operators with a FIXME note about origin propagation
These changes prevent the analysis from generating duplicate loans when expressions are wrapped in implicit casts, which improves the accuracy of the lifetime safety analysis.
This reintroduces `Type.h`, having earlier been renamed to `TypeBase.h`,
as a redirection to `TypeBase.h`, and redirects most users to include
the former instead.
This is a preparatory patch for being able to provide inline definitions
for `Type` methods which would otherwise cause a circular dependency
with `Decl{,CXX}.h`.
Doing these operations into their own NFC patch helps the git rename
detection logic work, preserving the history.
This patch makes clang just a little slower to build (~0.17%), just
because it makes more code indirectly include `DeclCXX.h`.
This is a preparatory patch, to be able to provide inline definitions
for `Type` functions which depend on `Decl{,CXX}.h`. As the latter also
depends on `Type.h`, this would not be possible without some
reorganizing.
Splitting this rename into its own patch allows git to track this as a
rename, and preserve all git history, and not force any code
reformatting.
A later NFC patch will reintroduce `Type.h` as redirection to
`TypeBase.h`, rewriting most places back to directly including `Type.h`
instead of `TypeBase.h`, leaving only a handful of places where this is
necessary.
Then yet a later patch will exploit this by making more stuff inline.
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.
This is a major change on how we represent nested name qualifications in
the AST.
* The nested name specifier itself and how it's stored is changed. The
prefixes for types are handled within the type hierarchy, which makes
canonicalization for them super cheap, no memory allocation required.
Also translating a type into nested name specifier form becomes a no-op.
An identifier is stored as a DependentNameType. The nested name
specifier gains a lightweight handle class, to be used instead of
passing around pointers, which is similar to what is implemented for
TemplateName. There is still one free bit available, and this handle can
be used within a PointerUnion and PointerIntPair, which should keep
bit-packing aficionados happy.
* The ElaboratedType node is removed, all type nodes in which it could
previously apply to can now store the elaborated keyword and name
qualifier, tail allocating when present.
* TagTypes can now point to the exact declaration found when producing
these, as opposed to the previous situation of there only existing one
TagType per entity. This increases the amount of type sugar retained,
and can have several applications, for example in tracking module
ownership, and other tools which care about source file origins, such as
IWYU. These TagTypes are lazily allocated, in order to limit the
increase in AST size.
This patch offers a great performance benefit.
It greatly improves compilation time for
[stdexec](https://github.com/NVIDIA/stdexec). For one datapoint, for
`test_on2.cpp` in that project, which is the slowest compiling test,
this patch improves `-c` compilation time by about 7.2%, with the
`-fsyntax-only` improvement being at ~12%.
This has great results on compile-time-tracker as well:

This patch also further enables other optimziations in the future, and
will reduce the performance impact of template specialization resugaring
when that lands.
It has some other miscelaneous drive-by fixes.
About the review: Yes the patch is huge, sorry about that. Part of the
reason is that I started by the nested name specifier part, before the
ElaboratedType part, but that had a huge performance downside, as
ElaboratedType is a big performance hog. I didn't have the steam to go
back and change the patch after the fact.
There is also a lot of internal API changes, and it made sense to remove
ElaboratedType in one go, versus removing it from one type at a time, as
that would present much more churn to the users. Also, the nested name
specifier having a different API avoids missing changes related to how
prefixes work now, which could make existing code compile but not work.
How to review: The important changes are all in
`clang/include/clang/AST` and `clang/lib/AST`, with also important
changes in `clang/lib/Sema/TreeTransform.h`.
The rest and bulk of the changes are mostly consequences of the changes
in API.
PS: TagType::getDecl is renamed to `getOriginalDecl` in this patch, just
for easier to rebasing. I plan to rename it back after this lands.
Fixes#136624
Fixes https://github.com/llvm/llvm-project/issues/43179
Fixes https://github.com/llvm/llvm-project/issues/68670
Fixes https://github.com/llvm/llvm-project/issues/92757
When searching for noreturn variable initializations, do not visit CFG
blocks that are already visited, it prevents hanging the analysis.
It must fix https://github.com/llvm/llvm-project/issues/150336.
Add a language option flag for experimental lifetime safety analysis in C++.
This change provides a language option to control the experimental lifetime safety analysis feature, making it more explicit and easier to enable/disable. Previously, the feature was controlled indirectly through a diagnostic warning flag, which we do not want to accidentally enable with `-Weverything` (atm)!
### Changes:
- Added a new language option `EnableLifetimeSafety` in `LangOptions.def` for experimental lifetime safety analysis in C++
- Added corresponding driver options `-fexperimental-lifetime-safety` and `-fno-experimental-lifetime-safety` in `Options.td`
- Modified `AnalysisBasedWarnings.cpp` to use the new language option flag instead of checking if a specific diagnostic is ignored
- Updated a test case to use the new flag instead of relying on the warning flag alone
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
de10e44b6fe7 ("Thread Safety Analysis: Support warning on
passing/returning pointers to guarded variables") added checks for
passing pointer to guarded variables. While new features do not
necessarily need to support the deprecated attributes (`guarded_var`,
and `pt_guarded_var`), we need to ensure that such features do not cause
the compiler to crash.
As such, code such as this:
struct {
int v __attribute__((guarded_var));
} p;
int *g() {
return &p.v; // handleNoMutexHeld() with POK_ReturnPointer
}
Would crash in debug builds with the assertion in handleNoMutexHeld()
triggering. The assertion is meant to capture the fact that this helper
should only be used for warnings on variables (which the deprecated
attributes only applied to).
To fix, the function handleNoMutexHeld() should handle all POK cases
that apply to variables explicitly, and produce a best-effort warning.
We refrain from introducing new warnings to avoid unnecessary code bloat
for deprecated features.
Fixes: https://github.com/llvm/llvm-project/issues/140330
Compiler sometimes issues warnings on exit from 'noreturn' functions, in
the code like:
[[noreturn]] extern void nonreturnable();
void (*func_ptr)();
[[noreturn]] void foo() {
func_ptr = nonreturnable;
(*func_ptr)();
}
where exit cannot take place because the function pointer is actually a
pointer to noreturn function.
This change introduces small data analysis that can remove some of the
warnings in the cases when compiler can prove that the set of reaching
definitions consists of noreturn functions only.
This option is similar to -Wuninitialized-const-reference, but diagnoses
the passing of an uninitialized value via a const pointer, like in the
following code:
```
void foo(const int *);
void test() {
int v;
foo(&v);
}
```
This is an extract from #147221 as suggested in [this
comment](https://github.com/llvm/llvm-project/pull/147221#discussion_r2190998730).
When one kind of diagnostics is disabled, this should not preclude other
diagnostics from displaying, even if they have lower priority. For
example, this should print a warning about passing an uninitialized
variable as a const reference:
```
> cat test.cpp
void foo(const int &);
int f(bool a) {
int v;
if (a) {
foo(v);
v = 5;
}
return v;
}
> clang test.cpp -fsyntax-only -Wuninitialized -Wno-sometimes-uninitialized
```
This helps to avoid duplicating warnings in cases like:
```
> cat test.cpp
void bar(int);
void foo(const int &);
void test(bool a) {
int v = v;
if (a)
bar(v);
else
foo(v);
}
> clang++.exe test.cpp -fsyntax-only -Wuninitialized
test.cpp:4:11: warning: variable 'v' is uninitialized when used within its own initialization [-Wuninitialized]
4 | int v = v;
| ~ ^
test.cpp:4:11: warning: variable 'v' is uninitialized when used within its own initialization [-Wuninitialized]
4 | int v = v;
| ~ ^
2 warnings generated.
```
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>
Introduce the `reentrant_capability` attribute, which may be specified
alongside the `capability(..)` attribute to denote that the defined
capability type is reentrant. Marking a capability as reentrant means
that acquiring the same capability multiple times is safe, and does not
produce warnings on attempted re-acquisition.
The most significant changes required are plumbing to propagate if the
attribute is present to a CapabilityExpr, and introducing
ReentrancyDepth to the LockableFactEntry class.
This PR attempts to improve the diagnostics flag
`-Wtautological-overlap-compare` (#13473). I have added code to warn
about float-point literals and character literals. I have also changed
the warning message for the non-overlapping case to provide a more
correct hint to the user.
Fixes#13473.
Previously, analysis-based diagnostics (like -Wconsumed) had to be
enabled at file scope in order to be run at the end of each function
body. This meant that they did not respect #pragma clang diagnostic
enabling or disabling the diagnostic.
Now, these pragmas can control the diagnostic emission.
Fixes#42199