RFC: Bounds Safety in C Syntax Compatibility with GCC

Good point. One question, is there any reason for the return value to be anything other than size_t?

Nested functions? (Just kidding :slight_smile:)

This is a bit off the top of my head as I haven’t thought about this situation too heavily. Unfortunately, some ideas require changes to C (at the very least).

@uecker previously suggested using a “forward parameter declaration” for this.

void foo(int *outCount; int *__counted_by(*outCount) ptr, int *outCount) ...

It’s similar to the forward declaration syntax we suggested further up, but it doesn’t involve changing the expression in __counted_by. It does involve changing the language, but it apparently has some support within the working group. @uecker could expand on this further, if he’s still in favor of the syntax.

If we didn’t want to go that route, perhaps we could use something along the lines of a lambda expression. I know C doesn’t have those, so this also requires a change in the base language. Though Clang supports “blocks”, which are similar to lambdas. I’m not sure whether GCC has a lambda-like feature.

// C++ example as I don't know the 'blocks' syntax.
void foo(int * __counted_by(update_count) ptr, int *outCount) {
  auto update_count = [&](void) -> size_t { return *outCount; };
  // ...
}

Like I said, these are off the top of my head, so they will have drawbacks.

I like it for its simplicity, but it might lead to some issues. For instance, if we go with @kees’s suggestion of synthesizing a function prototype if one doesn’t exist and id hasn’t been encountered before, how do we differentiate between id as a variable or function? Also, what if id is both a field and a function?

int foo();

struct x {
    int __counted_by(foo) *bar;
    int foo;
};

Using __counted_by_func makes it more explicit.

Yeah, me too. I suggested void * due to the need to forward declare the function prototype, but if we don’t have to do that then there’s no need.

See what I wrote above for @rapidsna’s similar question.

I know and I wasn’t trying to minimize the impact this feature would have. I don’t have much in the way of a good answer to this as, like I said, I haven’t seen the code you’re using to perform the analyses. Am I correct that it’s at least possible to get the information from the AST when needed?

One reason I want the functions to be static (and hopefully inline) is to keep it close to the structure it’s associated with, thus removing, or at least limiting, this worry. We wouldn’t be able to stop someone from doing something like:

// some_header.h
struct b;
static size_t __pure inline count_for_struct_b(struct b *ptr) {
 // ...
}

// some_other_header.h
#include "somewhere/in/limbo/some_header.h"

struct b {
  int * __counted_by(count_for_struct_b) buf;
};

(This is a degenerate case worthy of the IOCCC.)

Making it static should ensure that the function is within the same translation unit as the struct’s definition and usage.

If we wanted to go that route (placing the attribute somewhere other than the fields / variables), I would suggest the alternative @hnrklssn wrote (in response to __counts):

However, this doesn’t easily extend to parameters. And I agree with the downsides he mentions in the same post.