https://gcc.gnu.org/g:37807a10d8816f650d38d6f59a4e230ddc9f785a

commit r17-3181-g37807a10d8816f650d38d6f59a4e230ddc9f785a
Author: Josef Melcr <[email protected]>
Date:   Thu Jul 30 15:21:19 2026 +0200

    Use positional_argument in callback_only attribute handler
    
    This patch replaces some of the manual bounds checking done in the
    callback_only attribute handler with calls to positional_argument.
    Since positional_argument issues warnings and the attribute handler
    issues errors, I decided to downgrade the errors to warnings for
    consistency.
    
            PR testsuite/126708
    
    gcc/c-family/ChangeLog:
    
            * c-attribs.cc (handle_callback_only_attribute): Use
            positional_argument for bounds checking in callback_only
            attribute handler, downgrade errors to warnings for
            consistency.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/attr-callback.c: Expect warnings instead of errors,
            adjust expected messages.
    
    Signed-off-by: Josef Melcr <[email protected]>

Diff:
---
 gcc/c-family/c-attribs.cc            | 102 +++++++++++++++--------------------
 gcc/testsuite/gcc.dg/attr-callback.c |  46 +++++++++++-----
 2 files changed, 76 insertions(+), 72 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 1ed02850a911..aecfdcda4906 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -4682,66 +4682,52 @@ handle_callback_only_attribute (tree *node, tree name, 
tree args,
   tree decl = *node;
   if (TREE_CODE (decl) != FUNCTION_DECL)
     {
-      error_at (DECL_SOURCE_LOCATION (decl),
-               "%qE attribute can only be used on functions", name);
+      warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+                 "%qE attribute can only be used on functions", name);
       *no_add_attrs = true;
     }
 
-  tree cb_fn_idx_node = TREE_VALUE (args);
-  if (TREE_CODE (cb_fn_idx_node) != INTEGER_CST)
+  tree val = positional_argument (decl, name, TREE_VALUE (args), POINTER_TYPE,
+                                 1, POSARG_ZERO);
+  if (!val)
     {
-      error_at (DECL_SOURCE_LOCATION (decl),
-               "argument specifying callback function position is not an "
-               "integer constant");
       *no_add_attrs = true;
       return NULL_TREE;
     }
+  TREE_VALUE (args) = val;
+
   /* We have to use the function type for validation, as
      DECL_ARGUMENTS returns NULL at this point.  */
-  int callback_fn_idx = TREE_INT_CST_LOW (cb_fn_idx_node);
+  int callback_fn_idx = TREE_INT_CST_LOW (val);
   tree decl_type_args = TYPE_ARG_TYPES (TREE_TYPE (decl));
   tree it;
-  int decl_nargs = list_length (decl_type_args);
   for (it = decl_type_args; it != NULL_TREE; it = TREE_CHAIN (it))
     if (it == void_list_node)
-      {
-       --decl_nargs;
-       break;
-      }
+      break;
+
   if (callback_fn_idx == CB_UNKNOWN_POS)
     {
-      error_at (DECL_SOURCE_LOCATION (decl),
-               "callback function position cannot be marked as unknown");
+      warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+                 "callback function position cannot be marked as unknown");
       *no_add_attrs = true;
       return NULL_TREE;
     }
+
   --callback_fn_idx;
-  if (callback_fn_idx >= decl_nargs)
-    {
-      error_at (DECL_SOURCE_LOCATION (decl),
-               "callback function position out of range");
-      *no_add_attrs = true;
-      return NULL_TREE;
-    }
 
-  /* Search for the type of the callback function
-     in parameters of the original function.  */
+  /* Search for the type of the callback function in parameters of the original
+     function.  We know it's there because it's been validated by
+     positional_argument.  */
   tree cfn = chain_index (callback_fn_idx, decl_type_args);
-  if (cfn == NULL_TREE)
-    {
-      error_at (DECL_SOURCE_LOCATION (decl),
-               "could not retrieve callback function from arguments");
-      *no_add_attrs = true;
-      return NULL_TREE;
-    }
+  gcc_checking_assert (cfn != NULL_TREE);
   cfn = TREE_VALUE (cfn);
   tree cfn_pointee_type = TREE_TYPE (cfn);
   if (TREE_CODE (cfn) != POINTER_TYPE
       || TREE_CODE (cfn_pointee_type) != FUNCTION_TYPE)
     {
-      error_at (DECL_SOURCE_LOCATION (decl),
-               "argument no. %d is not an address of a function",
-               callback_fn_idx + 1);
+      warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+                 "argument no. %d is not an address of a function",
+                 callback_fn_idx + 1);
       *no_add_attrs = true;
       return NULL_TREE;
     }
@@ -4759,9 +4745,9 @@ handle_callback_only_attribute (tree *node, tree name, 
tree args,
       }
   if (cfn_nargs != type_nargs)
     {
-      error_at (DECL_SOURCE_LOCATION (decl),
-               "argument number mismatch, %d expected, got %d", type_nargs,
-               cfn_nargs);
+      warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+                 "argument number mismatch, %d expected, got %d", type_nargs,
+                 cfn_nargs);
       *no_add_attrs = true;
       return NULL_TREE;
     }
@@ -4777,12 +4763,23 @@ handle_callback_only_attribute (tree *node, tree name, 
tree args,
     {
       if (TREE_CODE (TREE_VALUE (cfn_it)) != INTEGER_CST)
        {
-         error_at (DECL_SOURCE_LOCATION (decl),
-                   "argument no. %d is not an integer constant", curr + 1);
+         warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+                     "argument no. %d is not an integer constant", curr + 1);
          *no_add_attrs = true;
          continue;
        }
 
+      tree expected_type = TREE_VALUE (it);
+      tree arg_val = positional_argument (decl, name, TREE_VALUE (cfn_it),
+                                         TREE_CODE (expected_type), curr + 1,
+                                         POSARG_ZERO);
+      if (!arg_val)
+       {
+         *no_add_attrs = true;
+         return NULL_TREE;
+       }
+
+      TREE_VALUE (cfn_it) = arg_val;
       int arg_idx = TREE_INT_CST_LOW (TREE_VALUE (cfn_it));
 
       /* No need to check for type compatibility,
@@ -4791,29 +4788,18 @@ handle_callback_only_attribute (tree *node, tree name, 
tree args,
        continue;
 
       arg_idx -= 1;
-      /* Report an error if the position is out of bounds,
-        but we can still check the rest of the arguments.  */
-      if (arg_idx >= decl_nargs)
-       {
-         error_at (DECL_SOURCE_LOCATION (decl),
-                   "callback argument index %d is out of range", arg_idx + 1);
-         *no_add_attrs = true;
-         continue;
-       }
-
       tree arg_type = chain_index (arg_idx, decl_type_args);
       gcc_checking_assert (arg_type != NULL_TREE);
       arg_type = TREE_VALUE (arg_type);
-      tree expected_type = TREE_VALUE (it);
       /* Check the type of the value we are about to pass ("arg_type")
         for compatibility with the actual type the callback function
         expects ("expected_type").  */
       if (!types_compatible_p (expected_type, arg_type))
        {
-         error_at (DECL_SOURCE_LOCATION (decl),
-                   "argument type at index %d is not compatible with callback "
-                   "argument type at index %d",
-                   arg_idx + 1, curr + 1);
+         warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+                     "argument type at index %d is not compatible with "
+                     "callback argument type at index %d",
+                     arg_idx + 1, curr + 1);
          *no_add_attrs = true;
          continue;
        }
@@ -4825,10 +4811,10 @@ handle_callback_only_attribute (tree *node, tree name, 
tree args,
   for (; it; it = lookup_attribute ("callback_only", TREE_CHAIN (it)))
     if (callback_get_fn_index (it) == callback_fn_idx)
       {
-       error_at (DECL_SOURCE_LOCATION (decl),
-                 "function declaration has multiple callback attributes "
-                 "describing argument no. %d",
-                 callback_fn_idx + 1);
+       warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+                   "function declaration has multiple callback attributes "
+                   "describing argument no. %d",
+                   callback_fn_idx + 1);
        *no_add_attrs = true;
        break;
       }
diff --git a/gcc/testsuite/gcc.dg/attr-callback.c 
b/gcc/testsuite/gcc.dg/attr-callback.c
index d9cc67c3affe..74c8287471bd 100755
--- a/gcc/testsuite/gcc.dg/attr-callback.c
+++ b/gcc/testsuite/gcc.dg/attr-callback.c
@@ -25,15 +25,15 @@ unknown_2(void (*)(int*, double*), int*, double*, char*);
 
 [[gnu::callback_only(1, 0, 3, 3)]]
 void
-too_many(void (*)(int*, double*), int*, double*); /* { dg-error "argument 
number mismatch, 2 expected, got 3" }*/
+too_many(void (*)(int*, double*), int*, double*); /* { dg-warning "argument 
number mismatch, 2 expected, got 3" }*/
 
 [[gnu::callback_only(1, 2)]]
 void
-too_few_1(void (*)(int*, double*), int*, double*); /* { dg-error "argument 
number mismatch, 2 expected, got 1" }*/
+too_few_1(void (*)(int*, double*), int*, double*); /* { dg-warning "argument 
number mismatch, 2 expected, got 1" }*/
 
 [[gnu::callback_only(1)]]
 void
-too_few_2(void (*)(int*, double*), int*, double*); /* { dg-error "argument 
number mismatch, 2 expected, got 0" }*/
+too_few_2(void (*)(int*, double*), int*, double*); /* { dg-warning "argument 
number mismatch, 2 expected, got 0" }*/
 
 [[gnu::callback_only(3, 1)]]
 void
@@ -45,52 +45,70 @@ downcast(char*, void* (*)(float*), double*);
 
 [[gnu::callback_only(1, 2, 5)]]
 void
-out_of_range_1(char (*)(float*, double*), float*, double*, int*); /* { 
dg-error "callback argument index 5 is out of range" } */
+out_of_range_1(char (*)(float*, double*), float*, double*, int*); /* { 
dg-warning "exceeds the number of function parameters" } */
 
 [[gnu::callback_only(1, -2, 3)]]
 void
-out_of_range_2(char (*)(float*, double*), float*, double*, int*); /* { 
dg-error "callback argument index -2 is out of range" } */
+out_of_range_2(char (*)(float*, double*), float*, double*, int*); /* { 
dg-warning "exceeds the number of function parameters" } */
 
 [[gnu::callback_only(-1, 2, 3)]]
 void
-out_of_range_3(char (*)(float*, double*), float*, double*, int*); /* { 
dg-error "callback function index -1 is out of range" } */
+out_of_range_3(char (*)(float*, double*), float*, double*, int*);  /* { 
dg-warning "exceeds the number of function parameters" } */
 
 [[gnu::callback_only(67, 2, 3)]]
 void
-out_of_range_4(char (*)(float*, double*), float*, double*, int*); /* { 
dg-error "callback function index 67 is out of range" } */
+out_of_range_4(char (*)(float*, double*), float*, double*, int*); /* { 
dg-warning "exceeds the number of function parameters" } */
 
 [[gnu::callback_only(0, 2, 3)]]
 void
-unknown_fn(char (*)(float*, double*), float*, double*, int*); /* { dg-error 
"callback function position cannot be marked as unknown" } */
+unknown_fn(char (*)(float*, double*), float*, double*, int*); /* { dg-warning 
"callback function position cannot be marked as unknown" } */
 
 [[gnu::callback_only(1, 2)]]
 void
-not_a_fn(int, int); /* { dg-error "argument no. 1 is not an address of a 
function" } */
+not_a_fn(int, int); /* { dg-warning "refers to" } */
 
 struct S
 {
   int x;
 };
 
+static struct S placeholder;
+
+static int one = 1;
+
+static const int const_one = 1;
+
 [[gnu::callback_only(1, 2)]]
 void
-incompatible_types_1(void (*)(struct S*), struct S); /* { dg-error "argument 
type at index 2 is not compatible with callback argument type at index 1" } */
+incompatible_types_1(void (*)(struct S*), struct S); /* { dg-warning "refers 
to" } */
 
 [[gnu::callback_only(1, 3, 2)]]
 void
-incompatible_types_2(void (*)(struct S*, int*), int*, double); /* { dg-error 
"argument type at index 3 is not compatible with callback argument type at 
index 1" } */
+incompatible_types_2(void (*)(struct S*, int*), int*, double); /* { dg-warning 
"refers to" } */
 
 [[gnu::callback_only(1, "2")]]
 void
-wrong_arg_type_1(void (*)(void*), void*); /* { dg-error "argument no. 1 is not 
an integer constant" } */
+wrong_arg_type_1(void (*)(void*), void*); /* { dg-warning "argument no. 1 is 
not an integer constant" } */
 
 [[gnu::callback_only("not a number", 2, 2)]]
 void
-wrong_arg_type_2(void (*)(void*, void*), void*); /* { dg-error "argument 
specifying callback function position is not an integer constant" } */
+wrong_arg_type_2(void (*)(void*, void*), void*); /* { dg-warning "has type" } 
*/
+
+[[gnu::callback_only(placeholder, 2, 2)]]
+void
+wrong_arg_type_3(void (*)(void*, void*), void*); /* { dg-warning "has type" } 
*/
+
+[[gnu::callback_only(one, 2, 2)]]
+void
+int_identifier(void (*)(void*, void*), void*); /* { dg-warning "is not an 
integer constant" } */
+
+[[gnu::callback_only(one, 2, 2)]]
+void
+int_identifier_1(void (*)(void*, void*), void*); /* { dg-warning "is not an 
integer constant" } */
 
 [[gnu::callback_only(1, 2), gnu::callback_only(1, 3)]]
 void
-multiple_single_fn(void (*)(int*), int*, int*); /* { dg-error "function 
declaration has multiple callback attributes describing argument no. 1" } */
+multiple_single_fn(void (*)(int*), int*, int*); /* { dg-warning "function 
declaration has multiple callback attributes describing argument no. 1" } */
 
 /* Check that the attribute won't resolve outside of our namespace.  */

Reply via email to