blob: 11ca159d0d7c7d7ee2351bbc0e999c68843243c0 [file] [log] [blame]
Luca Versari81222852022-08-05 06:36:10 -07001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#include "lifetime_analysis/lifetime_constraints.h"
6
Luca Versari31771ca2022-09-16 07:26:43 -07007#include <llvm/ADT/DenseSet.h>
8
9#include <algorithm>
10
Luca Versariefeaf272023-01-16 10:19:28 -080011#include "lifetime_annotations/lifetime.h"
Luca Versari91a56ff2022-08-22 01:58:33 -070012#include "lifetime_annotations/lifetime_substitutions.h"
Luca Versari7f2929c2023-01-16 10:15:54 -080013#include "lifetime_annotations/pointee_type.h"
Luca Versari91a56ff2022-08-22 01:58:33 -070014
Luca Versari81222852022-08-05 06:36:10 -070015namespace clang {
16namespace tidy {
17namespace lifetimes {
18
19clang::dataflow::LatticeJoinEffect LifetimeConstraints::join(
Luca Versari7f2929c2023-01-16 10:15:54 -080020 const LifetimeConstraints& other) {
Luca Versari81222852022-08-05 06:36:10 -070021 bool changed = false;
22 for (auto p : other.outlives_constraints_) {
23 changed |= outlives_constraints_.insert(p).second;
24 }
25 return changed ? clang::dataflow::LatticeJoinEffect::Changed
26 : clang::dataflow::LatticeJoinEffect::Unchanged;
27}
28
Luca Versari31771ca2022-09-16 07:26:43 -070029namespace {
30
31// Simple Disjoint-Set-Union with path compression (but no union-by-rank). This
32// guarantees O(log n) time per operation.
33class LifetimeDSU {
34 public:
35 void MakeSet(Lifetime l) { parent_[l] = l; }
36 Lifetime Find(Lifetime l) {
37 if (l == parent_[l]) return l;
38 return parent_[l] = Find(parent_[l]);
39 }
40 void Union(Lifetime a, Lifetime b) {
41 a = Find(a);
42 b = Find(b);
43 if (a != b) {
44 parent_[a] = b;
Luca Versari91a56ff2022-08-22 01:58:33 -070045 }
46 }
47
Luca Versari31771ca2022-09-16 07:26:43 -070048 private:
49 llvm::DenseMap<Lifetime, Lifetime> parent_;
50};
Luca Versari91a56ff2022-08-22 01:58:33 -070051
Luca Versari31771ca2022-09-16 07:26:43 -070052} // namespace
Luca Versari91a56ff2022-08-22 01:58:33 -070053
Luca Versari7f2929c2023-01-16 10:15:54 -080054llvm::DenseSet<Lifetime> LifetimeConstraints::GetOutlivingLifetimes(
55 const Lifetime l) const {
56 // TODO(veluca): here we could certainly reduce complexity, for example by
57 // constructing the constraint graph instead of iterating over all constraints
58 // each time.
59 std::vector<Lifetime> stack{l};
60 llvm::DenseSet<Lifetime> visited;
61 while (!stack.empty()) {
62 Lifetime v = stack.back();
63 stack.pop_back();
64 if (visited.contains(v)) continue;
65 visited.insert(v);
66 for (auto [shorter, longer] : outlives_constraints_) {
67 if (shorter == v) {
68 stack.push_back(longer);
69 }
70 }
71 }
72 visited.erase(l);
73 return visited;
74}
75
Luca Versari31771ca2022-09-16 07:26:43 -070076llvm::Error LifetimeConstraints::ApplyToFunctionLifetimes(
Luca Versari7f2929c2023-01-16 10:15:54 -080077 FunctionLifetimes& function_lifetimes) {
Luca Versari31771ca2022-09-16 07:26:43 -070078 // We want to make output-only lifetimes as long as possible; thus, we collect
79 // those separately.
80 llvm::DenseSet<Lifetime> output_lifetimes;
81 function_lifetimes.GetReturnLifetimes().Traverse(
82 [&output_lifetimes](Lifetime l, Variance) {
83 output_lifetimes.insert(l);
84 });
85
86 // Collect all "interesting" lifetimes, i.e. all lifetimes that appear in the
87 // function call.
Luca Versariefeaf272023-01-16 10:19:28 -080088 llvm::DenseSet<Lifetime> all_interesting_lifetimes;
Luca Versari31771ca2022-09-16 07:26:43 -070089 function_lifetimes.Traverse(
Luca Versariefeaf272023-01-16 10:19:28 -080090 [&all_interesting_lifetimes](Lifetime l, Variance) {
91 all_interesting_lifetimes.insert(l);
92 });
Luca Versari31771ca2022-09-16 07:26:43 -070093
Luca Versari31771ca2022-09-16 07:26:43 -070094 LifetimeSubstitutions substitutions;
95
96 // Keep track of which lifetimes already have their final substitutions
97 // computed.
98 llvm::DenseSet<Lifetime> already_have_substitutions;
99
100 // First of all, substitute everything that outlives 'static with 'static.
Luca Versari7f2929c2023-01-16 10:15:54 -0800101 for (Lifetime outlives_static : GetOutlivingLifetimes(Lifetime::Static())) {
Luca Versari31771ca2022-09-16 07:26:43 -0700102 if (outlives_static.IsLocal()) {
Luca Versari91a56ff2022-08-22 01:58:33 -0700103 return llvm::createStringError(llvm::inconvertibleErrorCode(),
104 "Function assigns local to static");
105 }
Luca Versari31771ca2022-09-16 07:26:43 -0700106 already_have_substitutions.insert(outlives_static);
107 substitutions.Add(outlives_static, Lifetime::Static());
108 }
Luca Versari91a56ff2022-08-22 01:58:33 -0700109
Luca Versari31771ca2022-09-16 07:26:43 -0700110 LifetimeDSU dsu;
111 dsu.MakeSet(Lifetime::Static());
Luca Versariefeaf272023-01-16 10:19:28 -0800112 for (Lifetime lifetime : all_interesting_lifetimes) {
Luca Versari31771ca2022-09-16 07:26:43 -0700113 dsu.MakeSet(lifetime);
114 }
115
Luca Versariefeaf272023-01-16 10:19:28 -0800116 for (Lifetime lifetime : all_interesting_lifetimes) {
Luca Versari7f2929c2023-01-16 10:15:54 -0800117 llvm::DenseSet<Lifetime> longer_lifetimes = GetOutlivingLifetimes(lifetime);
118 longer_lifetimes.erase(Lifetime::Static());
Luca Versari31771ca2022-09-16 07:26:43 -0700119
120 // If constrained to be outlived by 'local, replace the lifetime with
121 // 'local, or error out if 'static.
122 auto local_it =
123 std::find_if(longer_lifetimes.begin(), longer_lifetimes.end(),
124 [](Lifetime l) { return l.IsLocal(); });
125
126 if (local_it != longer_lifetimes.end()) {
127 substitutions.Add(lifetime, *local_it);
128 already_have_substitutions.insert(lifetime);
129 continue;
130 }
131
132 // Now all the longer lifetimes must be variable lifetimes. As we do not
133 // support inequalities, we simply state that they must be equivalent.
134 for (Lifetime longer : longer_lifetimes) {
135 if (already_have_substitutions.contains(longer)) continue;
Luca Versariefeaf272023-01-16 10:19:28 -0800136 if (!all_interesting_lifetimes.contains(longer)) continue;
Luca Versari31771ca2022-09-16 07:26:43 -0700137 dsu.Union(longer, lifetime);
Luca Versari91a56ff2022-08-22 01:58:33 -0700138 }
139 }
Luca Versari31771ca2022-09-16 07:26:43 -0700140
141 // Everything that is equivalent to 'static must be replaced by 'static, not
142 // by an arbitrary lifetime in the equivalence set.
143 Lifetime cc_of_static = dsu.Find(Lifetime::Static());
144
Luca Versariefeaf272023-01-16 10:19:28 -0800145 for (Lifetime lifetime : all_interesting_lifetimes) {
Luca Versari31771ca2022-09-16 07:26:43 -0700146 if (already_have_substitutions.contains(lifetime) ||
147 !lifetime.IsVariable()) {
148 continue;
149 }
150
151 Lifetime cc = dsu.Find(lifetime);
152
153 if (cc == cc_of_static) {
154 substitutions.Add(lifetime, Lifetime::Static());
155 } else {
156 substitutions.Add(lifetime, cc);
157 }
158 }
159
Luca Versari91a56ff2022-08-22 01:58:33 -0700160 function_lifetimes.SubstituteLifetimes(substitutions);
161
162 return llvm::Error::success();
163}
164
Luca Versari7f2929c2023-01-16 10:15:54 -0800165namespace {
166
167enum LifetimeRequirement {
168 kReplacementIsGe = 0x1,
169 kReplacementIsLe = 0x2,
170 kReplacementIsEq = 0x3,
171};
172
173// Computes the requirement corresponding to *composing* the two requirements
174// together; for example, using a type containing a contravariant lifetime in
175// contravariant position would result in a covariant lifetime.
176// In general, this function behaves like multiplication where Ge = -1, Le = 1,
177// Eq = 0.
178LifetimeRequirement Compose(LifetimeRequirement a, LifetimeRequirement b) {
179 if (a == LifetimeRequirement::kReplacementIsEq ||
180 b == LifetimeRequirement::kReplacementIsEq) {
181 return LifetimeRequirement::kReplacementIsEq;
182 }
183 if (a != b) {
184 return LifetimeRequirement::kReplacementIsGe;
185 }
186 return LifetimeRequirement::kReplacementIsLe;
187}
188
189void AddConstraint(LifetimeRequirement req, Lifetime obj, Lifetime replacement,
190 LifetimeConstraints& constraints) {
191 if (req & LifetimeRequirement::kReplacementIsLe) {
192 constraints.AddOutlivesConstraint(replacement, obj);
193 }
194 if (req & LifetimeRequirement::kReplacementIsGe) {
195 constraints.AddOutlivesConstraint(obj, replacement);
196 }
197}
198
199void CollectLifetimeConstraints(const ValueLifetimes&, const ValueLifetimes&,
200 LifetimeRequirement, LifetimeConstraints&);
201
Luca Versaria12c9d52023-02-23 08:40:48 -0800202void CollectLifetimeConstraints(const FunctionLifetimes&,
203 const FunctionLifetimes&, LifetimeRequirement,
204 LifetimeConstraints&);
205
Luca Versari7f2929c2023-01-16 10:15:54 -0800206// Collects all the constraints that are required to use `replacement` as a
207// replacement for `obj`, taking into account the requirements due to their
208// positions (i.e. covariant/contravariant/invariant).
209void CollectLifetimeConstraints(const ObjectLifetimes& obj,
210 const ObjectLifetimes& replacement,
211 LifetimeRequirement object_requirement,
212 LifetimeRequirement descendants_requirement,
213 LifetimeConstraints& constraints) {
214 AddConstraint(object_requirement, obj.GetLifetime(),
215 replacement.GetLifetime(), constraints);
216 CollectLifetimeConstraints(obj.GetValueLifetimes(),
217 replacement.GetValueLifetimes(),
218 descendants_requirement, constraints);
219}
220
221void CollectLifetimeConstraints(const ValueLifetimes& obj,
222 const ValueLifetimes& replacement,
223 LifetimeRequirement requirement,
224 LifetimeConstraints& constraints) {
225 assert(obj.Type().getCanonicalType() ==
226 replacement.Type().getCanonicalType());
227 if (!PointeeType(obj.Type()).isNull()) {
228 LifetimeRequirement pointee_req =
Luca Versariefeaf272023-01-16 10:19:28 -0800229 PointeeType(obj.Type()).isConstQualified()
230 ? LifetimeRequirement::kReplacementIsLe
231 : LifetimeRequirement::kReplacementIsEq;
Luca Versari7f2929c2023-01-16 10:15:54 -0800232 CollectLifetimeConstraints(obj.GetPointeeLifetimes(),
233 replacement.GetPointeeLifetimes(), requirement,
234 Compose(pointee_req, requirement), constraints);
235 }
236 if (obj.Type()->isRecordType()) {
237 assert(obj.GetNumTemplateNestingLevels() ==
238 replacement.GetNumTemplateNestingLevels());
239 for (size_t depth = 0; depth < obj.GetNumTemplateNestingLevels(); depth++) {
240 assert(obj.GetNumTemplateArgumentsAtDepth(depth) ==
241 replacement.GetNumTemplateArgumentsAtDepth(depth));
242 for (size_t idx = 0; idx < obj.GetNumTemplateArgumentsAtDepth(depth);
243 idx++) {
244 std::optional<ValueLifetimes> obj_arg =
245 obj.GetTemplateArgumentLifetimes(depth, idx);
246 std::optional<ValueLifetimes> replacement_arg =
247 replacement.GetTemplateArgumentLifetimes(depth, idx);
248 assert(obj_arg.has_value() == replacement_arg.has_value());
249 if (obj_arg.has_value() && replacement_arg.has_value()) {
250 CollectLifetimeConstraints(*obj_arg, *replacement_arg,
251 LifetimeRequirement::kReplacementIsEq,
252 constraints);
253 }
254 }
255 }
256 for (const auto& lftm_param : GetLifetimeParameters(obj.Type())) {
257 // TODO(veluca): should lifetime parameters be invariant like template
258 // parameters?
259 AddConstraint(requirement, obj.GetLifetimeParameter(lftm_param),
260 replacement.GetLifetimeParameter(lftm_param), constraints);
261 }
262 }
Luca Versaria12c9d52023-02-23 08:40:48 -0800263 if (clang::isa<clang::FunctionProtoType>(obj.Type())) {
264 CollectLifetimeConstraints(obj.GetFuncLifetimes(),
265 replacement.GetFuncLifetimes(), requirement,
266 constraints);
267 }
Luca Versari7f2929c2023-01-16 10:15:54 -0800268}
269
270void CollectLifetimeConstraints(const FunctionLifetimes& callable,
271 const FunctionLifetimes& replacement_callable,
Luca Versaria12c9d52023-02-23 08:40:48 -0800272 LifetimeRequirement requirement,
Luca Versari7f2929c2023-01-16 10:15:54 -0800273 LifetimeConstraints& constraints) {
274 for (size_t i = 0; i < callable.GetNumParams(); i++) {
Luca Versaria12c9d52023-02-23 08:40:48 -0800275 CollectLifetimeConstraints(
276 callable.GetParamLifetimes(i),
277 replacement_callable.GetParamLifetimes(i),
278 Compose(LifetimeRequirement::kReplacementIsGe, requirement),
279 constraints);
Luca Versari7f2929c2023-01-16 10:15:54 -0800280 }
281 CollectLifetimeConstraints(
282 callable.GetReturnLifetimes(), replacement_callable.GetReturnLifetimes(),
Luca Versaria12c9d52023-02-23 08:40:48 -0800283 Compose(LifetimeRequirement::kReplacementIsLe, requirement), constraints);
Luca Versari7f2929c2023-01-16 10:15:54 -0800284 if (callable.IsNonStaticMethod()) {
285 CollectLifetimeConstraints(
286 callable.GetThisLifetimes(), replacement_callable.GetThisLifetimes(),
Luca Versaria12c9d52023-02-23 08:40:48 -0800287 Compose(LifetimeRequirement::kReplacementIsGe, requirement),
288 constraints);
Luca Versari7f2929c2023-01-16 10:15:54 -0800289 }
290}
291
292} // namespace
293
294LifetimeConstraints LifetimeConstraints::ForCallableSubstitution(
295 const FunctionLifetimes& callable,
296 const FunctionLifetimes& replacement_callable) {
Luca Versariefeaf272023-01-16 10:19:28 -0800297 LifetimeConstraints constraints =
298 LifetimeConstraints::ForCallableSubstitutionFull(callable,
299 replacement_callable);
Luca Versari7f2929c2023-01-16 10:15:54 -0800300
301 llvm::DenseSet<Lifetime> all_lifetimes;
302 callable.Traverse(
303 [&all_lifetimes](Lifetime l, Variance) { all_lifetimes.insert(l); });
304
305 LifetimeConstraints ret;
306 for (auto l : all_lifetimes) {
307 for (auto outliving : constraints.GetOutlivingLifetimes(l)) {
308 if (all_lifetimes.contains(outliving)) {
309 ret.AddOutlivesConstraint(l, outliving);
310 }
311 }
312 }
313
314 return ret;
315}
316
Luca Versariefeaf272023-01-16 10:19:28 -0800317LifetimeConstraints LifetimeConstraints::ForCallableSubstitutionFull(
318 const FunctionLifetimes& callable,
319 const FunctionLifetimes& replacement_callable) {
320 LifetimeConstraints constraints;
Luca Versaria12c9d52023-02-23 08:40:48 -0800321 CollectLifetimeConstraints(callable, replacement_callable,
322 LifetimeRequirement::kReplacementIsLe,
323 constraints);
Luca Versariefeaf272023-01-16 10:19:28 -0800324 return constraints;
325}
326
Luca Versari81222852022-08-05 06:36:10 -0700327} // namespace lifetimes
328} // namespace tidy
329} // namespace clang