blob: abecc0320a15217eba3b03f4f74b2c46d8519713 [file] [log] [blame]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "lifetime_analysis/lifetime_constraints.h"
#include "lifetime_annotations/lifetime_substitutions.h"
namespace clang {
namespace tidy {
namespace lifetimes {
clang::dataflow::LatticeJoinEffect LifetimeConstraints::join(
const LifetimeConstraints& other) {
bool changed = false;
for (auto p : other.outlives_constraints_) {
changed |= outlives_constraints_.insert(p).second;
}
return changed ? clang::dataflow::LatticeJoinEffect::Changed
: clang::dataflow::LatticeJoinEffect::Unchanged;
}
llvm::Error LifetimeConstraints::ApplyToFunctionLifetimes(
FunctionLifetimes& function_lifetimes) {
// Since we do not support "outlives" annotations, we can simply
// find all the *connected* components on an undirected graph where there is
// an edge between lifetime a and lifetime b iff there is a constraint in
// either direction between a and b, ignoring `static` and `local` (which are
// handled separately).
// TODO(veluca): do this properly by collapsing SCCs and analyzing the
// resulting outlives DAG if/when we support "outlives" annotations.
llvm::DenseMap<Lifetime, llvm::SmallVector<Lifetime>> outlives_edges;
llvm::DenseSet<Lifetime> all_lifetimes;
// CCs that contain any of these lifetimes must be substituted with `$static`.
llvm::DenseSet<Lifetime> outlives_static;
llvm::DenseMap<Lifetime, Lifetime> is_outlived_by_local;
for (auto [shorter, longer] : outlives_constraints_) {
all_lifetimes.insert(longer);
all_lifetimes.insert(shorter);
if (shorter.IsVariable() && longer.IsVariable()) {
outlives_edges[longer].push_back(shorter);
outlives_edges[shorter].push_back(longer);
}
if (shorter == Lifetime::Static()) {
outlives_static.insert(longer);
}
if (longer.IsLocal()) {
is_outlived_by_local[shorter] = longer;
}
}
llvm::DenseSet<Lifetime> visited;
LifetimeSubstitutions substitutions;
for (const auto& lifetime : all_lifetimes) {
if (!lifetime.IsVariable() || visited.count(lifetime)) continue;
llvm::SmallVector<Lifetime> connected_component;
llvm::SmallVector<Lifetime> stack;
stack.push_back(lifetime);
bool cc_outlives_static = false;
bool cc_is_outlived_by_local = false;
Lifetime local_outliving_lifetime;
while (!stack.empty()) {
Lifetime cur = stack.back();
stack.pop_back();
if (visited.count(cur)) continue;
visited.insert(cur);
if (outlives_static.count(cur)) {
cc_outlives_static = true;
}
if (is_outlived_by_local.count(cur)) {
cc_is_outlived_by_local = true;
local_outliving_lifetime = is_outlived_by_local[cur];
}
connected_component.push_back(cur);
for (auto next : outlives_edges[cur]) {
stack.push_back(next);
}
}
if (cc_outlives_static && cc_is_outlived_by_local) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Function assigns local to static");
}
Lifetime representative = cc_outlives_static ? Lifetime::Static()
: cc_is_outlived_by_local
? local_outliving_lifetime
: lifetime;
for (Lifetime memb : connected_component) {
substitutions.Add(memb, representative);
}
}
function_lifetimes.SubstituteLifetimes(substitutions);
return llvm::Error::success();
}
} // namespace lifetimes
} // namespace tidy
} // namespace clang