Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ function attribute to indicate lifetime of return value is same as argument

This code has undefined behavior:

#include <string>

std::string make_str(const char* s)
{
    return s;
}

const char* get_str(const std::string& s)
{
    return s.c_str();
}

const char* bad()
{
    return get_str(make_str("hello"));
}

The bad function creates a temporary std::string and returns a pointer to its data, which is invalid as soon as the function has returned.

GCC 5+ catches this ("function returns address of local variable") but only if compiling with -O3. At more typical optimization levels including -O2, GCC compiles it without complaint, even with -Wall -Wextra. Clang never catches it unless you use the experimental -Wlifetime feature.

My question is: Can we tell the compiler explicitly about such lifetime dependencies, e.g. using an attribute? For example, I would like to be able to do this:

[[lifetime-depends : s]] // hypothetical syntax
const char* get_str(const std::string& s);

Or maybe this:

const char* get_str(const std::string& s)
  __attribute__((lifetime-depends(0))); // hypothetical syntax

I'll accept an answer that works with any official release of GCC or Clang, but prefer GCC 6.1 with C++14. Clang's experimental -Wlifetime is not an answer, because I want to be explicit, not rely on heuristics (which seemingly wouldn't work across multiple translation units anyway).

Alternatively, I'll accept an answer providing reasons why this wouldn't be useful or feasible to implement.

like image 929
John Zwinck Avatar asked Feb 07 '19 09:02

John Zwinck


1 Answers

Opinion based answer which does not provide an answer, but questions the idea behind the question (and was too much text for a comment):

IMHO that would imply a quite immense reliability on compiler intelligence to catch user errors, because that is the code you posted, a failure. The standard already covers how to handle lifetimes of temporaries and does consider extending the lifetime in cases the reference is still in use (see draft n4713 eg. 15.2 Bullet 5). However, your posted example is a different case...

Also, while the Paper p1179r0 (comment by P.W) does provide an idea for pointers to objects stored on the stack it does not help with objects on the heap. And how should it? Returning a pointer to char (or any type) is not an error... returning a pointer to an array which does not exist anymore, is. How should it know c_str() returns a pointer to an array of a temporary wich does delete in it's destructor (kinda specific, isn't it?)? How to know if the array is destroyed? By looking into the destructor if a delete is done? What if the destructor does not delete? Warn about destructor should delete? What if the new was a placement new? What if.... factory pattern...? What if creating pointers for smart pointer (e.g: shared_ptr)...? What if another 200 points... Managing lifetime on the heap, would mostly result in garbage collection.

The warning you get is function returns address of local variable [-Wreturn-local-addr] which, I think, has nothing to do with your code, but is more an "accident" caused by short string optimization. SSO optimized std::string does return a pointer to a local as the message says... Simple test, make your string longer than 16 chars, the warning disappears...

like image 156
user1810087 Avatar answered Oct 19 '22 03:10

user1810087