File: attr-musttail.cpp

package info (click to toggle)
llvm-toolchain-13 1%3A13.0.1-11
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,418,840 kB
  • sloc: cpp: 5,290,826; ansic: 996,570; asm: 544,593; python: 188,212; objc: 72,027; lisp: 30,291; f90: 25,395; sh: 24,898; javascript: 9,780; pascal: 9,398; perl: 7,484; ml: 5,432; awk: 3,523; makefile: 2,913; xml: 953; cs: 573; fortran: 539
file content (269 lines) | stat: -rw-r--r-- 13,532 bytes parent folder | download | duplicates (15)
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
268
269
// RUN: %clang_cc1 -verify -fsyntax-only -fms-extensions -fcxx-exceptions -fopenmp -triple x86_64-linux %s

int ReturnsInt1();
int Func1() {
  [[clang::musttail]] ReturnsInt1();              // expected-error {{'musttail' attribute only applies to return statements}}
  [[clang::musttail(1, 2)]] return ReturnsInt1(); // expected-error {{'musttail' attribute takes no arguments}}
  [[clang::musttail]] return 5;                   // expected-error {{'musttail' attribute requires that the return value is the result of a function call}}
  [[clang::musttail]] return ReturnsInt1();
}

void NoFunctionCall() {
  [[clang::musttail]] return; // expected-error {{'musttail' attribute requires that the return value is the result of a function call}}
}

[[clang::musttail]] static int int_val = ReturnsInt1(); // expected-error {{'musttail' attribute cannot be applied to a declaration}}

void NoParams(); // expected-note {{target function has different number of parameters (expected 1 but has 0)}}
void TestParamArityMismatch(int x) {
  [[clang::musttail]] // expected-note {{tail call required by 'musttail' attribute here}}
  return NoParams();  // expected-error {{cannot perform a tail call to function 'NoParams' because its signature is incompatible with the calling function}}
}

void LongParam(long x); // expected-note {{target function has type mismatch at 1st parameter (expected 'long' but has 'int')}}
void TestParamTypeMismatch(int x) {
  [[clang::musttail]]  // expected-note {{tail call required by 'musttail' attribute here}}
  return LongParam(x); // expected-error {{cannot perform a tail call to function 'LongParam' because its signature is incompatible with the calling function}}
}

long ReturnsLong(); // expected-note {{target function has different return type ('int' expected but has 'long')}}
int TestReturnTypeMismatch() {
  [[clang::musttail]]   // expected-note {{tail call required by 'musttail' attribute here}}
  return ReturnsLong(); // expected-error {{cannot perform a tail call to function 'ReturnsLong' because its signature is incompatible with the calling function}}
}

struct Struct1 {
  void MemberFunction(); // expected-note {{'MemberFunction' declared here}}
};
void TestNonMemberToMember() {
  Struct1 st;
  [[clang::musttail]]         // expected-note {{tail call required by 'musttail' attribute here}}
  return st.MemberFunction(); // expected-error {{non-member function cannot perform a tail call to non-static member function 'MemberFunction'}}
}

void ReturnsVoid(); // expected-note {{'ReturnsVoid' declared here}}
struct Struct2 {
  void TestMemberToNonMember() {
    [[clang::musttail]]   // expected-note {{tail call required by 'musttail' attribute here}}
    return ReturnsVoid(); // expected-error{{non-static member function cannot perform a tail call to non-member function 'ReturnsVoid'}}
  }
};

class HasNonTrivialDestructor {
public:
  ~HasNonTrivialDestructor() {}
  int ReturnsInt();
};

void ReturnsVoid2();
void TestNonTrivialDestructorInScope() {
  HasNonTrivialDestructor foo;              // expected-note {{jump exits scope of variable with non-trivial destructor}}
  [[clang::musttail]] return ReturnsVoid(); // expected-error {{cannot perform a tail call from this return statement}}
}

int NonTrivialParam(HasNonTrivialDestructor x);
int TestNonTrivialParam(HasNonTrivialDestructor x) {
  [[clang::musttail]] return NonTrivialParam(x); // expected-error {{tail call requires that the return value, all parameters, and any temporaries created by the expression are trivially destructible}}
}

HasNonTrivialDestructor ReturnsNonTrivialValue();
HasNonTrivialDestructor TestReturnsNonTrivialValue() {
  // FIXME: the diagnostic cannot currently distinguish between needing to run a
  // destructor for the return value and needing to run a destructor for some
  // other temporary created in the return statement.
  [[clang::musttail]] return (ReturnsNonTrivialValue()); // expected-error {{tail call requires that the return value, all parameters, and any temporaries created by the expression are trivially destructible}}
}

HasNonTrivialDestructor TestReturnsNonTrivialNonFunctionCall() {
  [[clang::musttail]] return HasNonTrivialDestructor(); // expected-error {{'musttail' attribute requires that the return value is the result of a function call}}
}

struct UsesPointerToMember {
  void (UsesPointerToMember::*p_mem)(); // expected-note {{'p_mem' declared here}}
};
void TestUsesPointerToMember(UsesPointerToMember *foo) {
  // "this" pointer cannot double as first parameter.
  [[clang::musttail]]            // expected-note {{tail call required by 'musttail' attribute here}}
  return (foo->*(foo->p_mem))(); // expected-error {{non-member function cannot perform a tail call to pointer-to-member function 'p_mem'}}
}

void ReturnsVoid2();
void TestNestedClass() {
  HasNonTrivialDestructor foo;
  class Nested {
    __attribute__((noinline)) static void NestedMethod() {
      // Outer non-trivial destructor does not affect nested class.
      [[clang::musttail]] return ReturnsVoid2();
    }
  };
}

template <class T>
T TemplateFunc(T x) { // expected-note{{target function has different return type ('long' expected but has 'int')}}
  return x ? 5 : 10;
}
int OkTemplateFunc(int x) {
  [[clang::musttail]] return TemplateFunc<int>(x);
}
template <class T>
T BadTemplateFunc(T x) {
  [[clang::musttail]]          // expected-note {{tail call required by 'musttail' attribute here}}
  return TemplateFunc<int>(x); // expected-error {{cannot perform a tail call to function 'TemplateFunc' because its signature is incompatible with the calling function}}
}
long TestBadTemplateFunc(long x) {
  return BadTemplateFunc<long>(x); // expected-note {{in instantiation of}}
}

void IntParam(int x);
void TestVLA(int x) {
  HasNonTrivialDestructor vla[x];         // expected-note {{jump exits scope of variable with non-trivial destructor}}
  [[clang::musttail]] return IntParam(x); // expected-error {{cannot perform a tail call from this return statement}}
}

void TestNonTrivialDestructorSubArg(int x) {
  [[clang::musttail]] return IntParam(NonTrivialParam(HasNonTrivialDestructor())); // expected-error {{tail call requires that the return value, all parameters, and any temporaries created by the expression are trivially destructible}}
}

void VariadicFunction(int x, ...);
void TestVariadicFunction(int x, ...) {
  [[clang::musttail]] return VariadicFunction(x); // expected-error {{'musttail' attribute may not be used with variadic functions}}
}

int TakesIntParam(int x);     // expected-note {{target function has type mismatch at 1st parameter (expected 'int' but has 'short')}}
int TakesShortParam(short x); // expected-note {{target function has type mismatch at 1st parameter (expected 'short' but has 'int')}}
int TestIntParamMismatch(int x) {
  [[clang::musttail]]        // expected-note {{tail call required by 'musttail' attribute here}}
  return TakesShortParam(x); // expected-error {{cannot perform a tail call to function 'TakesShortParam' because its signature is incompatible with the calling function}}
}
int TestIntParamMismatch2(short x) {
  [[clang::musttail]]      // expected-note {{tail call required by 'musttail' attribute here}}
  return TakesIntParam(x); // expected-error {{cannot perform a tail call to function 'TakesIntParam' because its signature is incompatible with the calling function}}
}

struct TestClassMismatch1 {
  void ToFunction(); // expected-note{{target function is a member of different class (expected 'TestClassMismatch2' but has 'TestClassMismatch1')}}
};
TestClassMismatch1 *tcm1;
struct TestClassMismatch2 {
  void FromFunction() {
    [[clang::musttail]]        // expected-note {{tail call required by 'musttail' attribute here}}
    return tcm1->ToFunction(); // expected-error {{cannot perform a tail call to function 'ToFunction' because its signature is incompatible with the calling function}}
  }
};

__regcall int RegCallReturnsInt(); // expected-note {{target function has calling convention regcall (expected cdecl)}}
int TestMismatchCallingConvention() {
  [[clang::musttail]]         // expected-note {{tail call required by 'musttail' attribute here}}
  return RegCallReturnsInt(); // expected-error {{cannot perform a tail call to function 'RegCallReturnsInt' because it uses an incompatible calling convention}}
}

int TestNonCapturingLambda() {
  auto lambda = []() { return 12; }; // expected-note {{'operator()' declared here}}
  [[clang::musttail]]                // expected-note {{tail call required by 'musttail' attribute here}}
  return lambda();                   // expected-error {{non-member function cannot perform a tail call to non-static member function 'operator()'}}

  // This works.
  auto lambda_fptr = static_cast<int (*)()>(lambda);
  [[clang::musttail]] return lambda_fptr();
  [[clang::musttail]] return (+lambda)();
}

int TestCapturingLambda() {
  int x;
  auto lambda = [x]() { return 12; }; // expected-note {{'operator()' declared here}}
  [[clang::musttail]]                 // expected-note {{tail call required by 'musttail' attribute here}}
  return lambda();                    // expected-error {{non-member function cannot perform a tail call to non-static member function 'operator()'}}
}

int TestNonTrivialTemporary(int) {
  [[clang::musttail]] return TakesIntParam(HasNonTrivialDestructor().ReturnsInt()); // expected-error {{tail call requires that the return value, all parameters, and any temporaries created by the expression are trivially destructible}}
}

void ReturnsVoid();
struct TestDestructor {
  ~TestDestructor() {
    [[clang::musttail]]   // expected-note {{tail call required by 'musttail' attribute here}}
    return ReturnsVoid(); // expected-error {{destructor '~TestDestructor' must not return void expression}}  // expected-error {{cannot perform a tail call from a destructor}}
  }
};

struct ClassWithDestructor { // expected-note {{target destructor is declared here}}
  void TestExplicitDestructorCall() {
    [[clang::musttail]]                  // expected-note {{tail call required by 'musttail' attribute here}}
    return this->~ClassWithDestructor(); // expected-error {{cannot perform a tail call to a destructor}}
  }
};

struct HasNonTrivialCopyConstructor {
  HasNonTrivialCopyConstructor(const HasNonTrivialCopyConstructor &);
};
HasNonTrivialCopyConstructor ReturnsClassByValue();
HasNonTrivialCopyConstructor TestNonElidableCopyConstructor() {
  // This is an elidable constructor, but when it is written explicitly
  // we decline to elide it.
  [[clang::musttail]] return HasNonTrivialCopyConstructor(ReturnsClassByValue()); // expected-error{{'musttail' attribute requires that the return value is the result of a function call}}
}

struct ClassWithConstructor {
  ClassWithConstructor() = default; // expected-note {{target constructor is declared here}}
};
void TestExplicitConstructorCall(ClassWithConstructor a) {
  [[clang::musttail]]                                    // expected-note {{tail call required by 'musttail' attribute here}}
  return a.ClassWithConstructor::ClassWithConstructor(); // expected-error{{cannot perform a tail call to a constructor}}  expected-warning{{explicit constructor calls are a Microsoft extension}}
}

void TestStatementExpression() {
  ({
    HasNonTrivialDestructor foo;               // expected-note {{jump exits scope of variable with non-trivial destructor}}
    [[clang::musttail]] return ReturnsVoid2(); // expected-error {{cannot perform a tail call from this return statement}}
  });
}

struct MyException {};
void TestTryBlock() {
  try {                                        // expected-note {{jump exits try block}}
    [[clang::musttail]] return ReturnsVoid2(); // expected-error {{cannot perform a tail call from this return statement}}
  } catch (MyException &e) {
  }
}

using IntFunctionType = int();
IntFunctionType *ReturnsIntFunction();
long TestRValueFunctionPointer() {
  [[clang::musttail]]            // expected-note {{tail call required by 'musttail' attribute here}}
  return ReturnsIntFunction()(); // expected-error{{cannot perform a tail call to function because its signature is incompatible with the calling function}}  // expected-note{{target function has different return type ('long' expected but has 'int')}}
}

void TestPseudoDestructor() {
  int n;
  using T = int;
  [[clang::musttail]] // expected-note {{tail call required by 'musttail' attribute here}}
  return n.~T();      // expected-error{{cannot perform a tail call to a destructor}}
}

struct StructPMF {
  typedef void (StructPMF::*PMF)();
  static void TestReturnsPMF();
};

StructPMF *St;
StructPMF::PMF ReturnsPMF();
void StructPMF::TestReturnsPMF() {
  [[clang::musttail]]           // expected-note{{tail call required by 'musttail' attribute here}}
  return (St->*ReturnsPMF())(); // expected-error{{static member function cannot perform a tail call to pointer-to-member function}}
}

// These tests are merely verifying that we don't crash with incomplete or
// erroneous ASTs. These cases crashed the compiler in early iterations.

struct TestBadPMF {
  int (TestBadPMF::*pmf)();
  void BadPMF() {
    [[clang::musttail]] return ((*this)->*pmf)(); // expected-error {{left hand operand to ->* must be a pointer to class compatible with the right hand operand, but is 'TestBadPMF'}}
  }
};

namespace ns {}
void TestCallNonValue() {
  [[clang::musttail]] return ns; // expected-error {{unexpected namespace name 'ns': expected expression}}
}