Okay, analyzer design time.
C++ has a distinction between lvalues and rvalues (actually glvalues and prvalues). There are a number of things that go into this distinction, but the part the analyzer cares about is that glvalues refer to true objects and prvalues do not. (In C++ parlance, “object” refers to anything with storage, a type, and lifetime, not just class instances. In the analyzer, we call such things “regions”.)
If you were to build a very literal C++ compiler, every glvalue would be represented by an address. PRValues*, on the other hand, don’t really need addresses; they only exist to be used transiently.
* How do you capitalize “prvalue”?
The analyzer pretty much always believes the compiler when it comes to glvalues vs. prvalues. That means that the value of a glvalue expression is always a Loc. A prvalue expression, on the other hand, will only be a Loc if it’s a pointer type*. If you’re talking about a struct prvalue, what you’re really talking about is the bindings in its memory region—it has no identity. This is represented by a CompoundVal or LazyCompoundVal. This model worked great for C, but it’s starting to show strain in C++.
* or one of the other types mentioned in Loc::isLocType. But not all of those can be expression values.
Besides “regular” temporaries, this is also a problem for prvalue function arguments. When the analyzer inlines a function, it just blithely binds the arguments to the proper VarRegions created for each ParmVarDecl in the current stack frame. That doesn’t really fly if we’re supposed to be calling the copy constructor. Worse, the copy constructor call happens outside of the function call, which means it happens before we’ve even decided whether or not to inline, which means there are no VarRegions yet to construct into. We’ve/I’ve been putting off dealing with this one for a while; we get around it mostly because we don’t inline constructors for temporaries.
The general temporary problem goes to what Richard was saying: a temporary expression is an rvalue, but if we have to run a constructor then clearly there was a region at some point. The C++11 standard (at least the version I have) is not wonderful about classifying temporaries ([basic.lval]p1):
- An lvalue…designates a function or an object.
- An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime.
- A glvalue (“generalized” lvalue) is an lvalue or an xvalue.
- An *rvalue…*is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated with an object.
- A prvalue (“pure” rvalue) is an rvalue that is not an xvalue.
So temporary objects are prvalues. Even though they are modifiable (defined later in the same section). Even though with construction and destruction they end up needing a fixed region, or at least consistency in region contents.
I don’t have a great answer here. My guess is we’ll probably end up turning on some inlining for temporaries to start, but not all.
Oh, right, so what’s that ‘isTemporaryPRValue’ doing? That’s taking the few cases where the AST wants a prvalue but the analyzer really needs a valid Loc, and conjuring a region on the fly. That’s important for things that need to have referential identity, like the ‘this’ pointer in a method called on a temporary, or (say) a temporary that’s been lifetime-extended.
Yuck, right? Just making all temporaries into xvalues would most likely make 60% of these problems go away.