Luca Versari | 8122285 | 2022-08-05 06:36:10 -0700 | [diff] [blame] | 1 | // 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 Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 7 | #include <llvm/ADT/DenseSet.h> |
| 8 | |
| 9 | #include <algorithm> |
| 10 | |
Luca Versari | efeaf27 | 2023-01-16 10:19:28 -0800 | [diff] [blame] | 11 | #include "lifetime_annotations/lifetime.h" |
Luca Versari | 91a56ff | 2022-08-22 01:58:33 -0700 | [diff] [blame] | 12 | #include "lifetime_annotations/lifetime_substitutions.h" |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 13 | #include "lifetime_annotations/pointee_type.h" |
Luca Versari | 91a56ff | 2022-08-22 01:58:33 -0700 | [diff] [blame] | 14 | |
Luca Versari | 8122285 | 2022-08-05 06:36:10 -0700 | [diff] [blame] | 15 | namespace clang { |
| 16 | namespace tidy { |
| 17 | namespace lifetimes { |
| 18 | |
| 19 | clang::dataflow::LatticeJoinEffect LifetimeConstraints::join( |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 20 | const LifetimeConstraints& other) { |
Luca Versari | 8122285 | 2022-08-05 06:36:10 -0700 | [diff] [blame] | 21 | 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 Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 29 | namespace { |
| 30 | |
| 31 | // Simple Disjoint-Set-Union with path compression (but no union-by-rank). This |
| 32 | // guarantees O(log n) time per operation. |
| 33 | class 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 Versari | 91a56ff | 2022-08-22 01:58:33 -0700 | [diff] [blame] | 45 | } |
| 46 | } |
| 47 | |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 48 | private: |
| 49 | llvm::DenseMap<Lifetime, Lifetime> parent_; |
| 50 | }; |
Luca Versari | 91a56ff | 2022-08-22 01:58:33 -0700 | [diff] [blame] | 51 | |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 52 | } // namespace |
Luca Versari | 91a56ff | 2022-08-22 01:58:33 -0700 | [diff] [blame] | 53 | |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 54 | llvm::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 Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 76 | llvm::Error LifetimeConstraints::ApplyToFunctionLifetimes( |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 77 | FunctionLifetimes& function_lifetimes) { |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 78 | // 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 Versari | efeaf27 | 2023-01-16 10:19:28 -0800 | [diff] [blame] | 88 | llvm::DenseSet<Lifetime> all_interesting_lifetimes; |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 89 | function_lifetimes.Traverse( |
Luca Versari | efeaf27 | 2023-01-16 10:19:28 -0800 | [diff] [blame] | 90 | [&all_interesting_lifetimes](Lifetime l, Variance) { |
| 91 | all_interesting_lifetimes.insert(l); |
| 92 | }); |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 93 | |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 94 | 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 Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 101 | for (Lifetime outlives_static : GetOutlivingLifetimes(Lifetime::Static())) { |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 102 | if (outlives_static.IsLocal()) { |
Luca Versari | 91a56ff | 2022-08-22 01:58:33 -0700 | [diff] [blame] | 103 | return llvm::createStringError(llvm::inconvertibleErrorCode(), |
| 104 | "Function assigns local to static"); |
| 105 | } |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 106 | already_have_substitutions.insert(outlives_static); |
| 107 | substitutions.Add(outlives_static, Lifetime::Static()); |
| 108 | } |
Luca Versari | 91a56ff | 2022-08-22 01:58:33 -0700 | [diff] [blame] | 109 | |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 110 | LifetimeDSU dsu; |
| 111 | dsu.MakeSet(Lifetime::Static()); |
Luca Versari | efeaf27 | 2023-01-16 10:19:28 -0800 | [diff] [blame] | 112 | for (Lifetime lifetime : all_interesting_lifetimes) { |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 113 | dsu.MakeSet(lifetime); |
| 114 | } |
| 115 | |
Luca Versari | efeaf27 | 2023-01-16 10:19:28 -0800 | [diff] [blame] | 116 | for (Lifetime lifetime : all_interesting_lifetimes) { |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 117 | llvm::DenseSet<Lifetime> longer_lifetimes = GetOutlivingLifetimes(lifetime); |
| 118 | longer_lifetimes.erase(Lifetime::Static()); |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 119 | |
| 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 Versari | efeaf27 | 2023-01-16 10:19:28 -0800 | [diff] [blame] | 136 | if (!all_interesting_lifetimes.contains(longer)) continue; |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 137 | dsu.Union(longer, lifetime); |
Luca Versari | 91a56ff | 2022-08-22 01:58:33 -0700 | [diff] [blame] | 138 | } |
| 139 | } |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 140 | |
| 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 Versari | efeaf27 | 2023-01-16 10:19:28 -0800 | [diff] [blame] | 145 | for (Lifetime lifetime : all_interesting_lifetimes) { |
Luca Versari | 31771ca | 2022-09-16 07:26:43 -0700 | [diff] [blame] | 146 | 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 Versari | 91a56ff | 2022-08-22 01:58:33 -0700 | [diff] [blame] | 160 | function_lifetimes.SubstituteLifetimes(substitutions); |
| 161 | |
| 162 | return llvm::Error::success(); |
| 163 | } |
| 164 | |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 165 | namespace { |
| 166 | |
| 167 | enum 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. |
| 178 | LifetimeRequirement 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 | |
| 189 | void 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 | |
| 199 | void CollectLifetimeConstraints(const ValueLifetimes&, const ValueLifetimes&, |
| 200 | LifetimeRequirement, LifetimeConstraints&); |
| 201 | |
Luca Versari | a12c9d5 | 2023-02-23 08:40:48 -0800 | [diff] [blame] | 202 | void CollectLifetimeConstraints(const FunctionLifetimes&, |
| 203 | const FunctionLifetimes&, LifetimeRequirement, |
| 204 | LifetimeConstraints&); |
| 205 | |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 206 | // 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). |
| 209 | void 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 | |
| 221 | void 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 Versari | efeaf27 | 2023-01-16 10:19:28 -0800 | [diff] [blame] | 229 | PointeeType(obj.Type()).isConstQualified() |
| 230 | ? LifetimeRequirement::kReplacementIsLe |
| 231 | : LifetimeRequirement::kReplacementIsEq; |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 232 | 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 Versari | a12c9d5 | 2023-02-23 08:40:48 -0800 | [diff] [blame] | 263 | if (clang::isa<clang::FunctionProtoType>(obj.Type())) { |
| 264 | CollectLifetimeConstraints(obj.GetFuncLifetimes(), |
| 265 | replacement.GetFuncLifetimes(), requirement, |
| 266 | constraints); |
| 267 | } |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 268 | } |
| 269 | |
| 270 | void CollectLifetimeConstraints(const FunctionLifetimes& callable, |
| 271 | const FunctionLifetimes& replacement_callable, |
Luca Versari | a12c9d5 | 2023-02-23 08:40:48 -0800 | [diff] [blame] | 272 | LifetimeRequirement requirement, |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 273 | LifetimeConstraints& constraints) { |
| 274 | for (size_t i = 0; i < callable.GetNumParams(); i++) { |
Luca Versari | a12c9d5 | 2023-02-23 08:40:48 -0800 | [diff] [blame] | 275 | CollectLifetimeConstraints( |
| 276 | callable.GetParamLifetimes(i), |
| 277 | replacement_callable.GetParamLifetimes(i), |
| 278 | Compose(LifetimeRequirement::kReplacementIsGe, requirement), |
| 279 | constraints); |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 280 | } |
| 281 | CollectLifetimeConstraints( |
| 282 | callable.GetReturnLifetimes(), replacement_callable.GetReturnLifetimes(), |
Luca Versari | a12c9d5 | 2023-02-23 08:40:48 -0800 | [diff] [blame] | 283 | Compose(LifetimeRequirement::kReplacementIsLe, requirement), constraints); |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 284 | if (callable.IsNonStaticMethod()) { |
| 285 | CollectLifetimeConstraints( |
| 286 | callable.GetThisLifetimes(), replacement_callable.GetThisLifetimes(), |
Luca Versari | a12c9d5 | 2023-02-23 08:40:48 -0800 | [diff] [blame] | 287 | Compose(LifetimeRequirement::kReplacementIsGe, requirement), |
| 288 | constraints); |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 289 | } |
| 290 | } |
| 291 | |
| 292 | } // namespace |
| 293 | |
| 294 | LifetimeConstraints LifetimeConstraints::ForCallableSubstitution( |
| 295 | const FunctionLifetimes& callable, |
| 296 | const FunctionLifetimes& replacement_callable) { |
Luca Versari | efeaf27 | 2023-01-16 10:19:28 -0800 | [diff] [blame] | 297 | LifetimeConstraints constraints = |
| 298 | LifetimeConstraints::ForCallableSubstitutionFull(callable, |
| 299 | replacement_callable); |
Luca Versari | 7f2929c | 2023-01-16 10:15:54 -0800 | [diff] [blame] | 300 | |
| 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 Versari | efeaf27 | 2023-01-16 10:19:28 -0800 | [diff] [blame] | 317 | LifetimeConstraints LifetimeConstraints::ForCallableSubstitutionFull( |
| 318 | const FunctionLifetimes& callable, |
| 319 | const FunctionLifetimes& replacement_callable) { |
| 320 | LifetimeConstraints constraints; |
Luca Versari | a12c9d5 | 2023-02-23 08:40:48 -0800 | [diff] [blame] | 321 | CollectLifetimeConstraints(callable, replacement_callable, |
| 322 | LifetimeRequirement::kReplacementIsLe, |
| 323 | constraints); |
Luca Versari | efeaf27 | 2023-01-16 10:19:28 -0800 | [diff] [blame] | 324 | return constraints; |
| 325 | } |
| 326 | |
Luca Versari | 8122285 | 2022-08-05 06:36:10 -0700 | [diff] [blame] | 327 | } // namespace lifetimes |
| 328 | } // namespace tidy |
| 329 | } // namespace clang |