Hi all, Gentle ping — this patch has been pending for review for about two weeks.
Since GCC 16 is approaching Stage 3 in a few days, I’d like to get this reviewed and merged before the window closes, if possible. Thanks a lot, Jasper On Thu, Oct 30, 2025 at 10:00 PM York Jasper Niebuhr <[email protected]> wrote: > > v2: > - Update comment above fold_offsetof to document the 'may_fail' parameter. > > This patch supersedes my earlier submission: > https://gcc.gnu.org/pipermail/gcc-patches/2025-October/697934.html > > It adds the c_parse_component_ref callback that plugins can register > using register_c_parse_component_ref_cb. The callback receives the > parsed COMPONENT_REF tree and may replace it by returning a new tree. > Replacements that are not type-compatible with the original are ignored. > > The callback allows plugins to observe or instrument struct member > accesses that would otherwise be lost due to folding before the earliest > possible plugin pass or hook. In particular, the fold_offsetof > functionality removes all traces of type and member information in > offsetof-like trees, leaving only an integer constant for plugins to > inspect. > > A typical use case would be to replace a select set of COMPONENT_REF > nodes with type-compatible expressions calling a placeholder function, > e.g. __deferred_offsetof(type, member). These calls cannot be folded > away and thus remain available for plugin analysis in later passes. > Offsets not of interest can be left untouched, preserving their const > qualification and use in static assertions. > > Allowing the callback to alter COMPONENT_REF nodes required minor > adjustments to fold_offsetof, which assumes a specific input format. > The parser paths that cannot guarantee that format after invoking the > callback now use fold_offsetof_maybe(), which attempts to fold normally > but, on failure, casts the unfolded expressions to the desired output > type. > > If the callback is not used to alter COMPONENT_REF trees, there is **no > change** in GCC’s behavior. > > Since this callback targets fairly niche use cases, I opted to not make > it part of the public plugin API so there is no need for maintenance > guarantees. > > **Changes since the first proposal:** > * Moved callback invocation to a higher level (the parser) > * The callback is no longer part of the public plugin API > > Signed-off-by: York Jasper Niebuhr <[email protected]> > > --- > gcc/c-family/c-common.cc | 52 ++++++++++++++++++++++------- > gcc/c-family/c-common.h | 3 +- > gcc/c/c-parser.cc | 72 +++++++++++++++++++++++++++++++++++++++- > 3 files changed, 113 insertions(+), 14 deletions(-) > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc > index 587d76461e9..a1c76642b12 100644 > --- a/gcc/c-family/c-common.cc > +++ b/gcc/c-family/c-common.cc > @@ -7073,46 +7073,53 @@ c_common_to_target_charset (HOST_WIDE_INT c) > /* Fold an offsetof-like expression. EXPR is a nested sequence of component > references with an INDIRECT_REF of a constant at the bottom; much like the > traditional rendering of offsetof as a macro. TYPE is the desired type of > - the whole expression. Return the folded result. */ > + the whole expression. On error, error_mark_node is returned. Compiler > + errors are only produced if MAY_FAIL is set to false. Otherwise, the > folded > + result is returned. */ > > tree > -fold_offsetof (tree expr, tree type, enum tree_code ctx) > +fold_offsetof (tree expr, tree type, enum tree_code ctx, bool may_fail) > { > tree base, off, t; > tree_code code = TREE_CODE (expr); > + > switch (code) > { > case ERROR_MARK: > return expr; > > case VAR_DECL: > - error ("cannot apply %<offsetof%> to static data member %qD", expr); > + if (!may_fail) > + error ("cannot apply %<offsetof%> to static data member %qD", expr); > return error_mark_node; > > case CALL_EXPR: > case TARGET_EXPR: > - error ("cannot apply %<offsetof%> when %<operator[]%> is overloaded"); > + if (!may_fail) > + error ("cannot apply %<offsetof%> when %<operator[]%> is overloaded"); > return error_mark_node; > > case NOP_EXPR: > case INDIRECT_REF: > if (!TREE_CONSTANT (TREE_OPERAND (expr, 0))) > { > - error ("cannot apply %<offsetof%> to a non constant address"); > + if (!may_fail) > + error ("cannot apply %<offsetof%> to a non constant address"); > return error_mark_node; > } > return convert (type, TREE_OPERAND (expr, 0)); > > case COMPONENT_REF: > - base = fold_offsetof (TREE_OPERAND (expr, 0), type, code); > + base = fold_offsetof (TREE_OPERAND (expr, 0), type, code, may_fail); > if (base == error_mark_node) > return base; > > t = TREE_OPERAND (expr, 1); > if (DECL_C_BIT_FIELD (t)) > { > - error ("attempt to take address of bit-field structure " > - "member %qD", t); > + if (!may_fail) > + error ("attempt to take address of bit-field structure " > + "member %qD", t); > return error_mark_node; > } > off = size_binop_loc (input_location, PLUS_EXPR, DECL_FIELD_OFFSET (t), > @@ -7121,7 +7128,7 @@ fold_offsetof (tree expr, tree type, enum tree_code ctx) > break; > > case ARRAY_REF: > - base = fold_offsetof (TREE_OPERAND (expr, 0), type, code); > + base = fold_offsetof (TREE_OPERAND (expr, 0), type, code, may_fail); > if (base == error_mark_node) > return base; > > @@ -7178,17 +7185,38 @@ fold_offsetof (tree expr, tree type, enum tree_code > ctx) > case COMPOUND_EXPR: > /* Handle static members of volatile structs. */ > t = TREE_OPERAND (expr, 1); > - gcc_checking_assert (VAR_P (get_base_address (t))); > - return fold_offsetof (t, type); > + if (!VAR_P (get_base_address (t))) > + return error_mark_node; > + return fold_offsetof (t, type, ERROR_MARK, may_fail); > > default: > - gcc_unreachable (); > + return error_mark_node; > } > > if (!POINTER_TYPE_P (type)) > return size_binop (PLUS_EXPR, base, convert (type, off)); > return fold_build_pointer_plus (base, off); > } > + > +/* Tries folding expr using fold_offsetof. On success, the folded offsetof > + is returned. On failure, the original expr is wrapped in an ADDR_EXPR > + and converted to the desired expression type. The resulting expression > + may or may not be constant! */ > + > +tree > +fold_offsetof_maybe (tree expr, tree type) > +{ > + /* expr might not have the correct structure, thus folding may fail. */ > + tree maybe_folded = fold_offsetof (expr, type, ERROR_MARK, true); > + if (maybe_folded != error_mark_node) > + return maybe_folded; > + > + tree ptr_type = build_pointer_type (TREE_TYPE (expr)); > + tree ptr = build1 (ADDR_EXPR, ptr_type, expr); > + > + return fold_convert (type, ptr); > +} > + > > /* *PTYPE is an incomplete array. Complete it with a domain based on > INITIAL_VALUE. If INITIAL_VALUE is not present, use 1 if DO_DEFAULT > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h > index ea6c2975056..70fcfeb6661 100644 > --- a/gcc/c-family/c-common.h > +++ b/gcc/c-family/c-common.h > @@ -1174,7 +1174,8 @@ extern bool c_dump_tree (void *, tree); > extern void verify_sequence_points (tree); > > extern tree fold_offsetof (tree, tree = size_type_node, > - tree_code ctx = ERROR_MARK); > + tree_code ctx = ERROR_MARK, bool may_fail = false); > +extern tree fold_offsetof_maybe (tree, tree = size_type_node); > > extern int complete_array_type (tree *, tree, bool); > extern void complete_flexible_array_elts (tree); > diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc > index 22ec0f849b7..08cf0d9839b 100644 > --- a/gcc/c/c-parser.cc > +++ b/gcc/c/c-parser.cc > @@ -11354,6 +11354,64 @@ get_counted_by_ref (tree array_ref) > return NULL_TREE; > } > > +/* Callback type for notifying plugins when the C parser constructs > + a COMPONENT_REF expression. > + > + The callback receives the COMPONENT_REF tree that has just been parsed. > + It may optionally return a replacement tree, which will be used instead > + of the original if it is type-compatible. Returning NULL_TREE leaves > + the expression unchanged. > + > + This callback is intended for plugins that wish to observe or transform > + member-access expressions (such as 'a.b' or 'a->b') or offsetof > expressions > + at parse time. */ > +using c_parse_component_ref_cb_t = tree (*)(tree ref); > + > +/* Plugin-registered callback for COMPONENT_REF parse notifications. > + Initialized to NULL when no plugin has registered a callback. */ > +static c_parse_component_ref_cb_t c_parse_component_ref_cb = nullptr; > + > +/* Register a plugin callback to be invoked for each parsed COMPONENT_REF. > + > + Only a single callback is supported; registering a new one replaces > + any previously registered callback. */ > +__attribute__ ((visibility ("default"))) > +void > +register_c_parse_component_ref_cb (c_parse_component_ref_cb_t cb) > +{ > + c_parse_component_ref_cb = cb; > +} > + > +/* Helper to notify the registered plugin callback that a COMPONENT_REF > + has been parsed. > + > + If a plugin has registered a callback, this function invokes it with > + the given COMPONENT_REF tree. If the callback returns a non-NULL > + tree whose type is compatible with the original (as determined by > + comptypes), the COMPONENT_REF is replaced with that tree. > + > + This preserves parser invariants and prevents type inconsistencies > + in subsequent compilation stages. */ > +static void > +notify_plugin_parse_component_ref (tree* ref) > +{ > + if (!ref || !c_parse_component_ref_cb) > + return; > + > + tree repl = c_parse_component_ref_cb (*ref); > + if (!repl) > + return; > + > + if (comptypes (TREE_TYPE (*ref), TREE_TYPE (repl))) > + { > + *ref = repl; > + return; > + } > + > + warning (0, "plugin: tree returned from %<c_parse_component_ref_cb%> " > + "has incompatible type; ignored"); > +} > + > /* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2, > C11 6.5.1-6.5.2). Compound literals aren't handled here; callers have to > call c_parser_postfix_expression_after_paren_type on encountering them. > @@ -11766,6 +11824,9 @@ c_parser_postfix_expression (c_parser *parser) > = build_component_ref (loc, offsetof_ref, comp_tok->value, > comp_tok->location, UNKNOWN_LOCATION, > false); > + > + notify_plugin_parse_component_ref (&offsetof_ref); > + > c_parser_consume_token (parser); > while (c_parser_next_token_is (parser, CPP_DOT) > || c_parser_next_token_is (parser, > @@ -11800,6 +11861,9 @@ c_parser_postfix_expression (c_parser *parser) > comp_tok->location, > UNKNOWN_LOCATION, > false); > + > + notify_plugin_parse_component_ref (&offsetof_ref); > + > c_parser_consume_token (parser); > } > else > @@ -11823,7 +11887,7 @@ c_parser_postfix_expression (c_parser *parser) > location_t end_loc = c_parser_peek_token (parser)->get_finish (); > c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, > "expected %<)%>"); > - expr.value = fold_offsetof (offsetof_ref); > + expr.value = fold_offsetof_maybe (offsetof_ref); > set_c_expr_source_range (&expr, loc, end_loc); > } > break; > @@ -13771,6 +13835,9 @@ c_parser_postfix_expression_after_primary (c_parser > *parser, > c_parser_consume_token (parser); > expr.value = build_component_ref (op_loc, expr.value, ident, > comp_loc, UNKNOWN_LOCATION); > + > + notify_plugin_parse_component_ref (&expr.value); > + > set_c_expr_source_range (&expr, start, finish); > expr.original_code = ERROR_MARK; > if (TREE_CODE (expr.value) != COMPONENT_REF) > @@ -13813,6 +13880,9 @@ c_parser_postfix_expression_after_primary (c_parser > *parser, > RO_ARROW), > ident, comp_loc, > expr.get_location ()); > + > + notify_plugin_parse_component_ref (&expr.value); > + > set_c_expr_source_range (&expr, start, finish); > expr.original_code = ERROR_MARK; > if (TREE_CODE (expr.value) != COMPONENT_REF) > -- > 2.43.0 >
