summaryrefslogtreecommitdiffstats
path: root/build/clang-plugin/tests/TestTemporaryLifetimeBound.cpp
blob: 0c51182cabd9db7a45b378a33914240a49c94c33 (plain)
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
#define MOZ_LIFETIME_BOUND __attribute__((annotate("moz_lifetime_bound")))

struct Foo {};

struct Bar {
  MOZ_LIFETIME_BOUND const Foo &AsFoo() const; // expected-note {{member function declared here}} expected-note {{member function declared here}} expected-note {{member function declared here}} expected-note {{member function declared here}} expected-note {{member function declared here}}
  MOZ_LIFETIME_BOUND operator const Foo &() const; // expected-note {{member function declared here}} expected-note {{member function declared here}} expected-note {{member function declared here}} expected-note {{member function declared here}} expected-note {{member function declared here}} expected-note {{member function declared here}}
};

Bar MakeBar() { return Bar(); }

Bar testReturnsInstance_Constructed() { return Bar(); }

const Foo &testReturnsReference_Static() {
  static constexpr auto bar = Bar{};
  return bar;
}

/* TODO This is bad as well... but not related to a temporary.
const Foo& testReturnsReference_Local() {
  constexpr auto bar = Bar{};
  return bar;
}
*/

const Foo &testReturnsReferenceToTemporaryViaLifetimeBound_Constructed() {
  return Bar(); // expected-error {{cannot return result of lifetime-bound function 'operator const Foo &' on temporary of type 'Bar'}}
}

const Foo &testReturnsReferenceToTemporaryViaLifetimeBound2_Constructed() {
  return static_cast<const Foo &>(Bar()); // expected-error {{cannot return result of lifetime-bound function 'operator const Foo &' on temporary of type 'Bar'}}
}

const Foo &testReturnsReferenceToTemporaryViaLifetimeBound3_Constructed() {
  return Bar().AsFoo(); // expected-error {{cannot return result of lifetime-bound function 'AsFoo' on temporary of type 'Bar'}}
}

const Foo &
testReturnsReferenceToTemporaryViaLifetimeBound4_Constructed(bool aCond) {
  static constexpr Foo foo;
  return aCond ? foo : Bar().AsFoo(); // expected-error {{cannot return result of lifetime-bound function 'AsFoo' on temporary of type 'Bar'}}
}

Foo testReturnsValueViaLifetimeBoundFunction_Constructed() { return Bar(); }

Foo testReturnsValueViaLifetimeBoundFunction2_Constructed() {
  return static_cast<const Foo &>(Bar());
}

Foo testReturnsValueViaLifetimeBoundFunction3_Constructed() {
  return Bar().AsFoo();
}

Bar testReturnInstance_Returned() { return MakeBar(); }

const Foo &testReturnsReferenceToTemporaryViaLifetimeBound_Returned() {
  return MakeBar(); // expected-error {{cannot return result of lifetime-bound function 'operator const Foo &' on temporary of type 'Bar'}}
}

const Foo &testReturnsReferenceToTemporaryViaLifetimeBound2_Returned() {
  return static_cast<const Foo &>(MakeBar()); // expected-error {{cannot return result of lifetime-bound function 'operator const Foo &' on temporary of type 'Bar'}}
}

const Foo &testReturnsReferenceToTemporaryViaLifetimeBound3_Returned() {
  return MakeBar().AsFoo(); // expected-error {{cannot return result of lifetime-bound function 'AsFoo' on temporary of type 'Bar'}}
}

Foo testReturnsValueViaLifetimeBoundFunction_Returned() { return MakeBar(); }

Foo testReturnsValueViaLifetimeBoundFunction2_Returned() {
  return static_cast<const Foo &>(MakeBar());
}

Foo testReturnsValueViaLifetimeBoundFunction3_Returned() {
  return MakeBar().AsFoo();
}

void testNoLifetimeExtension() {
  const Foo &foo = Bar(); // expected-error {{cannot bind result of lifetime-bound function 'operator const Foo &' on temporary of type 'Bar' to reference, does not extend lifetime}}
}

void testNoLifetimeExtension2() {
  const auto &foo = static_cast<const Foo &>(MakeBar()); // expected-error {{cannot bind result of lifetime-bound function 'operator const Foo &' on temporary of type 'Bar' to reference, does not extend lifetime}}
}

void testNoLifetimeExtension3() {
  const Foo &foo = Bar().AsFoo(); // expected-error {{cannot bind result of lifetime-bound function 'AsFoo' on temporary of type 'Bar' to reference, does not extend lifetime}}
}

void testNoLifetimeExtension4(bool arg) {
  const Foo foo;
  const Foo &fooRef = arg ? foo : Bar().AsFoo(); // expected-error {{cannot bind result of lifetime-bound function 'AsFoo' on temporary of type 'Bar' to reference, does not extend lifetime}}
}

// While this looks similar to testNoLifetimeExtension4, this is actually fine,
// as the coerced type of the conditional operator is `Foo` here rather than
// `const Foo&`, and thus an implicit copy of `Bar().AsFoo()` is created, whose
// lifetime is actually extended.
void testLifetimeExtension(bool arg) {
  const Foo &foo = arg ? Foo() : Bar().AsFoo();
}

void testConvertToValue() { const Foo foo = Bar(); }

Foo testReturnConvertToValue() {
  return static_cast<Foo>(Bar());
}

void FooFunc(const Foo &aFoo);

// We want to allow binding to parameters of the target reference type though.
// This is the very reason the annotation is required, and the function cannot
// be restricted to lvalues. Lifetime is not an issue here, as the temporary's
// lifetime is until the end of the full expression anyway.

void testBindToParameter() {
  FooFunc(Bar());
  FooFunc(static_cast<const Foo &>(Bar()));
  FooFunc(Bar().AsFoo());
  FooFunc(MakeBar());
}

// This should be OK, because the return value isn't necessarily coming from the
// argument (and it should be OK for any type).
const Foo &RandomFunctionCall(const Foo &aFoo);
const Foo &testReturnFunctionCall() { return RandomFunctionCall(Bar()); }