Skip to content

Commit

Permalink
Added draft-lifetimes document
Browse files Browse the repository at this point in the history
  • Loading branch information
seanbaxter committed Oct 14, 2024
1 parent 4c599c1 commit 5a6a18f
Show file tree
Hide file tree
Showing 12 changed files with 2,098 additions and 2 deletions.
1,323 changes: 1,323 additions & 0 deletions docs/draft-lifetimes.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/index.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<html>
<head>
<meta http-equiv="refresh" content="0; URL=P3390R0.html">
<meta http-equiv="refresh" content="0; URL=draft.html">
</head>
<body>
Automatic redirection failed, please go to
<a href="P3390R0.html">P3390R0.html</a>.
<a href="draft.html">draft.html</a>.
</body>
</html>
571 changes: 571 additions & 0 deletions lifetimes/draft-lifetimes.md

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions lifetimes/lifetimes1.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#feature on safety

// Function parameters have different lifetime parameters.
// Return type is constrained by x's lifetime.
auto f1/(a, b)(int^/a x, int^/b y, bool pred) safe -> int^/a {
// Error:
// function auto f1/(a, b)(int^/a, int^/b) -> int^/a returns
// object with lifetime b, but b doesn't outlive a
// return y;
return pred ? x : y;
}

// Function parameters have a common lifetime parameter.
auto f2/(a)(int^/a x, int^/a y, bool pred) safe -> int^/a {
// Ok
return pred ? x : y;
}

// Error:
// cannot use lifetime elision for return type int^
auto f3(int^ x, int^ y) safe -> int^;
10 changes: 10 additions & 0 deletions lifetimes/lifetimes2.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#feature on safety

// New elision rules:
// All parameters are constrained by a common lifetime.
// The common lifetime constrains the return type.
int% f4(int% x, int% y, bool pred) safe {
// Can return either x or y, because they outlive the common lifetime
// and the common lifetime outlives the result object.
return pred ? x : y;
}
37 changes: 37 additions & 0 deletions lifetimes/lifetimes3.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#feature on safety

const int% f1(const int% x, const int% y, bool pred) safe {
// The return reference is constrained by all reference parameters: x and y.
return pred ? x : y;
}

struct Obj {
const int% f2(const int% arg) const % safe {
// Non-static member functions are constrained by the implicit
// object lifetime.
// It's OK to return `x`, because self outlives the return.
return %x;
}

const int% f3(const int% arg) const % safe {
// Error: arg does not outlive the return reference.
return arg;
}

const int% f4(const self%, const int% arg) safe {
// OK - f4 is a free function with an explicit self parameter.
return arg;
}

int x;
};

int main() {
int x = 1, y = 2;
f1(x, y, true); // OK

Obj obj { };
obj.f2(x); // OK
obj.f3(x); // Error
obj.f4(x); // OK.
}
1 change: 1 addition & 0 deletions lifetimes/lifetimes4.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#feature on safety
27 changes: 27 additions & 0 deletions lifetimes/lifetimes5.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#feature on safety

class string_view/(a) {
// Keep a borrow to a slice over the string data.
const [char; dyn]^/a p_;
public:
};

class info/(a) {
// The handle has lifetime /a.
string_view/a sv;

public:
void swap(self^, info^ rhs) safe {
string_view temp = self->sv;
self->sv = rhs->sv;
rhs->sv = temp;
}
};

void func/(a)(info/a^ lhs, info/a^ rhs) safe {
lhs.swap(rhs);
}

void func2(info^ lhs, info^ rhs) safe {
lhs.swap(rhs);
}
17 changes: 17 additions & 0 deletions lifetimes/vector1.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <vector>

void f1(std::vector<float>& vec, float& x) {
// Do vec and x alias? If so, the push_back may invalidate x.
vec.push_back(6);

// Potential UB: x may have been invalidated by the push_back.
x = 6;
}

int main() {
std::vector<float> vec;
vec.push_back(1):

// Legacy references permit aliasing.
f1(vec, vec[0]);
}
28 changes: 28 additions & 0 deletions lifetimes/vector2.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#feature on safety
#include <cstdint>

template<typename T>
class Vec {
public:
void push_back(T value) % safe;

const T% operator[](size_t idx) const % safe;
T% operator[](size_t idx) % safe;
};

void f2(Vec<float>% vec, float% x) safe {
// Does push_back potentially invalidate x?
// No! Exclusivity prevents vec and x from aliasing.
vec.push_back(7);

// Okay to store to x, because it doesn't point into vec's data.
*x = 7;
}

int main() safe {
Vec<float> vec { };
mut vec.push_back(1);

// Ill-formed: shared borrow of vec between its mutable borrow and its use
f2(mut vec, mut vec[0]);
}
31 changes: 31 additions & 0 deletions lifetimes/vector3.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#feature on safety

template<typename Key, typename Value>
class Map {
public:
// Non-static member functions do not constrain the result object to
// the function parameters.
auto get1(const Key% key) % safe -> Value%;

// Free function do constrain the result object to the f unction praameters.
auto get2(self%, const Key% key) safe -> Value%;
};

int main() safe {
Map<float, long> map { };

// Bind the key reference to a materialized temporary.
// The temporary expires at the end of this statement.
long% value1 = mut map.get1(3.14f);

// We can still access value, because it's not constrained on the
// key argument.
*value1 = 1001;

// The call to get2 constrains the returned reference to the lifetime
// of the key temporary.
long% value2 = mut map.get2(1.6186f);

// This is ill-formed, because get2's key argument is out of scope.
*value2 = 1002;
}
30 changes: 30 additions & 0 deletions lifetimes/vector4.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#feature on safety

template<typename Key, typename Value>
class Map {
public:
// Lifetime elision rules constrain the return by self.
auto get1(self^, const Key^ key) safe -> Value^;

// Use explicit parameterizations for alternate constraints.
auto get2/(a)(self^/a, const Key^/a key) safe -> Value^/a;
};

int main() safe {
Map<float, long> map { };

// Bind the key reference to a materialized temporary.
// The temporary expires at the end of this statement.
long^ value1 = mut map.get1(3.14f);

// We can still access value, because it's not constrained on the
// key argument.
*value1 = 1001;

// The call to get2 constrains the returned reference to the lifetime
// of the key temporary.
long^ value2 = mut map.get2(1.6186f);

// This is ill-formed, because get2's key argument is out of scope.
*value2 = 1002;
}

0 comments on commit 5a6a18f

Please sign in to comment.