diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4057,9 +4057,8 @@ let Category = DocCatFunction; let Content = [{ The ``not_tail_called`` attribute prevents tail-call optimization on statically -bound calls. It has no effect on indirect calls. Virtual functions, objective-c -methods, and functions marked as ``always_inline`` cannot be marked as -``not_tail_called``. +bound calls. Objective-c methods, and functions marked as ``always_inline`` +cannot be marked as ``not_tail_called``. For example, it prevents tail-call optimization in the following case: @@ -4085,28 +4084,25 @@ return (*fn)(a); } -Marking virtual functions as ``not_tail_called`` is an error: +Generally, marking an overriding virtual function as ``not_tail_called`` is +not useful, because this attribute is a property of the static type. Calls +made through a pointer or reference to the base class type will respect +the ``not_tail_called`` attribute of the base class's member function, +regardless of the runtime destination of the call: .. code-block:: c++ - class Base { - public: - // not_tail_called on a virtual function is an error. - [[clang::not_tail_called]] virtual int foo1(); - - virtual int foo2(); - - // Non-virtual functions can be marked ``not_tail_called``. - [[clang::not_tail_called]] int foo3(); - }; - - class Derived1 : public Base { - public: - int foo1() override; - - // not_tail_called on a virtual function is an error. - [[clang::not_tail_called]] int foo2() override; + struct Foo { virtual void f(); }; + struct Bar : Foo { + [[clang::not_tail_called]] void f() override; }; + void callera(Bar& bar) { + Foo& foo = bar; + // not_tail_called has no effect on here, even though the + // underlying method is f from Bar. + foo.f(); + bar.f(); // No tail-call optimization on here. + } }]; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6432,16 +6432,6 @@ } } - // Virtual functions cannot be marked as 'notail'. - if (auto *Attr = ND.getAttr()) - if (auto *MD = dyn_cast(&ND)) - if (MD->isVirtual()) { - S.Diag(ND.getLocation(), - diag::err_invalid_attribute_on_virtual_function) - << Attr; - ND.dropAttr(); - } - // Check the attributes on the function type, if any. if (const auto *FD = dyn_cast(&ND)) { // Don't declare this variable in the second operand of the for-statement; diff --git a/clang/test/CodeGenCXX/attr-notail.cpp b/clang/test/CodeGenCXX/attr-notail.cpp --- a/clang/test/CodeGenCXX/attr-notail.cpp +++ b/clang/test/CodeGenCXX/attr-notail.cpp @@ -4,14 +4,28 @@ public: [[clang::not_tail_called]] int m1(); int m2(); + [[clang::not_tail_called]] virtual int m3(); + virtual int m4(); }; -int foo1(int a, Class1 *c1) { +class Class2: public Class1 { +public: + [[clang::not_tail_called]] int m4() override; +}; + +int foo1(int a, Class1 *c1, Class2 &c2) { if (a) return c1->m1(); + c1->m3(); + Class1 &c = c2; + c.m4(); + c2.m4(); return c1->m2(); } -// CHECK-LABEL: define{{.*}} i32 @_Z4foo1iP6Class1( +// CHECK-LABEL: define{{.*}} i32 @_Z4foo1iP6Class1R6Class2( // CHECK: %{{[a-z0-9]+}} = notail call i32 @_ZN6Class12m1Ev(%class.Class1* +// CHECK: %{{[a-z0-9]+}} = notail call i32 %{{[0-9]+}}(%class.Class1* +// CHECK-NOT: %{{[a-z0-9]+}} = notail call i32 %{{[0-9]+}}(%class.Class1* +// CHECK: %{{[a-z0-9]+}} = notail call i32 %{{[0-9]+}}(%class.Class2* // CHECK: %{{[a-z0-9]+}} = call i32 @_ZN6Class12m2Ev(%class.Class1* diff --git a/clang/test/SemaCXX/attr-notail.cpp b/clang/test/SemaCXX/attr-notail.cpp --- a/clang/test/SemaCXX/attr-notail.cpp +++ b/clang/test/SemaCXX/attr-notail.cpp @@ -1,8 +1,9 @@ // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s +// expected-no-diagnostics class Base { public: - [[clang::not_tail_called]] virtual int foo1(); // expected-error {{'not_tail_called' attribute cannot be applied to virtual functions}} + [[clang::not_tail_called]] virtual int foo1(); virtual int foo2(); [[clang::not_tail_called]] int foo3(); virtual ~Base() {} @@ -11,6 +12,6 @@ class Derived1 : public Base { public: int foo1() override; - [[clang::not_tail_called]] int foo2() override; // expected-error {{'not_tail_called' attribute cannot be applied to virtual functions}} + [[clang::not_tail_called]] int foo2() override; [[clang::not_tail_called]] int foo4(); };