================
@@ -9,10 +9,47 @@
#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H
#define LLVM_CLANG_ANALYSIS_SCALABLE_TUSUMMARY_TUSUMMARYBUILDER_H
+#include "clang/Analysis/Scalable/Model/EntityId.h"
+#include "clang/Analysis/Scalable/TUSummary/EntitySummary.h"
+#include <memory>
+#include <utility>
+
namespace clang::ssaf {
+class EntityName;
+class TUSummary;
+
class TUSummaryBuilder {
- // Empty for now.
+public:
+ explicit TUSummaryBuilder(TUSummary &Summary) : Summary(Summary) {}
+
+ /// Add an entity to the summary and return its EntityId.
+ /// If the entity already exists, returns the existing ID (idempotent).
+ EntityId addEntity(const EntityName &E);
+
+ /// Associate the \p Data EntitySummary with the \p Entity.
+ /// This consumes the \p Data only if \p Entity wasn't associated yet with
the
+ /// same kind of EntitySummary.
+ /// \returns a pointer to the EntitySummary and whether it inserted or not.
+ template <typename ConcreteEntitySummary,
+ DerivesFromEntitySummary<ConcreteEntitySummary> * = nullptr>
+ std::pair<EntitySummary *, bool>
+ addSummary(EntityId Entity, std::unique_ptr<ConcreteEntitySummary> &&Data) {
+ std::unique_ptr<EntitySummary> TypeErasedData = std::move(Data);
+ auto [It, Inserted] = addSummaryImpl(Entity, std::move(TypeErasedData));
+ // Move it back on failue to keep the `Data` unconsumed.
+ if (!Inserted) {
+ Data = std::unique_ptr<ConcreteEntitySummary>(
+ static_cast<ConcreteEntitySummary *>(TypeErasedData.release()));
+ }
+ return {It, Inserted};
+ }
----------------
steakhal wrote:
The problem here is that most people would use
`std::make_unique<FancySummary>(...)` that would create a
`std::unique_ptr<FancySummary>`.
However, the `addSummary` would only take a `std::unique_ptr<EntitySummary>` so
a conversion would need to happen; so at the callsite it would more-or-less
implicitly look like this:
```c++
auto Summ = std::make_unique<FancySummary>(...);
Builder.addSummary(ID, std::unique_ptr<EntitySummary>(std::move(Summ))); // <--
implicit uptr conversion is now spelled out.
```
And that means that the callsite has no way of recovering the FancySummary
object if the insertion failed.
Constructing a FancySummary might be expensive, so we don't want to lose it.
However, with the templated addSummary, I can restore back the summary as-if
the move didn't happen.
This is natural. The `try_emplace` has the same contract - it only moves on
insertion.
Now the question is, how should an extractor recover a failed insertion?
It could take the existing summary and "merge" it with the one it wanted to
insert. Since the `addSummary` returns the pointer to the object it can store
back the result without an additional traversal of the map.
It could sanity check that the existing summary is semantically equivalent to
the one it wanted to insert.
Or it can discard it - and assume the existing one is "good enough".
Finally, it could just assert that the insertion must always go through and
abort if that's not the case.
Remember, it depends on the analysis (extractor) if a duplicated EntitySummary
is fine or not.
For example, extracting some information about definitions. We can have
duplicated definitions of some types, like enums for example, or even structs
in some context. This would mean that `addSummary` would need to deal with
duplicated EntitySummaries of this kind.
For the most part, C++ is kind of guarded by this under the One definition
rule, but this might not always hold for other types of program properties.
https://github.com/llvm/llvm-project/pull/181220
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits