================
@@ -688,22 +689,45 @@ class Environment {
   /// and functions referenced in `FuncDecl`. `FuncDecl` must have a body.
   void initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl);
 
+  static PrValueToResultObject
+  buildResultObjectMap(DataflowAnalysisContext *DACtx,
+                       const FunctionDecl *FuncDecl,
+                       RecordStorageLocation *ThisPointeeLoc,
+                       RecordStorageLocation *LocForRecordReturnVal);
+
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
-  // FIXME: move the fields `CallStack`, `ReturnVal`, `ReturnLoc` and
-  // `ThisPointeeLoc` into a separate call-context object, shared between
-  // environments in the same call.
+  // FIXME: move the fields `CallStack`, `ResultObjectMap`, `ReturnVal`,
+  // `ReturnLoc` and `ThisPointeeLoc` into a separate call-context object,
+  // shared between environments in the same call.
   // https://github.com/llvm/llvm-project/issues/59005
 
   // `DeclContext` of the block being analysed if provided.
   std::vector<const DeclContext *> CallStack;
 
-  // Value returned by the function (if it has non-reference return type).
+  // Maps from prvalues of record type to their result objects. Shared between
+  // all environments for the same function.
+  // FIXME: It's somewhat unsatisfactory that we have to use a `shared_ptr`
+  // here, though the cost is acceptable: The overhead of a `shared_ptr` is
+  // incurred when it is copied, and this happens only relatively rarely (when
+  // we fork the environment). The need for a `shared_ptr` will go away once we
+  // introduce a shared call-context object (see above).
+  std::shared_ptr<PrValueToResultObject> ResultObjectMap;
----------------
martinboehme wrote:

> What would be the model for something like:
> 
> ```
> while (cond)
> {
>   f();
> }
> ```
> 
> where `f` returns a value. Do we have the same storage location for the 
> prvalue in all iterations?

Let me clarify what you're asking.

I think what you mean is that `f()` is declared like this, correct?

```cxx
struct S {};
S f();
```

In this case, there is indeed no glvalue of type `S` -- just the prvalue for 
the call `f()`. Normally, of course, prvalues don't have storage locations, but 
in this case, because there isn't an actual result object, we treat the 
`CallExpr` as if it was a `MaterializeTemporaryExpr` (see comment in 
`ResultObjectVisitor::VisitExpr()`).

(Just making sure up to here that we agree on the setting -- do you agree with 
all of the above?)

First of all, yes, we use the same storage location on all iterations of the 
loop -- witness the call to `DACtx.getStableStorageLocation(*E)`.

> In that case, the location is in fact a "summary" for the different 
> iterations.

I'm not sure I would agree with this. Let's assume this was a case where we 
actually have a `MaterializeTemporaryExpr` (imagine we're accessing some member 
of `S`, i.e. assume the expression in the loop is actually 
`f().some_member_fn()`). Then the compiler's codegen would very likely place 
the temporary in the same storage location on every iteration of the loop. 
(Sure, nothing in the standard mandates this AFAIK, but it's almost certainly 
what the compiler is going to do.) So the stable storage location that we use 
for the expression isn't a "summary" storage location, but it reflects the fact 
that the generated code, too, would use the same storage location.

https://github.com/llvm/llvm-project/pull/87320
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to