Fix forwarding of static lifetimes in function calls.
PiperOrigin-RevId: 458157702
diff --git a/lifetime_analysis/lifetime_analysis.cc b/lifetime_analysis/lifetime_analysis.cc
index 9a9f002..af8ef2a 100644
--- a/lifetime_analysis/lifetime_analysis.cc
+++ b/lifetime_analysis/lifetime_analysis.cc
@@ -871,6 +871,20 @@
//
// Some additional considerations apply if the callee signature contains the
// 'static lifetime, either in the parameters or the return value:
+ // - Any objects that are associated with the static lifetime in the callee
+ // must be forced to have static lifetime.
+ // We have no way of doing this directly, as we cannot mutate the lifetime
+ // of the object (and, in any case, such a mutation would be global and not
+ // limited to the current point in the program flow).
+ // Instead, for each such object, we synthesize a pointer with static
+ // lifetime and make it point at the object. Later, in
+ // PropagateStaticToPointees(), this will cause us to assign static lifetime
+ // to the object.
+ // A cleaner solution to this would be to explicitly express "outlives"
+ // constraints in the lattice. This might also help more generally to
+ // simplify the logic associated with static lifetimes, but it would also be
+ // a more invasive change.
+ //
// - Any pointer or reference may point to an object of static lifetime. This
// has the following implications:
// - In step 2, when adding edges to the points-to map, we always add edges
@@ -878,6 +892,7 @@
// type of the pointer.
// - In step 3, an object of static lifetime conforms to any callee lifetime
// if that lifetime occurs in covariant position.
+ //
// - The callee may have access to objects of static lifetime that are not
// passed as arguments, in addition to the ones that are accessible from the
// arguments.
@@ -909,6 +924,17 @@
object_repository, lifetime_points_to_set);
}
+ // Force any objects associated with the static lifetime in the callee to have
+ // static lifetime (see more detailed explanation above).
+ if (auto iter = lifetime_points_to_set.find(Lifetime::Static());
+ iter != lifetime_points_to_set.end()) {
+ for (const Object& object : iter->second) {
+ Object pointer = object_repository.CreateStaticObject(
+ ast_context.getPointerType(object.Type()));
+ points_to_map.ExtendPointerPointsToSet(pointer, {object});
+ }
+ }
+
// Step 2: Propagate points-to sets to output parameters.
for (auto [type, param_lifetimes, arg_object] : fn_params) {
PropagateLifetimesToPointees({arg_object}, type, param_lifetimes,
diff --git a/lifetime_analysis/test/static_lifetime.cc b/lifetime_analysis/test/static_lifetime.cc
index 243020e..e5e9d2a 100644
--- a/lifetime_analysis/test/static_lifetime.cc
+++ b/lifetime_analysis/test/static_lifetime.cc
@@ -186,9 +186,7 @@
f1(s);
}
)"),
- // TODO(b/237517535): The lifetimes deduced for `f2` are incorrect
- // and should be static.
- LifetimesAre({{"f1", "static"}, {"f2", "a"}}));
+ LifetimesAre({{"f1", "static"}, {"f2", "static"}}));
}
TEST_F(LifetimeAnalysisTest, ConstructorStoresThisPointerInStatic) {
@@ -220,10 +218,7 @@
S s;
};
)"),
- // TODO(b/230725905): The lifetimes for T::T should be "static:"
- // because T contains a member variable of type S, and all
- // instances of S need to be static.
- LifetimesAre({{"S::S", "static:"}, {"T::T", "a:"}}));
+ LifetimesAre({{"S::S", "static:"}, {"T::T", "static:"}}));
}
TEST_F(LifetimeAnalysisTest,
@@ -240,10 +235,7 @@
T() {}
};
)"),
- // TODO(b/230725905): The lifetimes for T::T should be "static:"
- // because T derives from S and all instances of S need to be
- // static.
- LifetimesAre({{"S::S", "static:"}, {"T::T", "a:"}}));
+ LifetimesAre({{"S::S", "static:"}, {"T::T", "static:"}}));
}
} // namespace