forked from OSchip/llvm-project
270 lines
13 KiB
C++
270 lines
13 KiB
C++
// 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}}
|
|
}
|