> On Apr 1, 2025, at 10:04, Martin Uecker <uec...@tugraz.at> wrote: > > > > Am Montag, dem 31.03.2025 um 13:59 -0700 schrieb Bill Wendling: >>> I'd like to offer up this to solve the issues we're facing. This is a >>> combination of everything that's been discussed here (or at least that >>> I've been able to read in the centi-thread :-). > > Thanks! I think this proposal much better as it avoids undue burden > on parsers, but it does not address all my concerns. > > > From my side, the issue about compromising the scoping rules of C > is also about unintended non-local effects of code changes. In > my opinion, a change in a library header elsewhere should not cause > code in a local scope (which itself might also come from a macro) to > emit a warning or require a programmer to add a workaround. So I am > not convinced that adding warnings or a workaround such as > __builtin_global_ref is a good solution. > > > I could see the following as a possible way forward: We only > allow the following two syntaxes: > > 1. Single argument referring to a member. > > __counted_by(len) > > with an argument that must be a single identifier and where > the identifier then must refer to a struct member. > > (I still think this is not ideal and potentially > confusing, but in contrast to new scoping rules it is > at least relatively easily to explain as a special rule.). > > > 2. Forward declarations. > > __counted_by(size_t len; len + PADDING)
In the above, the PADDING is some constant? More complicated expressions involving globals will not be supported? > > where then the second part can also be a more complicated > expression, but with the explicit requirement that all > identifiers in this expression are then looked up according to > regular C language rules. So except for the forward declared > member(s) they are *never* looked up in the member namespace of > the struct, i.e. no new name lookup rules are introduced. One question here: What’s the major issue if we’d like to add one new scoping rule, for example, “Structure scope” (the same as the C++’s instance scope) to C? (In addition to the "VLA in structure" issue I mentioned in my previous writeup, is there any other issue to prevent this new scoping rule being added into C ?). Qing > > > I think this could address my concerns about breaking > scoping in C. Still, I personally would prefer designator syntax > for both C and C++ as a nicer solution, and one that already > has some support from WG14. > > Martin > > >>> >>> --- >>> >>> 1. The use of '__self' isn't feasible, so we won't use it. Instead, >>> we'll rely upon the current behavior—resolving any identifiers to the >>> "instance scope". This new scope is used __only__ in attributes, and >>> resolves identifiers to those in the least enclosing, non-anonymous >>> struct. For example: >>> >>> struct foo { >>> char count; >>> struct bar { >>> struct { >>> int len; >>> }; >>> struct { >>> struct { >>> int *valid_use __counted_by(len); // Valid. >>> }; >>> }; >>> int *invalid_use __counted_by(count); // Invalid. >>> } b; >>> }; >>> >>> Rationale: This is how '__guarded_by' currently resolves identifiers, >>> so there's precedence. And if we can't force its usage in all >>> situations, it's less a feature and more a "nicety" which will lead to >>> a massive discrepancy between compiler implementations. Despite the >>> fact that this introduces a new scoping mechanism to C, its use is not >>> as extensive as C++'s instance scoping and will apply only to >>> attributes. In the case where we have two different resolution >>> techniquest happening within the same structure (e.g. VLAs), we can >>> issue warnings as outlined in Yeoul's RFC[1]. >>> >>> 2. A method of forward declaring variables will be added for variables >>> that occur in the struct after the attribute. For example: >>> >>> A: Necessary usage: >>> >>> struct foo { >>> int *buf __counted_by(char count; count); >>> char count; >>> }; >>> >>> B: Unnecessary, but still valid, usage: >>> >>> struct foo { >>> char count; >>> int *buf __counted_by(char count; count); >>> }; >>> >>> * The forward declaration is required in (A) but not in (B). >>> * The type of 'count' as declared in '__counted_by' *must* match the real >>> type. >>> >>> Rationale: This alleviates the issues of "double parsing" for >>> compilers that aren't able to handle it. (We can also remove the >>> '-fexperimental-late-parse-attributes' flag in Clang.) >>> >>> 3. A new builtin '__builtin_global_ref()' (or similarly named) is >>> added to refer to variables outside of the most-enclosing structure. >>> Example: >>> >>> int count_that_will_never_change_we_promise; >>> >>> struct foo { >>> int *bar >>> __counted_by(__builtin_global_ref(count_that_will_never_change_we_promise)); >>> unsigned flags; >>> }; >>> >>> As Yeoul pointed out, there isn't a way to refer to variables that >>> have been shadowed, so the 'global' in '__builtin_global_ref' is a bit >>> of a misnomer as it could refer to a local variable. >>> >>> Rationale: For those who need the flexibility to use variables outside >>> of the struct, this is an acceptable escape route. It does make bounds >>> checking less strict, though, as we can't track any modifications to >>> the global, so caution must be used. >>> >>> Bonus suggestion (by yours truly): >>> >>> I'd like the option to allow functions to calculate expressions (it >>> can be used for a single identifier too, but that's too heavy-handed). >>> It won't be required for an expression, but is a good way to avoid any >>> issues regarding '__builtin_global_ref', like variables shadowing the >>> global variable. Example: >>> >>> int global; >>> >>> struct foo; >>> static int counted_by_calc(struct foo *); >>> >>> struct foo { >>> char count; >>> int fnord; >>> int *buf __counted_by(counted_by_calc); >>> }; >>> >>> static int counted_by_calc(struct foo *ptr) __attribute__((pure)) { >>> return ptr->count * (global << 42) - ptr->fnord; >>> } >>> >>> A pointer to the current least enclosing, non-anonymous struct is >>> passed into 'counted_by_calc' by the compiler. >>> >>> Rationale: This gets rid of all ambiguities when calculating an >>> expression. It's marked 'pure' so there should be no side-effects. >>> >>> --- >>> >>> I believe these suggestions cover everything we've discussed. Please >>> comment with anything I missed and your opinions on each. >>> >>> [1] >>> https://discourse.llvm.org/t/rfc-forward-referencing-a-struct-member-within-bounds-annotations/85510 >>> >>> Share and enjoy! >>> -bw > >