On Mon, Jul 21, 2025 at 2:19 PM Martin Uecker <ma.uec...@gmail.com> wrote: > Am Montag, dem 21.07.2025 um 04:29 -0700 schrieb Bill Wendling: > > On Sun, Jul 20, 2025 at 4:41 AM Martin Uecker <ma.uec...@gmail.com> wrote: > > But first, as of this time, all of our efforts over the past several > > months to get an agreement on a syntax suitable for both Clang and GCC > > have failed, with both parties having issues with one or the others' > > suggestions. We're at loggerheads. If we can't get past this, then > > either there will be two competing versions of Clang's bounds safety > > checking (very bad), or only Clang will be able to support it (very > > very bad). > > IMHO, having two versions may be preferable to dumping a lot of > technical debt all over the ecosystem.
Two versions will essentially kill this feature in the Linux kernel, as it's so much more difficult to maintain two diverging features, even behind macros. And I don't see what I'm suggesting as technical debt of any kind. If approved, all issues about context and extent of use will be resolved. > > Re original context: The 'struct c_parser' object doesn't have a lot > > of context in it. What I wrote below is a very simplified way of > > performing the delayed parsing, but it's just as valid to do something > > like: > > You also need to worry about state outside of this object. As I mentioned below, if there's more state outside of the c_parser object, I believe it should be consolidated into a 'struct c_context'-like object. Not having it in one place is itself technical debt, so it would be a nice cleanup. > > struct c_parser delayed_parser = *parser; > > /* change the tokens and counts in 'delayed_parser' */ > > /* perform the delayed parsing */ > > > > Then 'parser' can go on its merry way, because it's not changed. If > > more context is needed, it's simple to push those onto a stack > > (probably defined in 'struct c_parser'). > > > > > For an attribute this may still be acceptable, but we need the same > > > thing for array sizes in C. For a proper language feature, you > > > would need to specify the context used to semantically interpret > > > the moved part of the program. If you shuffle tokens around - which > > > we would also need to be able to do recursively, this becomes messy > > > quickly. And a C compiler would not just have to store tokens, but > > > has store and recreate the complete original semantic context, not > > > just for the current version of the language, but also for all > > > future versions. That simply sucks. > > > > I didn't add a "Scope" section to my initial RFC, but I should have. I > > expect delayed parsing to be used for the bounds safety attributes. > > Full stop. If people want to expand it in the future, then great, we > > can burn that bridge when we come to it. :-) > > In my experience, a bad precedent is terribly when trying to standardize > something better latter. Please give an example of array sizes in C. I need to see a concrete example before discussing how delayed parsing wouldn't work in that situation. > > Semantic Analysis should be separate from parsing (I couldn't find any > > uses of 'struct c_parser' in c/c-decl.cc, but maybe that doesn't mean > > much). There shouldn't be anything interesting in the current 'struct > > c_parser' object that should interest SEMA. If there is, then it > > should probably reside in some type of 'struct c_context' object. (As > > I mentioned, though, I am not an expert on GCC's code base, so if > > there are other bits of context outside of 'struct c_parser' that > > should be accounted for, then they obviously will be added to the > > final implementation.) > > It is not that other parts use state from c_parser but that the > parser drives semantic analyis by calling into the other parts, > which modify global state and all the types produced along the > way We want types produced along the way. It's kind of the point of delayed parsing. :-) Otherwise, we could simply assume that all of the fields used in __counted_by have an integral type and, thus, have been seen already...maybe even generating a uniquely named temporary decl that would be replaced after the struct is parsed (not suggesting, because ew...). So in a sense, yes, we want the context to have changed by the time we do the actual parse of the attribute's contents. We need the struct and its contents to have proper types. The "proof of concept" below doesn't do it, but I would prefer this happen in 'finish_struct' or before it. > > Keep in mind that the resolution performed on the single identifier > > operates with *no* context of how it was initially parsed. It performs > > a 'lookup_name' (with scope) and then runs through the TREE_CHAIN > > until it gets to the field. > > If you only want delayed resolution of an identifier, then this is > not delayed parsing and one should limit it to looking up the > identifier later. WG14 has [.N] syntax under discussion and this > would do exactly this. I disagree. It's definitely a form of delayed parsing, because a single identifier is an expression. The current implementation does a special check for it being a single identifier and then delays token resolution until the 'verify_counted_by_...' call. It just skips all of 'c_parse_...' calls that drill down to the 'CPP_ID_ID' case, which I think is why the code to skip through the TREE_CHAINs exists. I've asked before how forward declarations (or in this case the [.N] syntax) would solve the issues at hand, and didn't get a very good response (or perhaps I didn't understand it). It appears to me that it would simply allow name lookups to occur later on, but instead of sending the expression through the 'c_parser_*' routines, it would just loop through the parsed tree finding unresolved identifiers and performing the name lookups on them at that time. (I may be misrepresenting it, so please correct me.) To summarize my point of view a bit: - This isn't a new feature. I don't even believe it's a feature at all. It's just something the compiler does internally to support a feature. - Any context that would affect the outcome of parsing of the attribute's argument are either expected/wanted or should be isolated into its own object and saved at the point where the parser encounters the tokens that require delayed parsing. - Extending a delayed parsing scheme, like this one here, to other features has yet to be explored. It may or may not be useful to them, but that doesn't indicate that it's not useful for this use case. - Other proposals for forward declarations or [.N] syntax have either been specifically rejected by one compiler or the other, or, in the case of the [.N] syntax and a Clang proposal to the C committee, have yet to be standardized. -bw > Martin > > > > > -bw > > > > > Martin > > > > > > Am Sonntag, dem 20.07.2025 um 11:20 +0000 schrieb mo...@google.com: > > > > From: Bill Wendling <mo...@google.com> > > > > > > > > Also, this code doesn't go further than parsing. I.e., it doesn't > > > > generate the > > > > internal gimple code that accesses the struct fields. The code is meant > > > > to show > > > > that it *is* possible to perform a delayed parsing with no "double > > > > parsing" and > > > > still be performant. > > > > > > > > Minor Nomenclature Change > > > > ------------------------- > > > > > > > > I (try to) use "bounds safety" instead of "counted_by" in the code. > > > > This is > > > > because Clang has many more bounds safety attributes in the pipeline and > > > > "bounds safety" seems like a better overall name for the set of > > > > attributes. > > > > > > > > Token-Based Delayed Parsing > > > > --------------------------- > > > > > > > > The solution introduces a token capture and delayed parsing mechanism > > > > that > > > > stores tokens during initial parsing and performs the actual parse > > > > later when > > > > the struct context is available. This isn't a novel idea, as the OMP > > > > code does > > > > something similar for directives. > > > > > > > > After storing the tokens, the parsing continues as if the expression > > > > was parsed > > > > (but it hasn't been yet). The parsing is delayed until the > > > > 'verify_counted_by_attribute ()' call, which performs the actual parse > > > > and > > > > reports any semantic errors. (The actual parse is done simply by > > > > creating a new > > > > 'c_parser' object and filling it with the delayed tokens.) > > > > > > > > On a successful parse, the 'C_TOKEN_VEC' tree node is converted into a > > > > tree > > > > node holding an expression. The token vector is freed afterwards. > > > > > > > > If this approach is deemed worthy, I would like to incorporate the > > > > "single > > > > identifier" parsing into the delayed parsing mechanism (a single > > > > identifier is > > > > just a simple expression). > > > > > > > > RFC > > > > --- > > > > > > > > - Is this something that GCC is interested in to get us past this hurdle > > > > that's delaying the upstreaming of more bounds safety checks? > > > > - Will this solve the issues at hand? (For this implementation, I'm > > > > specifically focusing struct field attributes. I haven't thought much > > > > about > > > > parameter list attributes.) > > > > - I think there's a layering violation. The 'c-decl.cc' "verifies" the > > > > counted_by argument, but it's in essence performing an identifier > > > > resolution, > > > > which is something the parser should be doing. Perhaps that code > > > > should be > > > > moved into "finish_struct ()", or somewhere else in "c/c-parser.cc"? > > > > - My GCC-fu isn't strong and I may have abused some parts of the parser > > > > in > > > > unholy ways. (I blame beer.) > > > > - Anything else you'd like to say or recommend. > > > > > > > > Disclaimer > > > > ---------- > > > > > > > > No AI's were harmed in the making of this RFC, though one did have its > > > > feelings > > > > hurt. > > > > > > > > Share and enjoy! > > > > -bw > > > > > > > > > > > > [1] > > > > https://discourse.llvm.org/t/rfc-bounds-safety-in-c-syntax-compatibility-with-gcc/85885 > > > > > > > > --- > > > > To: gcc-patches@gcc.gnu.org > > > > Cc: Kees Cook <k...@kernel.org> > > > > Cc: Nick Desaulniers <nick.desaulni...@gmail.com> > > > > Cc: Martin Uecker <uec...@tugraz.at> > > > > Cc: Qing Zhao <qing.z...@oracle.com> > > > > Cc: Jakub Jelinek <ja...@redhat.com> > > > > Cc: Richard Biener <richard.guent...@gmail.com> > > > > Cc: Yeoul Na <yeoul...@apple.com> > > > > Cc: John McCall <rjmcc...@apple.com> > > > > Cc: Aaron Ballman <aa...@aaronballman.com> > > > > Cc: Justin Stitt <justinst...@google.com> > > > > > > > > v1: - Initial RFC submitted. > > > > - Kees Cook's Linus asbestos suit borrowed. > > > > --- > > > > gcc/attribs.cc | 8 ++- > > > > gcc/c-family/c-attribs.cc | 37 +++++++++--- > > > > gcc/c/c-decl.cc | 97 ++++++++++++++++++++++-------- > > > > gcc/c/c-parser.cc | 123 ++++++++++++++++++++++++++++++++++++-- > > > > gcc/c/c-parser.h | 1 + > > > > gcc/c/c-typeck.cc | 21 +++++++ > > > > 6 files changed, 246 insertions(+), 41 deletions(-) > > > > > > > > diff --git a/gcc/attribs.cc b/gcc/attribs.cc > > > > index 3fce9d625256..74001889c72a 100644 > > > > --- a/gcc/attribs.cc > > > > +++ b/gcc/attribs.cc > > > > @@ -752,8 +752,10 @@ decl_attributes (tree *node, tree attributes, int > > > > flags, > > > > } > > > > else > > > > { > > > > - int nargs = list_length (args); > > > > - if (nargs < spec->min_length > > > > + int nargs = args && TREE_CODE (args) == C_TOKEN_VEC > > > > + ? 1 > > > > + : list_length (args); > > > > + if (nargs < spec->min_length > > > > || (spec->max_length >= 0 > > > > && nargs > spec->max_length)) > > > > { > > > > @@ -771,7 +773,7 @@ decl_attributes (tree *node, tree attributes, int > > > > flags, > > > > spec->min_length, spec->max_length, nargs); > > > > continue; > > > > } > > > > - } > > > > + } > > > > gcc_assert (is_attribute_p (spec->name, name)); > > > > > > > > if (spec->decl_required && !DECL_P (*anode)) > > > > diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc > > > > index 1f4a0df12051..0e8c17440b9a 100644 > > > > --- a/gcc/c-family/c-attribs.cc > > > > +++ b/gcc/c-family/c-attribs.cc > > > > @@ -2887,7 +2887,14 @@ handle_counted_by_attribute (tree *node, tree > > > > name, > > > > bool *no_add_attrs) > > > > { > > > > tree decl = *node; > > > > - tree argval = TREE_VALUE (args); > > > > + tree argval; > > > > + > > > > + /* Handle both C_TOKEN_VEC and regular argument formats. */ > > > > + if (TREE_CODE (args) == C_TOKEN_VEC) > > > > + argval = args; > > > > + else > > > > + argval = TREE_VALUE (args); > > > > + > > > > tree old_counted_by = lookup_attribute ("counted_by", > > > > DECL_ATTRIBUTES (decl)); > > > > > > > > /* This attribute is not supported in C++. */ > > > > @@ -2922,11 +2929,13 @@ handle_counted_by_attribute (tree *node, tree > > > > name, > > > > " array member field", name); > > > > *no_add_attrs = true; > > > > } > > > > - /* The argument should be an identifier. */ > > > > - else if (TREE_CODE (argval) != IDENTIFIER_NODE) > > > > + /* The argument should be an identifier or a C_TOKEN_VEC for complex > > > > + expressions. */ > > > > + else if (TREE_CODE (argval) != IDENTIFIER_NODE > > > > + && TREE_CODE (argval) != C_TOKEN_VEC) > > > > { > > > > error_at (DECL_SOURCE_LOCATION (decl), > > > > - "%<counted_by%> argument is not an identifier"); > > > > + "%<counted_by%> argument is not an identifier or > > > > expression"); > > > > *no_add_attrs = true; > > > > } > > > > /* Issue error when there is a counted_by attribute with a different > > > > @@ -2934,14 +2943,28 @@ handle_counted_by_attribute (tree *node, tree > > > > name, > > > > else if (old_counted_by != NULL_TREE) > > > > { > > > > tree old_fieldname = TREE_VALUE (TREE_VALUE (old_counted_by)); > > > > - if (strcmp (IDENTIFIER_POINTER (old_fieldname), > > > > - IDENTIFIER_POINTER (argval)) != 0) > > > > - { > > > > + /* Only check conflicts for simple identifiers; complex > > > > expressions > > > > + will be validated during struct completion. */ > > > > + if (TREE_CODE (argval) == IDENTIFIER_NODE > > > > + && TREE_CODE (old_fieldname) == IDENTIFIER_NODE > > > > + && strcmp (IDENTIFIER_POINTER (old_fieldname), > > > > + IDENTIFIER_POINTER (argval)) > > > > + != 0) > > > > + { > > > > error_at (DECL_SOURCE_LOCATION (decl), > > > > "%<counted_by%> argument %qE conflicts with" > > > > " previous declaration %qE", argval, old_fieldname); > > > > *no_add_attrs = true; > > > > } > > > > + else if (TREE_CODE (argval) == C_TOKEN_VEC > > > > + || TREE_CODE (old_fieldname) == C_TOKEN_VEC) > > > > + { > > > > + /* For complex expressions, defer conflict checking to struct > > > > + completion. */ > > > > + warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes, > > > > + "multiple %<counted_by%> attributes on the same > > > > field; " > > > > + "only the last one will be used"); > > > > + } > > > > } > > > > > > > > return NULL_TREE; > > > > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc > > > > index acbe2b88e674..615d0f5a1e88 100644 > > > > --- a/gcc/c/c-decl.cc > > > > +++ b/gcc/c/c-decl.cc > > > > @@ -9445,43 +9445,90 @@ verify_counted_by_attribute (tree struct_type, > > > > tree field_decl) > > > > if (!attr_counted_by) > > > > return; > > > > > > > > - /* If there is an counted_by attribute attached to the field, > > > > + /* If there is a counted_by attribute attached to the field, > > > > verify it. */ > > > > > > > > - tree fieldname = TREE_VALUE (TREE_VALUE (attr_counted_by)); > > > > + tree argument; > > > > + tree parsed_expr = NULL_TREE; > > > > > > > > - /* Verify the argument of the attrbute is a valid field of the > > > > - containing structure. */ > > > > - > > > > - tree counted_by_field = lookup_field (struct_type, fieldname); > > > > - > > > > - /* Error when the field is not found in the containing structure and > > > > - remove the corresponding counted_by attribute from the > > > > field_decl. */ > > > > - if (!counted_by_field) > > > > - { > > > > - error_at (DECL_SOURCE_LOCATION (field_decl), > > > > - "argument %qE to the %<counted_by%> attribute" > > > > - " is not a field declaration in the same structure" > > > > - " as %qD", fieldname, field_decl); > > > > - DECL_ATTRIBUTES (field_decl) > > > > - = remove_attribute ("counted_by", DECL_ATTRIBUTES (field_decl)); > > > > - } > > > > + /* Handle both C_TOKEN_VEC and regular argument formats */ > > > > + tree args = TREE_VALUE (attr_counted_by); > > > > + if (TREE_CODE (args) == C_TOKEN_VEC) > > > > + argument = args; /* Direct C_TOKEN_VEC */ > > > > else > > > > - /* Error when the field is not with an integer type. */ > > > > + argument = TREE_VALUE (args); /* Standard tree list format */ > > > > + > > > > + /* Handle both simple identifiers and complex expressions. */ > > > > + if (TREE_CODE (argument) == IDENTIFIER_NODE) > > > > { > > > > + /* Simple identifier case - existing logic. */ > > > > + tree counted_by_field = lookup_field (struct_type, argument); > > > > + > > > > + if (!counted_by_field) > > > > + { > > > > + error_at (DECL_SOURCE_LOCATION (field_decl), > > > > + "argument %qE to the %<counted_by%> attribute" > > > > + " is not a field declaration in the same structure" > > > > + " as %qD", > > > > + argument, field_decl); > > > > + DECL_ATTRIBUTES (field_decl) > > > > + = remove_attribute ("counted_by", DECL_ATTRIBUTES > > > > (field_decl)); > > > > + return; > > > > + } > > > > + > > > > + /* Find the actual field declaration. */ > > > > while (TREE_CHAIN (counted_by_field)) > > > > counted_by_field = TREE_CHAIN (counted_by_field); > > > > tree real_field = TREE_VALUE (counted_by_field); > > > > > > > > if (!INTEGRAL_TYPE_P (TREE_TYPE (real_field))) > > > > { > > > > - error_at (DECL_SOURCE_LOCATION (field_decl), > > > > - "argument %qE to the %<counted_by%> attribute" > > > > - " is not a field declaration with an integer type", > > > > - fieldname); > > > > - DECL_ATTRIBUTES (field_decl) > > > > + error_at (DECL_SOURCE_LOCATION (field_decl), > > > > + "argument %qE to the %<counted_by%> attribute" > > > > + " is not a field declaration with an integer type", > > > > + argument); > > > > + DECL_ATTRIBUTES (field_decl) > > > > + = remove_attribute ("counted_by", DECL_ATTRIBUTES > > > > (field_decl)); > > > > + return; > > > > + } > > > > + } > > > > + else if (TREE_CODE (argument) == C_TOKEN_VEC) > > > > + { > > > > + /* Complex expression case - parse the stored tokens. */ > > > > + parsed_expr > > > > + = c_parse_token_vec_expression_with_struct (argument, > > > > struct_type); > > > > + > > > > + if (!parsed_expr) > > > > + { > > > > + error_at (DECL_SOURCE_LOCATION (field_decl), > > > > + "invalid expression in %<counted_by%> attribute"); > > > > + DECL_ATTRIBUTES (field_decl) > > > > = remove_attribute ("counted_by", DECL_ATTRIBUTES > > > > (field_decl)); > > > > - } > > > > + return; > > > > + } > > > > + > > > > + /* Validate that the expression result is integer type. */ > > > > + if (!INTEGRAL_TYPE_P (TREE_TYPE (parsed_expr))) > > > > + { > > > > + error_at (DECL_SOURCE_LOCATION (field_decl), > > > > + "expression in %<counted_by%> attribute does not" > > > > + " have integer type"); > > > > + DECL_ATTRIBUTES (field_decl) > > > > + = remove_attribute ("counted_by", DECL_ATTRIBUTES > > > > (field_decl)); > > > > + return; > > > > + } > > > > + > > > > + /* If we successfully parsed the expression, replace the > > > > C_TOKEN_VEC > > > > + with the parsed expression for later use. */ > > > > + TREE_VALUE (attr_counted_by) = build_tree_list (NULL_TREE, > > > > parsed_expr); > > > > + } > > > > + else > > > > + { > > > > + error_at (DECL_SOURCE_LOCATION (field_decl), > > > > + "invalid argument to %<counted_by%> attribute"); > > > > + DECL_ATTRIBUTES (field_decl) > > > > + = remove_attribute ("counted_by", DECL_ATTRIBUTES > > > > (field_decl)); > > > > + return; > > > > } > > > > } > > > > > > > > diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc > > > > index 5119841a5895..a9c641151d39 100644 > > > > --- a/gcc/c/c-parser.cc > > > > +++ b/gcc/c/c-parser.cc > > > > @@ -5623,6 +5623,30 @@ c_parser_attribute_arguments (c_parser *parser, > > > > bool takes_identifier, > > > > return attr_args; > > > > } > > > > > > > > +static bool > > > > +c_parser_bounds_safety_attr (tree attr_name) > > > > +{ > > > > + const char *name = IDENTIFIER_POINTER (attr_name); > > > > + return !strcmp (name, "counted_by"); > > > > +} > > > > + > > > > +/* Check if the bounds safety attribute needs complex expression > > > > parsing. */ > > > > +static bool > > > > +c_parser_needs_complex_bounds_parsing (c_parser *parser) > > > > +{ > > > > + /* Look ahead to see if this is a simple identifier or a complex > > > > + expression. */ > > > > + c_token *tok = c_parser_peek_token (parser); > > > > + if (tok->type != CPP_NAME) > > > > + return true; > > > > + > > > > + c_token *next = c_parser_peek_2nd_token (parser); > > > > + if (next->type == CPP_CLOSE_PAREN) > > > > + return false; > > > > + > > > > + return true; > > > > +} > > > > + > > > > /* Parse (possibly empty) gnu-attributes. This is a GNU extension. > > > > > > > > gnu-attributes: > > > > @@ -5692,12 +5716,56 @@ c_parser_gnu_attribute (c_parser *parser, tree > > > > attrs, > > > > } > > > > c_parser_consume_token (parser); > > > > > > > > - tree attr_args > > > > - = c_parser_attribute_arguments (parser, > > > > - attribute_takes_identifier_p > > > > (attr_name), > > > > - false, > > > > - is_attribute_p ("assume", attr_name), > > > > - true); > > > > + tree attr_args; > > > > + if (!c_parser_bounds_safety_attr (attr_name) > > > > + || !c_parser_needs_complex_bounds_parsing (parser)) > > > > + attr_args = c_parser_attribute_arguments ( > > > > + parser, attribute_takes_identifier_p (attr_name), false, > > > > + is_attribute_p ("assume", attr_name), true); > > > > + else > > > > + { > > > > + unsigned int n = 1; > > > > + unsigned int paren_count = 0; > > > > + > > > > + for (; true; ++n) > > > > + { > > > > + c_token *tok = c_parser_peek_nth_token (parser, n); > > > > + > > > > + /* Safety check to avoid infinite loops. */ > > > > + if (tok->type == CPP_EOF) > > > > + { > > > > + c_parser_error (parser, "expected identifier or > > > > expression"); > > > > + return error_mark_node; > > > > + } > > > > + > > > > + if (tok->type == CPP_CLOSE_PAREN) > > > > + { > > > > + if (!paren_count) > > > > + break; > > > > + paren_count--; > > > > + } > > > > + else if (tok->type == CPP_OPEN_PAREN) > > > > + paren_count++; > > > > + else if (tok->type == CPP_SEMICOLON) > > > > + { > > > > + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, > > > > + "expected identifier or > > > > expression"); > > > > + return error_mark_node; > > > > + } > > > > + } > > > > + > > > > + vec<c_token, va_gc> *v; > > > > + vec_alloc (v, n - 1); > > > > + for (--n; n; --n) > > > > + { > > > > + c_token *tok = c_parser_peek_token (parser); > > > > + v->quick_push (*tok); > > > > + c_parser_consume_token (parser); > > > > + } > > > > + > > > > + attr_args = make_node (C_TOKEN_VEC); > > > > + C_TOKEN_VEC_TOKENS (attr_args) = v; > > > > + } > > > > > > > > attr = build_tree_list (attr_name, attr_args); > > > > if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) > > > > @@ -27746,6 +27814,49 @@ c_maybe_parse_omp_decl (tree decl, tree d) > > > > return true; > > > > } > > > > > > > > +/* Context for parsing counted_by expressions within struct scope. */ > > > > +tree bounds_safety_attr_struct_type = NULL_TREE; > > > > + > > > > +/* Parse an expression from a C_TOKEN_VEC. This is used for delayed > > > > + parsing of counted_by attribute expressions. */ > > > > + > > > > +tree > > > > +c_parse_token_vec_expression (tree token_vec) > > > > +{ > > > > + vec<c_token, va_gc> *tokens = C_TOKEN_VEC_TOKENS (token_vec); > > > > + > > > > + if (!tokens || vec_safe_is_empty (tokens)) > > > > + return NULL_TREE; > > > > + > > > > + /* Create a parser for just the list of tokens. */ > > > > + c_parser delay_parser = {}; > > > > + delay_parser.tokens = tokens->address (); > > > > + delay_parser.tokens_avail = tokens->length (); > > > > + delay_parser.raw_tokens = tokens; > > > > + > > > > + /* Parse the expression. */ > > > > + c_expr expr = c_parser_expr_no_commas (&delay_parser, NULL); > > > > + > > > > + if (expr.value == error_mark_node) > > > > + return NULL_TREE; > > > > + > > > > + return expr.value; > > > > +} > > > > + > > > > +/* Parse an expression from a C_TOKEN_VEC with struct context. */ > > > > + > > > > +tree > > > > +c_parse_token_vec_expression_with_struct (tree token_vec, tree > > > > struct_type) > > > > +{ > > > > + tree saved_struct_type = bounds_safety_attr_struct_type; > > > > + bounds_safety_attr_struct_type = struct_type; > > > > + > > > > + tree result = c_parse_token_vec_expression (token_vec); > > > > + > > > > + bounds_safety_attr_struct_type = saved_struct_type; > > > > + return result; > > > > +} > > > > + > > > > /* OpenMP 4.0: > > > > # pragma omp declare target new-line > > > > declarations and definitions > > > > diff --git a/gcc/c/c-parser.h b/gcc/c/c-parser.h > > > > index a84779bcbf83..577644cef13a 100644 > > > > --- a/gcc/c/c-parser.h > > > > +++ b/gcc/c/c-parser.h > > > > @@ -205,5 +205,6 @@ extern void c_parser_declspecs (c_parser *, struct > > > > c_declspecs *, bool, bool, > > > > enum c_lookahead_kind); > > > > extern struct c_type_name *c_parser_type_name (c_parser *, bool = > > > > false); > > > > extern bool c_maybe_parse_omp_decl (tree, tree); > > > > +extern tree c_parse_token_vec_expression_with_struct (tree, tree); > > > > > > > > #endif > > > > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc > > > > index f161bd9d0e74..788c0fe3763f 100644 > > > > --- a/gcc/c/c-typeck.cc > > > > +++ b/gcc/c/c-typeck.cc > > > > @@ -56,6 +56,9 @@ along with GCC; see the file COPYING3. If not see > > > > #include "tree-pretty-print-markup.h" > > > > #include "gcc-urlifier.h" > > > > > > > > +/* Forward declaration for bounds safety attribute context. */ > > > > +extern tree bounds_safety_attr_struct_type; > > > > + > > > > /* Possible cases of implicit conversions. Used to select diagnostic > > > > messages > > > > and control folding initializers in convert_for_assignment. */ > > > > enum impl_conv { > > > > @@ -3528,6 +3531,24 @@ build_external_ref (location_t loc, tree id, > > > > bool fun, tree *type) > > > > return error_mark_node; > > > > else > > > > { > > > > + /* Check if we're parsing a counted_by expression and can resolve > > > > + the identifier as a struct field. */ > > > > + if (bounds_safety_attr_struct_type) > > > > + { > > > > + tree field = lookup_field (bounds_safety_attr_struct_type, > > > > id); > > > > + if (field) > > > > + { > > > > + /* We found a field - create a reference to it */ > > > > + while (TREE_CHAIN (field)) > > > > + field = TREE_CHAIN (field); > > > > + tree real_field = TREE_VALUE (field); > > > > + > > > > + ref = real_field; > > > > + *type = TREE_TYPE (ref); > > > > + return ref; > > > > + } > > > > + } > > > > + > > > > undeclared_variable (loc, id); > > > > return error_mark_node; > > > > } > > > >