carbon-lang icon indicating copy to clipboard operation
carbon-lang copied to clipboard

avoid copies when returning by value

Open zygoloid opened this issue 4 years ago • 1 comments

Instead of guaranteeing that a copy happens when returning by value, could we give a weaker guarantee that there is no lifetime issue, but that we may or may not make a copy?

What I'm thinking is: we could say that a function that returns by value may return a reference to anything that it knows lives at least as long as any of its parameters (including me). Then at the call site, we can check whether the returned value outlives any of the function parameters and make a copy if so. Then:

// F doesn't make a copy; returned value lives at least as long as parameter s
fn F(s: String) -> String { return s; }
// G makes a copy: v doesn't live long enough
fn G(s: String) -> String {
  let v: String = "foo" + s + "bar";
  return v;
}
fn Print(s: String);
fn H() {
  // Parameter of `F` can be kept alive until `;`, so we know
  // the return value lives at least that long and don't need 
  // to make any copies.
  Print(F("hello"));
  // `x` outlives the function argument, so we make a copy here.
  let x: String = F("hello");
  Print(x);
}

I suppose we can avoid making a copy even in the second case in H by lifetime-extending the "hello" temporary. I'm not sure that's a good idea; it might be too unpredictable.

One problem with this is that the function return is creating an immutable view, and we need the callee to know how long it's promising that returned value will remain immutable for. I suppose this is nothing new; this is analogous to a classic C++ issue:

const string &s = v[i];
v.push_back("x");
use(s);

... where this either works or fails depending on whether v[i] produces a reference to an existing object (eg, v is vector<string>) or ends up binding s to a temporary (eg, v is vector<const char*>). We might want some simple syntax to force a copy and end a chain of immutable views. (You could use var for that, but that also implies mutability, which might be undesirable.)

There's also a calling convention complexity issue with this kind of approach: if F can either return a handle to some existing object or copy to some caller-provided storage, then the caller always needs to provide the storage and may need to perform a branch to tell whether it should provide a copy. That seems like something we could handle but I'm not sure whether it'll be worthwhile unless we get to avoid a lot of copies.

Originally posted by @zygoloid in https://github.com/carbon-language/carbon-lang/pull/821#r706519914

zygoloid avatar Sep 14 '21 21:09 zygoloid

We triage inactive PRs and issues in order to make it easier to find active work. If this issue should remain active or becomes active again, please comment or remove the inactive label. The long term label can also be added for issues which are expected to take time. This issue is labeled inactive because the last activity was over 90 days ago.

github-actions[bot] avatar Dec 14 '21 01:12 github-actions[bot]