Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there such a thing as "function attribute correctness"?

Ok so C23 adds function attributes to the language but I'm not sure if anyone actually considered how they are supposed to be used. Consider this example:

[[deprecated]] int foo () { return 0; }
[[nodiscard]]  int bar () { return 0; }

int main()
{
  typedef int foo_t ();
  foo_t* fp1;
  foo_t* fp2;
  foo_t* fp3;

  fp1 = foo;
  fp2 = fp1;
  fp3 = bar;
  fp1();
  fp2();
  fp3();
}

Some things of note here:

  • It is not possible for me to specify an attribute in the typedef because apparently attributes are not attached to the type.
  • The only warning I get in gcc or clang for this entire program is during fp1 = foo; assignment. foo is deprecated.
  • I do not get a warning when assigning fp1 to fp2.
  • I do not get a warning when calling a deprecated function through fp1 or fp2.
  • I do not get a warning when calling fp3 but ignore/discard the result.

Furthermore, there appears to be no check between function declarations and definitions. This compiles cleanly:

[[unsequenced]] int foo ();
[[nodiscard]]   int foo ();
[[deprecated]]  int foo () { return 0; }

Why would I ever want a compiler to allow such nonsense?

Function attributes are not attached to the type, so how can we get "function attribute correctness" and making them work as intended? Or are they just plain underspecified/broken, added to the language at a whim, to be fixed at a later point when someone can be bothered to actually examine the consequences of them?

Am I missing something here?

like image 489
Lundin Avatar asked Oct 30 '25 10:10

Lundin


1 Answers

The normal use of deprecated is for a publisher of some software, such as a library, to advise clients that some item is being deprecated. For this purpose, they would mark a declaration of the function in the header they publish, not the definition of the function. When a client translation unit uses an item marked deprecated, the compiler will generate a diagnostic.

This suffices to advise the client of the problem. The fact that further diagnostics are not produced when the value of the item (or effective value, when a function designator is automatically converted to an address) is later used further, having been copied from the item, does not negate the initial diagnostic. This attribute is not intended to modify the type and to change semantics regarding type and its role in assignments and such. Its intent is to provide a diagnostic when a client uses a deprecated item, and it achieves that.

With nodiscard, it is unfortunate that the attribute cannot be propagated to a copy of the value, but shortcoming is compelled by the situation and/or C history; it is not due to faulty design of attributes. Some programs store function addresses in a common array or dynamically assign a common pointer a dynamically selected function address. The array elements or the common pointer have a single type, so we cannot specify that a nodiscard function is a different type from a non-nodiscard function. In some code structures, compiler analysis might be able to trace the value assigned to a pointer and be able to warn that p(); discards the return value when p is known to hold the address of a nodiscard function, but this is of course impossible in the general case. Fully tracking nodiscard would require execution-time support.

In summary, these attributes are not intended to provide new type features. They are intended to provide new warnings that catch some issues, and they are not intended to catch all issues. (This is not new; it is impossible for compilers to catch all errors.) They serve the purposes for which they were designed.

like image 151
Eric Postpischil Avatar answered Oct 31 '25 23:10

Eric Postpischil