Compile time checks and GCC's '__warning__' attribute

Hi,

I’ve been trying to implement some basic compile time checks such as using “__builtin_object_size” to verify certain preconditions (such as ensuring a function that is passed a byte array contains a minimum number of bytes). This turned out to be a little tricker than expected as “_Static_assert” and similar mechanisms can’t be used since “__builtin_object_size” can’t be used as a integral constant expression.

Most GCC code I’ve looked at uses the “warning” or “error” attributes to declare trap functions that if not eliminated via dead-code elimination will generate a compiler warning or error (big improvement over just a runtime abort). For example:

size_t d_len = __builtin_object_size(d, 0);
if (__builtin_constant_p(copy_amount) && (copy_amount > d_len)) {
// declared with attribute((error(“memcpy called with size bigger than destination”)));
__memcpy_dest_size_error();
}

Sadly neither of these attributes are currently supported in Clang. Is this a reasonable feature request or does Clang have some better mechanism? (Perhaps the Static Analyzer?)

I notice that Clang supports both the “deprecated” and “unavailable” attributes, but these serve a slightly different purpose of warning or preventing a user from calling certain functions.

Probably the most promising option appears to be Clang 3.5’s new “enable_if” function attribute that could be used to make the function unavailable if it violates certain preconditions.

Cheers,
David

[Moving from llvmdev to cfe-dev]

[Moving from llvmdev to cfe-dev]

Ah. Apologies for the wrong list.

__builtin_object_size can be used as a constant expression in some cases
(essentially, when its operand is a constant expression).

I was actually a little surprised that it's not always a constant
expression - I would have assumed that if it wasn't a constant expression,
the worst it would do is turn into a (size_t) -1 for type 0.

I'm guessing the reason is that this can only be determined at a later
stage (after optimization).

enable_if should do the job here; it's designed for exactly this sort of
check. Something like:

void *memcpy(void *dest, const void *src, size_t n)
  __attribute__((enable_if(__builtin_object_size(dest, 0) < n, "selected
if dest is too small"),
                 unavailable("dest of memcpy is smaller than size of
buffer to copy"),
                 overloadable));
void *memcpy(void *dest, const void *src, size_t n)
  __attribute__((enable_if(__builtin_object_size(src, 0) < n, "selected if
src is too small"),
                 unavailable("src of memcpy is smaller than size of buffer
to copy"),
                 overloadable));
void *memcpy(void *dest, const void *src, size_t n) asm("memcpy")
  __attribute__((overloadable));

... should work.

Ah, awesome. Will certainly try this out.

I was kind of hoping for something that would have been somewhat portable
between GCC and Clang (even if it involved some macro magic).

Hopefully GCC picks up these attributes - feels like a much more explicit
mechanism than "did this function call get optimized out". About the only
downside I see is that "overloadable" probably means name mangling, though
the docs mention that can be avoided by using "static inline".

Cheers!