| // 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/visit_lifetimes.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "lifetime_analysis/object.h" |
| #include "lifetime_analysis/object_set.h" |
| #include "lifetime_annotations/pointee_type.h" |
| #include "lifetime_annotations/type_lifetimes.h" |
| #include "clang/AST/Attr.h" |
| #include "clang/AST/Attrs.inc" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/Type.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringRef.h" |
| |
| namespace clang { |
| namespace tidy { |
| namespace lifetimes { |
| namespace { |
| |
| llvm::SmallVector<std::string> GetFieldLifetimeArguments( |
| const clang::FieldDecl* field) { |
| // TODO(mboehme): Report errors as Clang diagnostics, not through |
| // llvm::report_fatal_error(). |
| |
| const clang::AnnotateAttr* member_lifetimes_attr = nullptr; |
| for (auto annotate : field->specific_attrs<clang::AnnotateAttr>()) { |
| if (annotate->getAnnotation() == "member_lifetimes") { |
| if (member_lifetimes_attr) { |
| llvm::report_fatal_error("repeated lifetime annotation"); |
| } |
| member_lifetimes_attr = annotate; |
| } |
| } |
| if (!member_lifetimes_attr) { |
| return {}; |
| } |
| |
| llvm::SmallVector<std::string> ret; |
| for (const auto& arg : member_lifetimes_attr->args()) { |
| llvm::StringRef lifetime; |
| if (llvm::Error err = EvaluateAsStringLiteral(arg, field->getASTContext()) |
| .moveInto(lifetime)) { |
| llvm::report_fatal_error(llvm::StringRef(toString(std::move(err)))); |
| } |
| ret.push_back(lifetime.str()); |
| } |
| |
| return ret; |
| } |
| |
| template <typename Callback> |
| void ForEachField(ObjectSet objects, clang::QualType record_type, |
| const ObjectLifetimes& object_lifetimes, |
| LifetimeVisitor& visitor, const Callback& callback) { |
| for (clang::FieldDecl* f : |
| record_type->getAs<clang::RecordType>()->getDecl()->fields()) { |
| ObjectLifetimes field_lifetimes = object_lifetimes.GetFieldOrBaseLifetimes( |
| f->getType(), GetFieldLifetimeArguments(f)); |
| callback(objects, field_lifetimes, f); |
| } |
| if (auto* cxxrecord = clang::dyn_cast<clang::CXXRecordDecl>( |
| record_type->getAs<clang::RecordType>()->getDecl())) { |
| for (const clang::CXXBaseSpecifier& base : cxxrecord->bases()) { |
| auto base_object_lifetimes = object_lifetimes.GetFieldOrBaseLifetimes( |
| base.getType(), GetLifetimeParameters(base.getType())); |
| auto base_object = visitor.GetBaseClassObject(objects, base.getType()); |
| ObjectSet next_objects = objects; |
| next_objects.Add(base_object); |
| ForEachField(next_objects, base.getType(), base_object_lifetimes, visitor, |
| callback); |
| } |
| } |
| } |
| |
| void VisitLifetimesImpl(const ObjectSet& points_to_set, |
| const ObjectLifetimes& object_lifetimes, |
| llvm::DenseSet<const Object*>& visited_objects, |
| LifetimeVisitor& visitor, int pointee_depth); |
| |
| // Traverse fields while walking up base classes. This can be a bit wasteful |
| // for cases like diamond inheritance (which is hopefully not common). |
| void TraverseObjectFieldsWithBases( |
| const ObjectSet& object_set, clang::QualType record_type, |
| const ObjectLifetimes& object_lifetimes, |
| llvm::DenseSet<const Object*>& visited_object, LifetimeVisitor& visitor, |
| int pointee_depth) { |
| assert(record_type->isRecordType()); |
| if (record_type->isIncompleteType()) { |
| return; |
| } |
| // Our analysis relies on objects reachable in the same way to be visited in |
| // the same call, thus we need to "merge" together the `Object`s that come |
| // from the same field but different `object`s in the object_set. |
| llvm::SmallVector<std::pair<ObjectSet, ObjectLifetimes>> fields_to_visit; |
| for (const Object* object : object_set) { |
| // This code relies on the vist order of ForEachField being independent |
| // of `object`. |
| size_t next_field = 0; |
| ForEachField( |
| {object}, record_type, object_lifetimes, visitor, |
| [&](const ObjectSet& bases, const ObjectLifetimes& field_lifetimes, |
| const clang::FieldDecl* f) { |
| size_t field = next_field++; |
| if (field == fields_to_visit.size()) { |
| fields_to_visit.emplace_back(ObjectSet(), |
| std::move(field_lifetimes)); |
| } |
| const Object* field_object = visitor.GetFieldObject(bases, f); |
| fields_to_visit[field].first.Add(field_object); |
| }); |
| } |
| for (auto [objects, lifetimes] : std::move(fields_to_visit)) { |
| VisitLifetimesImpl(objects, lifetimes, visited_object, visitor, |
| pointee_depth); |
| } |
| } |
| |
| void VisitLifetimesImpl(const ObjectSet& points_to_set, |
| const ObjectLifetimes& object_lifetimes, |
| llvm::DenseSet<const Object*>& visited_objects, |
| LifetimeVisitor& visitor, int pointee_depth) { |
| size_t num_visited_before = visited_objects.size(); |
| for (const Object* object : points_to_set) { |
| visited_objects.insert(object); |
| } |
| if (num_visited_before == visited_objects.size()) { |
| // No new object -> nothing to do. This avoids infinite loops. |
| return; |
| } |
| |
| if (const clang::QualType type = object_lifetimes.GetValueLifetimes().Type(); |
| type->isRecordType()) { |
| TraverseObjectFieldsWithBases(points_to_set, type, object_lifetimes, |
| visited_objects, visitor, pointee_depth); |
| } |
| |
| // TODO(veluca): here we call Traverse even when there is no child type. |
| // This is likely an indication that it is better to split up Traverse into |
| // multiple methods. |
| clang::QualType child_type = |
| PointeeType(object_lifetimes.GetValueLifetimes().Type()); |
| |
| ObjectSet child_object = |
| visitor.Traverse(object_lifetimes, points_to_set, pointee_depth); |
| |
| if (!child_object.empty() && !child_type.isNull()) { |
| VisitLifetimesImpl( |
| child_object, |
| object_lifetimes.GetValueLifetimes().GetPointeeLifetimes(), |
| visited_objects, visitor, pointee_depth + 1); |
| } |
| } |
| |
| } // namespace |
| |
| void VisitLifetimes(const ObjectSet& points_to_set, clang::QualType type, |
| const ObjectLifetimes& object_lifetimes, |
| LifetimeVisitor& visitor) { |
| llvm::DenseSet<const Object*> visited_objects; |
| VisitLifetimesImpl(points_to_set, object_lifetimes, visited_objects, visitor, |
| /*pointee_depth=*/0); |
| } |
| |
| } // namespace lifetimes |
| } // namespace tidy |
| } // namespace clang |