Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apply function attribute to destructor

Tags:

c++

attributes

Is it possible to apply attributes to destructors? Example:

#if defined (__GNUC__) && !defined(__clang__)
#  define TEST_PRE_ATTR [[deprecated]] __attribute__((deprecated))
#  define TEST_POST_ATTR __attribute__((error("test")))
#elif defined(_MSC_VER) && !defined(__clang__)
#  define TEST_PRE_ATTR [[deprecated]] __declspec(deprecated)
#  define TEST_POST_ATTR
#elif defined (__clang__)
#  define TEST_PRE_ATTR [[deprecated]] __attribute__((deprecated))
#  define TEST_POST_ATTR __attribute__((diagnose_if(true, "test", "error")))
#endif

struct Foo {
    //TEST_PRE_ATTR 
    void bar() 
    //TEST_POST_ATTR 
    {}

    TEST_PRE_ATTR
    ~Foo()
    TEST_POST_ATTR
    = default;
};

int main() {
    Foo* f = new Foo();
    f->bar();
    delete f;
    return 0;
}

GCC, Clang, and MSVC ignore all of these attributes on Foo::~Foo. If you apply the same attributes to Foo::bar, they create warnings as expected. According to the standard and cppreference, the grammar for destructors should allow leading and trailing attributes. (And I suppose the fact that this does not produce any syntax errors confirms that.)

In my specific case, I have an UndefinedBehaviorSanitizer build (-fsanitize=undefined) and I am trying to use __attribute__((no_sanitize("undefined"))) on a destructor to suppress an error from an upstream library (out of my control). However, I can't suppress the error because all compilers seem to be ignoring this attribute.

I have a feeling that the answer will be something extremely unsatisfying like "compilers are allowed to ignore any attribute for any reason." If that is the case, can someone suggest a workaround? I would rather not do something as heavy-handed as disabling UBSan for the entire target.

like image 865
0x5453 Avatar asked Apr 09 '21 19:04

0x5453


Video Answer


1 Answers

GCC will emit the warning with a prepositional attribute, if you replace defaulting the destructor by an empty body. Postpositional attribute will not trigger warnings.

struct Foo {
    [[deprecated,gnu::noinline,gnu::error("test")]]
    ~Foo() { __asm__(""); }
};

int main() {
    Foo* f = new Foo();
    delete f;
    return 0;
}

This outputs:

<source>: In function 'int main()':
<source>:8:12: warning: 'Foo::~Foo()' is deprecated [-Wdeprecated-declarations]
    8 |     delete f;
      |            ^
<source>:3:5: note: declared here
    3 |     ~Foo() { __asm__(""); }
      |     ^
<source>:8:12: error: call to 'Foo::~Foo' declared with attribute error: test
    8 |     delete f;
      |            ^
Compiler returned: 1

The empty __asm__ prevents inlining, to ensure the gnu::error diagnostic is triggered. Clang behaves similarly; going by the latter’s diagnostics, postpositional attributes are apparently applied to the type of this (!). MSVC, on the other hand, always ignores the [[deprecated]] attribute on destructors. (I’m not too surprised, given that it makes little sense to deprecate destructors. They may have never bothered to test this.)

I imagine that defaulting the destructor makes GCC and Clang ignore the destructor entirely and instead use built-in destruction logic to generate code instead. I haven’t yet consulted the standard to know how the specification plays into this.

like image 130
user3840170 Avatar answered Nov 15 '22 08:11

user3840170