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
>

Reply via email to