File: attr-lifetimebound.cpp

package info (click to toggle)
llvm-toolchain-19 1%3A19.1.7-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,998,520 kB
  • sloc: cpp: 6,951,680; ansic: 1,486,157; asm: 913,598; python: 232,024; f90: 80,126; objc: 75,281; lisp: 37,276; pascal: 16,990; sh: 10,009; ml: 5,058; perl: 4,724; awk: 3,523; makefile: 3,167; javascript: 2,504; xml: 892; fortran: 664; cs: 573
file content (267 lines) | stat: -rw-r--r-- 9,781 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
// RUN: %clang_cc1 -std=c++2a -verify %s

namespace usage_invalid {
  // FIXME: Should we diagnose a void return type?
  void voidreturn(int &param [[clang::lifetimebound]]);

  int *not_class_member() [[clang::lifetimebound]]; // expected-error {{non-member function has no implicit object parameter}}
  struct A {
    A() [[clang::lifetimebound]]; // expected-error {{cannot be applied to a constructor}}
    ~A() [[clang::lifetimebound]]; // expected-error {{cannot be applied to a destructor}}
    static int *static_class_member() [[clang::lifetimebound]]; // expected-error {{static member function has no implicit object parameter}}
    int not_function [[clang::lifetimebound]]; // expected-error {{only applies to parameters and implicit object parameters}}
    int [[clang::lifetimebound]] also_not_function; // expected-error {{cannot be applied to types}}
  };
  int *attr_with_param(int &param [[clang::lifetimebound(42)]]); // expected-error {{takes no arguments}}
}

namespace usage_ok {
  struct IntRef { int *target; };

  int &refparam(int &param [[clang::lifetimebound]]);
  int &classparam(IntRef param [[clang::lifetimebound]]);

  // Do not diagnose non-void return types; they can still be lifetime-bound.
  long long ptrintcast(int &param [[clang::lifetimebound]]) {
    return (long long)&param;
  }
  // Likewise.
  int &intptrcast(long long param [[clang::lifetimebound]]) {
    return *(int*)param;
  }

  struct A {
    A();
    A(int);
    int *class_member() [[clang::lifetimebound]];
    operator int*() [[clang::lifetimebound]];
  };

  int *p = A().class_member(); // expected-warning {{temporary whose address is used as value of local variable 'p' will be destroyed at the end of the full-expression}}
  int *q = A(); // expected-warning {{temporary whose address is used as value of local variable 'q' will be destroyed at the end of the full-expression}}
  int *r = A(1); // expected-warning {{temporary whose address is used as value of local variable 'r' will be destroyed at the end of the full-expression}}

  void test_assignment() {
    p = A().class_member(); // expected-warning {{object backing the pointer p will be destroyed at the end of the full-expression}}
    p = {A().class_member()}; // expected-warning {{object backing the pointer p will be destroyed at the end of the full-expression}}
    q = A(); // expected-warning {{object backing the pointer q will be destroyed at the end of the full-expression}}
    r = A(1); // expected-warning {{object backing the pointer r will be destroyed at the end of the full-expression}}
  }

  struct FieldCheck {
    struct Set {
      int a;
    };
    struct Pair {
      const int& a;
      int b;
      Set c;
      int * d;
    };
    Pair p;  
    FieldCheck(const int& a): p(a){}
    Pair& getR() [[clang::lifetimebound]] { return p; }
    Pair* getP() [[clang::lifetimebound]] { return &p; }
    Pair* getNoLB() { return &p; }
  };
  void test_field_access() {
    int x = 0;
    const int& a = FieldCheck{x}.getR().a;
    const int& b = FieldCheck{x}.getP()->b;   // expected-warning {{temporary bound to local reference 'b' will be destroyed at the end of the full-expression}}
    const int& c = FieldCheck{x}.getP()->c.a; // expected-warning {{temporary bound to local reference 'c' will be destroyed at the end of the full-expression}}
    const int& d = FieldCheck{x}.getNoLB()->c.a;
    const int* e = FieldCheck{x}.getR().d;
  }
}

# 1 "<std>" 1 3
namespace std {
  using size_t = __SIZE_TYPE__;
  struct string {
    string();
    string(const char*);

    char &operator[](size_t) const [[clang::lifetimebound]];
  };
  string operator""s(const char *, size_t);

  struct string_view {
    string_view();
    string_view(const char *p [[clang::lifetimebound]]);
    string_view(const string &s [[clang::lifetimebound]]);
  };
  string_view operator""sv(const char *, size_t);

  struct vector {
    int *data();
    size_t size();
  };

  template<typename K, typename V> struct map {};
}
# 68 "attr-lifetimebound.cpp" 2

using std::operator""s;
using std::operator""sv;

namespace p0936r0_examples {
  std::string_view s = "foo"s; // expected-warning {{temporary}}

  std::string operator+(std::string_view s1, std::string_view s2);
  void f() {
    std::string_view sv = "hi";
    std::string_view sv2 = sv + sv; // expected-warning {{temporary}}
    sv2 = sv + sv; // FIXME: can we infer that we should warn here too?
  }

  struct X { int a, b; };
  const int &f(const X &x [[clang::lifetimebound]]) { return x.a; }
  const int &r = f(X()); // expected-warning {{temporary}}

  char &c = std::string("hello my pretty long strong")[0]; // expected-warning {{temporary}}

  struct reversed_range {
    int *begin();
    int *end();
    int *p;
    std::size_t n;
  };
  template <typename R> reversed_range reversed(R &&r [[clang::lifetimebound]]) {
    return reversed_range{r.data(), r.size()};
  }

  std::vector make_vector();
  void use_reversed_range() {
    // FIXME: Don't expose the name of the internal range variable.
    for (auto x : reversed(make_vector())) {} // expected-warning {{temporary implicitly bound to local reference will be destroyed at the end of the full-expression}}
  }

  template <typename K, typename V>
  const V &findOrDefault(const std::map<K, V> &m [[clang::lifetimebound]],
                         const K &key,
                         const V &defvalue [[clang::lifetimebound]]);

  // FIXME: Maybe weaken the wording here: "local reference 'v' could bind to temporary that will be destroyed at end of full-expression"?
  std::map<std::string, std::string> m;
  const std::string &v = findOrDefault(m, "foo"s, "bar"s); // expected-warning {{temporary bound to local reference 'v'}}
}

// definitions for std::move, std::forward et al.
namespace std {
inline namespace foo {

template <class T> struct remove_reference {
    typedef T type;
};
template <class T> struct remove_reference<T &> {
    typedef T type;
};
template <class T> struct remove_reference<T &&> {
    typedef T type;
};

template <class T> constexpr typename remove_reference<T>::type &&move(T &&t) {
    return static_cast<typename remove_reference<T>::type>(t);
}

template <class T>
constexpr T &&forward(typename remove_reference<T>::type &t) {
    return static_cast<T &&>(t);
}

template <class T>
constexpr T &&forward(typename remove_reference<T>::type &&t) {
    return static_cast<T &&>(t);
}

template <class T> constexpr const T &as_const(T &x) { return x; }

template <class T, bool RValueRef> struct PickRef {
    using type = typename remove_reference<T>::type &;
};
template <class T> struct PickRef<T, true> {
    using type = typename remove_reference<T>::type &&;
};

template <class T> struct is_lvalue_reference {
    static constexpr bool value = false;
};

template <class T> struct is_lvalue_reference<T &> {
    static constexpr bool value = true;
};

template <class T> struct is_const {
    static constexpr bool value = false;
};

template <class T> struct is_const<const T> {
    static constexpr bool value = true;
};

template <bool B, class T, class F> struct conditional {
    using type = T;
};

template <class T, class F> struct conditional<false, T, F> {
    using type = F;
};

template <class U, class T>
using CopyConst = typename conditional<is_const<remove_reference<U>>::value,
                                       const T, T>::type;

template <class U, class T>
using OverrideRef =
    typename conditional<is_lvalue_reference<U &&>::value,
                         typename remove_reference<T>::type &,
                         typename remove_reference<T>::type &&>::type;

template <class U, class T>
using ForwardLikeRetType = OverrideRef<U &&, CopyConst<U, T>>;

template <class U>
constexpr auto forward_like(auto &&t) -> ForwardLikeRetType<U, decltype(t)> {
    return static_cast<ForwardLikeRetType<U, decltype(t)>>(t);
}

template <class T>
auto move_if_noexcept(T &t) ->
    typename PickRef<T, noexcept(T(static_cast<T &&>(t)))>::type {
    return static_cast<
        typename PickRef<T, noexcept(T(static_cast<T &&>(t)))>::type>(t);
}

template <class T> T *addressof(T &arg) {
    return reinterpret_cast<T *>(
        &const_cast<char &>(reinterpret_cast<const volatile char &>(arg)));
}

} // namespace foo
} // namespace std

namespace move_forward_et_al_examples {
  struct S {
    S &self() [[clang::lifetimebound]] { return *this; }
  };

  S &&Move = std::move(S{}); // expected-warning {{temporary bound to local reference 'Move' will be destroyed at the end of the full-expression}}
  S MoveOk = std::move(S{});

  S &&Forward = std::forward<S &&>(S{}); // expected-warning {{temporary bound to local reference 'Forward' will be destroyed at the end of the full-expression}}
  S ForwardOk = std::forward<S &&>(S{});

  S &&ForwardLike = std::forward_like<int&&>(S{}); // expected-warning {{temporary bound to local reference 'ForwardLike' will be destroyed at the end of the full-expression}}
  S ForwardLikeOk = std::forward_like<int&&>(S{});

  const S &Const = std::as_const(S{}.self()); // expected-warning {{temporary bound to local reference 'Const' will be destroyed at the end of the full-expression}}
  const S ConstOk = std::as_const(S{}.self());

  S &&MoveIfNoExcept = std::move_if_noexcept(S{}.self()); // expected-warning {{temporary bound to local reference 'MoveIfNoExcept' will be destroyed at the end of the full-expression}}
  S MoveIfNoExceptOk = std::move_if_noexcept(S{}.self());

  S *AddressOf = std::addressof(S{}.self()); // expected-warning {{temporary whose address is used as value of local variable 'AddressOf' will be destroyed at the end of the full-expression}}
  S X;
  S *AddressOfOk = std::addressof(X);
} // namespace move_forward_et_al_examples