Patch 4 adds support to the machinery behind -Wstringop-overflow
to issue warnings for (likely) out of bounds accesses in calls to
functions with the internal attribute access specification.  This
implements the feature pr50584 asks for (plus more).
[4/5] - Extend -Wstringop-overflow to detect out-of-bounds accesses to array parameters.

gcc/ChangeLog:

	* tree-ssa-uninit.c (maybe_warn_pass_by_reference): Handle attribute
	access internal representation of arrays.

gcc/testsuite/ChangeLog:

	* gcc.dg/uninit-37.c: New test.

gcc/ChangeLog:

	PR c/50584
	* builtins.c (warn_for_access): Add argument.  Distinguish between
	reads and writes.
	(check_access): Add argument.  Distinguish between reads and writes.
	(gimple_call_alloc_size): Set range even on failure.
	(gimple_parm_array_size): New function.
	(compute_objsize): Call it.
	(check_memop_access): Pass check_access an additional argument.
	(expand_builtin_memchr, expand_builtin_strcat): Same.
	(expand_builtin_strcpy, expand_builtin_stpcpy_1): Same.
	(expand_builtin_stpncpy, check_strncat_sizes): Same.
	(expand_builtin_strncat, expand_builtin_strncpy): Same.
	(expand_builtin_memcmp): Same.
	* builtins.h (compute_objsize): Declare a new overload.
	(gimple_parm_array_size): Declare.
	(check_access): Add argument.
	* calls.c (append_attrname): Simplify.
	(maybe_warn_rdwr_sizes): Handle internal attribute access.

gcc/testsuite/ChangeLog:

	PR c/50584
	* c-c++-common/Wsizeof-pointer-memaccess1.c: Disable new expected
	warnings.
	* g++.dg/ext/attr-access.C: Update text of expected warnings.
	* gcc.dg/Wstringop-overflow-23.c: Same.
	* gcc.dg/Wstringop-overflow-24.c: Same.
	* gcc.dg/attr-access-none.c: Same.
	* gcc.dg/Wstringop-overflow-40.c: New test.
	* gcc.dg/attr-access-2.c: New test.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 228db78f32b..bbaeb3aedd1 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3312,26 +3312,122 @@ determine_block_size (tree len, rtx len_rtx,
 }
 
 /* For an expression EXP issue an access warning controlled by option OPT
-   with access to a region SLEN bytes in size in the RANGE of sizes.  */
+   with access to a region SIZE bytes in size in the RANGE of sizes.
+   WRITE is true for a write access, READ for a read access, neither for
+   call that may or may not perform an access but for which the range
+   is expected to valid.
+   Returns true when a warning has been issued.  */
 
 static bool
 warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
-		 tree slen, bool access)
+		 tree size, bool write, bool read)
 {
   bool warned = false;
 
-  if (access)
+  if (write && read)
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	warned = (func
+		  ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+			       "%K%qD accessing %E byte in a region "
+			       "of size %E",
+			       "%K%qD accessing %E bytes in a region "
+			       "of size %E",
+			       exp, func, range[0], size)
+		  : warning_n (loc, opt, tree_to_uhwi (range[0]),
+			       "%Kaccessing %E byte in a region "
+			       "of size %E",
+			       "%Kaccessing %E bytes in a region "
+			       "of size %E",
+			       exp, range[0], size));
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.  */
+	  warned = (func
+		    ? warning_at (loc, opt,
+				  "%K%qD accessing %E or more bytes in "
+				  "a region of size %E",
+				  exp, func, range[0], size)
+		    : warning_at (loc, opt,
+				  "%Kaccessing %E or more bytes in "
+				  "a region of size %E",
+				  exp, range[0], size));
+	}
+      else
+	warned = (func
+		  ? warning_at (loc, opt,
+				"%K%qD accessing between %E and %E bytes "
+				"in a region of size %E",
+				exp, func, range[0], range[1],
+				size)
+		  : warning_at (loc, opt,
+				"%Kaccessing between %E and %E bytes "
+				"in a region of size %E",
+				exp, range[0], range[1],
+				size));
+      return warned;
+    }
+
+  if (write)
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	warned = (func
+		  ? warning_n (loc, opt, tree_to_uhwi (range[0]),
+			       "%K%qD writing %E byte into a region "
+			       "of size %E overflows the destination",
+			       "%K%qD writing %E bytes into a region "
+			       "of size %E overflows the destination",
+			       exp, func, range[0], size)
+		  : warning_n (loc, opt, tree_to_uhwi (range[0]),
+			       "%Kwriting %E byte into a region "
+			       "of size %E overflows the destination",
+			       "%Kwriting %E bytes into a region "
+			       "of size %E overflows the destination",
+			       exp, range[0], size));
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.  */
+	  warned = (func
+		    ? warning_at (loc, opt,
+				  "%K%qD writing %E or more bytes into "
+				  "a region of size %E overflows "
+				  "the destination",
+				  exp, func, range[0], size)
+		    : warning_at (loc, opt,
+				  "%Kwriting %E or more bytes into "
+				  "a region of size %E overflows "
+				  "the destination",
+				  exp, range[0], size));
+	}
+      else
+	warned = (func
+		  ? warning_at (loc, opt,
+				"%K%qD writing between %E and %E bytes "
+				"into a region of size %E overflows "
+				"the destination",
+				exp, func, range[0], range[1],
+				size)
+		  : warning_at (loc, opt,
+				"%Kwriting between %E and %E bytes "
+				"into a region of size %E overflows "
+				"the destination",
+				exp, range[0], range[1],
+				size));
+      return warned;
+    }
+
+  if (read)
     {
       if (tree_int_cst_equal (range[0], range[1]))
 	warned = (func
 		  ? warning_n (loc, opt, tree_to_uhwi (range[0]),
 			       "%K%qD reading %E byte from a region of size %E",
 			       "%K%qD reading %E bytes from a region of size %E",
-			       exp, func, range[0], slen)
+			       exp, func, range[0], size)
 		  : warning_n (loc, opt, tree_to_uhwi (range[0]),
 			       "%Kreading %E byte from a region of size %E",
 			       "%Kreading %E bytes from a region of size %E",
-			       exp, range[0], slen));
+			       exp, range[0], size));
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
@@ -3339,59 +3435,47 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
 		    ? warning_at (loc, opt,
 				  "%K%qD reading %E or more bytes from a region "
 				  "of size %E",
-				  exp, func, range[0], slen)
+				  exp, func, range[0], size)
 		    : warning_at (loc, opt,
 				  "%Kreading %E or more bytes from a region "
 				  "of size %E",
-				  exp, range[0], slen));
+				  exp, range[0], size));
 	}
       else
 	warned = (func
 		  ? warning_at (loc, opt,
 				"%K%qD reading between %E and %E bytes from "
 				"a region of size %E",
-				exp, func, range[0], range[1], slen)
+				exp, func, range[0], range[1], size)
 		  : warning_at (loc, opt,
 				"%Kreading between %E and %E bytes from "
 				"a region of size %E",
-				exp, range[0], range[1], slen));
+				exp, range[0], range[1], size));
 
       return warned;
     }
 
-  if (tree_int_cst_equal (range[0], range[1]))
+  if (tree_int_cst_equal (range[0], range[1])
+      || tree_int_cst_sign_bit (range[1]))
     warned = (func
-	      ? warning_n (loc, opt, tree_to_uhwi (range[0]),
-			   "%K%qD epecting %E byte in a region of size %E",
-			   "%K%qD expecting %E bytes in a region of size %E",
-			   exp, func, range[0], slen)
-	      : warning_n (loc, opt, tree_to_uhwi (range[0]),
-			   "%Kexpecting %E byte in a region of size %E",
-			   "%Kexpecting %E bytes in a region of size %E",
-			   exp, range[0], slen));
-  else if (tree_int_cst_sign_bit (range[1]))
-    {
-      /* Avoid printing the upper bound if it's invalid.  */
-      warned = (func
-		? warning_at (loc, opt,
-			      "%K%qD expecting %E or more bytes in a region "
-			      "of size %E",
-			      exp, func, range[0], slen)
-		: warning_at (loc, opt,
-			      "%Kexpecting %E or more bytes in a region "
-			      "of size %E",
-			      exp, range[0], slen));
-    }
+	      ? warning_at (loc, OPT_Warray_parameter_,
+			    "%K%qD expecting %E or more bytes in a region "
+			    "of size %E",
+			    exp, func, range[0], size)
+	      : warning_at (loc, OPT_Warray_parameter_,
+			    "%Kexpecting %E or more bytes in a region "
+			    "of size %E",
+			    exp, range[0], size));
   else
     warned = (func
-	      ? warning_at (loc, opt,
+	      ? warning_at (loc, OPT_Warray_parameter_,
 			    "%K%qD expecting between %E and %E bytes in "
 			    "a region of size %E",
-			    exp, func, range[0], range[1], slen)
-	      : warning_at (loc, opt,
+			    exp, func, range[0], range[1], size)
+	      : warning_at (loc, OPT_Warray_parameter_,
 			    "%Kexpectting between %E and %E bytes in "
 			    "a region of size %E",
-			    exp, range[0], range[1], slen));
+			    exp, range[0], range[1], size));
   return warned;
 }
 
@@ -3545,8 +3629,9 @@ inform_access (const access_ref &ref, bool write)
    When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
-   ACCESS is true for accesses, false for simple size checks in calls
-   to functions that neither read from nor write to the region.
+   WRITE is true for write accesses, READ is true for reads.  Both are
+   false for simple size checks in calls to functions that neither read
+   from nor write to the region.
 
    When nonnull, PAD points to a more detailed description of the access.
 
@@ -3556,7 +3641,7 @@ inform_access (const access_ref &ref, bool write)
 bool
 check_access (tree exp, tree, tree, tree dstwrite,
 	      tree maxread, tree srcstr, tree dstsize,
-	      bool access /* = true */,
+	      bool write /* = true */, bool read /* = true */,
 	      const access_data *pad /* = NULL */)
 {
   int opt = OPT_Wstringop_overflow_;
@@ -3636,6 +3721,11 @@ check_access (tree exp, tree, tree, tree dstwrite,
     get_size_range (dstwrite, range);
 
   tree func = get_callee_fndecl (exp);
+  /* Read vs write access by built-ins can be determined from the const
+     qualifiers on the pointer argument.  In the absence of attribute
+     access, non-const qualified pointer arguments to user-defined
+     functions are assumed to both read and write the objects.  */
+  const bool builtin = func ? fndecl_built_in_p (func) : false;
 
   /* First check the number of bytes to be written against the maximum
      object size.  */
@@ -3717,49 +3807,10 @@ check_access (tree exp, tree, tree, tree dstwrite,
 				      "the destination",
 				      exp, range[0], dstsize));
 	    }
-	  else if (tree_int_cst_equal (range[0], range[1]))
-	    warned = (func
-		      ? warning_n (loc, opt, tree_to_uhwi (range[0]),
-				   "%K%qD writing %E byte into a region "
-				   "of size %E overflows the destination",
-				   "%K%qD writing %E bytes into a region "
-				   "of size %E overflows the destination",
-				   exp, func, range[0], dstsize)
-		      : warning_n (loc, opt, tree_to_uhwi (range[0]),
-				   "%Kwriting %E byte into a region "
-				   "of size %E overflows the destination",
-				   "%Kwriting %E bytes into a region "
-				   "of size %E overflows the destination",
-				   exp, range[0], dstsize));
-	  else if (tree_int_cst_sign_bit (range[1]))
-	    {
-	      /* Avoid printing the upper bound if it's invalid.  */
-	      warned = (func
-			? warning_at (loc, opt,
-				      "%K%qD writing %E or more bytes into "
-				      "a region of size %E overflows "
-				      "the destination",
-				      exp, func, range[0], dstsize)
-			: warning_at (loc, opt,
-				      "%Kwriting %E or more bytes into "
-				      "a region of size %E overflows "
-				      "the destination",
-				      exp, range[0], dstsize));
-	    }
 	  else
-	    warned = (func
-		      ? warning_at (loc, opt,
-				    "%K%qD writing between %E and %E bytes "
-				    "into a region of size %E overflows "
-				    "the destination",
-				    exp, func, range[0], range[1],
-				    dstsize)
-		      : warning_at (loc, opt,
-				    "%Kwriting between %E and %E bytes "
-				    "into a region of size %E overflows "
-				    "the destination",
-				    exp, range[0], range[1],
-				    dstsize));
+	    warned = warn_for_access (loc, func, exp, opt, range, dstsize,
+				      write, read && !builtin);
+
 	  if (warned)
 	    {
 	      TREE_NO_WARNING (exp) = true;
@@ -3872,7 +3923,8 @@ check_access (tree exp, tree, tree, tree dstwrite,
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
-      if (warn_for_access (loc, func, exp, opt, range, slen, access))
+      if (warn_for_access (loc, func, exp, opt, range, slen,
+			   write && !builtin, read))
 	{
 	  TREE_NO_WARNING (exp) = true;
 	  if (pad)
@@ -3885,8 +3937,11 @@ check_access (tree exp, tree, tree, tree dstwrite,
 }
 
 /* If STMT is a call to an allocation function, returns the constant
-   size of the object allocated by the call represented as sizetype.
-   If nonnull, sets RNG1[] to the range of the size.  */
+   maximum size of the object allocated by the call represented as
+   sizetype.  If nonnull, sets RNG1[] to the range of the size.
+   When nonnull, uses RVALS for range information, otherwise calls
+   get_range_info to get it.
+   Returns null when STMT is not a call to a valid allocation function.  */
 
 tree
 gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
@@ -3942,8 +3997,14 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   if (!rng1)
     rng1 = rng1_buf;
 
+  const int prec = ADDR_MAX_PRECISION;
+  const tree size_max = TYPE_MAX_VALUE (sizetype);
   if (!get_range (size, rng1, rvals))
-    return NULL_TREE;
+    {
+      /* Use the full non-negative range on failure.  */
+      rng1[0] = wi::zero (prec);
+      rng1[1] = wi::to_wide (size_max, prec);
+    }
 
   if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
     return fold_convert (sizetype, size);
@@ -3953,10 +4014,13 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   tree n = argidx2 < nargs ? gimple_call_arg (stmt, argidx2) : integer_one_node;
   wide_int rng2[2];
   if (!get_range (n, rng2, rvals))
-    return NULL_TREE;
+    {
+      /* As above, use the full non-negative range on failure.  */
+      rng2[0] = wi::zero (prec);
+      rng2[1] = wi::to_wide (size_max, prec);
+    }
 
   /* Extend to the maximum precision to avoid overflow.  */
-  const int prec = ADDR_MAX_PRECISION;
   rng1[0] = wide_int::from (rng1[0], prec, UNSIGNED);
   rng1[1] = wide_int::from (rng1[1], prec, UNSIGNED);
   rng2[0] = wide_int::from (rng2[0], prec, UNSIGNED);
@@ -3966,7 +4030,6 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
      of SIZE_MAX and the product of the upper bounds as a constant.  */
   rng1[0] = rng1[0] * rng2[0];
   rng1[1] = rng1[1] * rng2[1];
-  tree size_max = TYPE_MAX_VALUE (sizetype);
   if (wi::gtu_p (rng1[1], wi::to_wide (size_max, prec)))
     {
       rng1[1] = wi::to_wide (size_max);
@@ -3976,6 +4039,61 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   return wide_int_to_tree (sizetype, rng1[1]);
 }
 
+/* For an access to an object referenced to by the function parameter PTR
+   of pointer type, and set RNG[] to the range of sizes of the object
+   obtainedfrom the attribute access specification for the current function.
+   Return the function parameter on success and null otherwise.  */
+
+tree
+gimple_parm_array_size (tree ptr, wide_int rng[2],
+			const vr_values * /* = NULL */)
+{
+  /* For a function argument try to determine the byte size of the array
+     from the current function declaratation (e.g., attribute access or
+     related).  */
+  tree var = SSA_NAME_VAR (ptr);
+  if (TREE_CODE (var) != PARM_DECL)
+    return NULL_TREE;
+
+  const unsigned prec = TYPE_PRECISION (sizetype);
+
+  rdwr_map rdwr_idx;
+  attr_access *access = get_parm_access (rdwr_idx, var);
+  if (!access)
+    return NULL_TREE;
+
+  if (access->sizarg != UINT_MAX)
+    {
+      /* TODO: Try to extract the range from the argument based on
+	 those of subsequent assertions or based on known calls to
+	 the current function.  */
+      return NULL_TREE;
+    }
+
+  if (!access->minsize)
+    return NULL_TREE;
+
+  /* Only consider ordinary array bound at level 2 (or above if it's
+     ever added).  */
+  if (warn_array_parameter < 2 && !access->static_p)
+    return NULL_TREE;
+
+  rng[0] = wi::zero (prec);
+  rng[1] = wi::uhwi (access->minsize, prec);
+  /* If the PTR argument points to an array multiply MINSIZE by the size
+     of array element type.  Otherwise, multiply it by the size of what
+     the pointer points to.  */
+  tree eltype = TREE_TYPE (TREE_TYPE (ptr));
+  if (TREE_CODE (eltype) == ARRAY_TYPE)
+    eltype = TREE_TYPE (eltype);
+  tree size = TYPE_SIZE_UNIT (eltype);
+  if (!size || TREE_CODE (size) != INTEGER_CST)
+    return NULL_TREE;
+
+  rng[1] *= wi::to_wide (size, prec);
+  return var;
+}
+
 /* Wrapper around the wide_int overload of get_range.  Returns the same
    result but accepts offset_int instead.  */
 
@@ -4168,6 +4286,21 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
 	  return false;
 	}
 
+      if (gimple_nop_p (stmt))
+	{
+	  /* For a function argument try to determine the byte size
+	     of the array from the current function declaratation
+	     (e.g., attribute access or related).  */
+	  wide_int wr[2];
+	  tree ref = gimple_parm_array_size (ptr, wr, rvals);
+	  if (!ref)
+	    return NULL_TREE;
+	  pref->ref = ref;
+	  pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
+	  pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+	  return true;
+	}
+
       /* TODO: Handle PHI.  */
 
       if (!is_gimple_assign (stmt))
@@ -4220,9 +4353,9 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
 
 /* Convenience wrapper around the above.  */
 
-static tree
+tree
 compute_objsize (tree ptr, int ostype, access_ref *pref,
-		 const vr_values *rvals = NULL)
+		 const vr_values *rvals /* = NULL */)
 {
   bitmap visited = NULL;
 
@@ -4291,7 +4424,7 @@ check_memop_access (tree exp, tree dest, tree src, tree size)
   tree dstsize = compute_objsize (dest, 0, &data.dst);
 
   return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
-		       srcsize, dstsize, true, &data);
+		       srcsize, dstsize, true, true, &data);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -4315,7 +4448,7 @@ expand_builtin_memchr (tree exp, rtx)
       tree size = compute_objsize (arg1, 0, &data.src);
       check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
 		    /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE,
-		    true, &data);
+		    false, true, &data);
     }
 
   return NULL_RTX;
@@ -4599,7 +4732,7 @@ expand_builtin_strcat (tree exp)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
 
   check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
-		destsize, true, &data);
+		destsize, true, true, &data);
 
   return NULL_RTX;
 }
@@ -4624,7 +4757,7 @@ expand_builtin_strcpy (tree exp, rtx target)
       tree destsize = compute_objsize (dest, warn_stringop_overflow - 1,
 				       &data.dst);
       check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
-		    src, destsize, true, &data);
+		    src, destsize, true, true, &data);
     }
 
   if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target))
@@ -4684,7 +4817,7 @@ expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1,
 				       &data.dst);
       check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
-		    src, destsize, true, &data);
+		    src, destsize, true, true, &data);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -4798,7 +4931,7 @@ expand_builtin_stpncpy (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
 
   check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize,
-		true, &data);
+		true, true, &data);
 
   return NULL_RTX;
 }
@@ -4878,7 +5011,7 @@ check_strncat_sizes (tree exp, tree objsize)
   /* The number of bytes to write is LEN but check_access will alsoa
      check SRCLEN if LEN's value isn't known.  */
   return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
-		       objsize, true, &data);
+		       objsize, true, true, &data);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -4951,7 +5084,7 @@ expand_builtin_strncat (tree exp, rtx)
     srclen = maxread;
 
   check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize,
-		true, &data);
+		true, true, &data);
 
   return NULL_RTX;
 }
@@ -4987,7 +5120,7 @@ expand_builtin_strncpy (tree exp, rtx target)
       /* The number of bytes to write is LEN but check_access will also
 	 check SLEN if LEN's value isn't known.  */
       check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
-		    destsize, true, &data);
+		    destsize, true, true, &data);
     }
 
   /* We must be passed a constant len and src parameter.  */
@@ -5304,14 +5437,14 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
   tree size = compute_objsize (arg1, 0, &data.src);
   no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
 			      len, /*maxread=*/NULL_TREE, size,
-			      /*objsize=*/NULL_TREE, true, &data);
+			      /*objsize=*/NULL_TREE, false, true, &data);
   if (no_overflow)
     {
       access_data data;
       size = compute_objsize (arg2, 0, &data.src);
       no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
 				  len,  /*maxread=*/NULL_TREE, size,
-				  /*objsize=*/NULL_TREE, true, &data);
+				  /*objsize=*/NULL_TREE, false, true, &data);
     }
 
   /* If the specified length exceeds the size of either object, 
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 8b812ceb2c4..c909ae11845 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -133,13 +133,6 @@ extern tree fold_call_stmt (gcall *, bool);
 extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
 extern bool is_simple_builtin (tree);
 extern bool is_inexpensive_builtin (tree);
-
-class vr_values;
-tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
-			     const vr_values * = NULL);
-extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
-			     const vr_values * = NULL);
-
 extern bool readonly_data_expr (tree exp);
 extern bool init_target_chars (void);
 extern unsigned HOST_WIDE_INT target_newline;
@@ -184,7 +177,15 @@ struct access_data
   access_ref dst, src;
 };
 
+class vr_values;
+extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
+				    const vr_values * = NULL);
+extern tree gimple_parm_array_size (tree, wide_int[2], const vr_values * = NULL);
+extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
+			     const vr_values * = NULL);
+extern tree compute_objsize (tree, int, access_ref *, const vr_values * = NULL);
+
 extern bool check_access (tree, tree, tree, tree, tree, tree, tree,
-			  bool = true, const access_data * = NULL);
+			  bool = true, bool = true, const access_data * = NULL);
 
 #endif /* GCC_BUILTINS_H */
diff --git a/gcc/calls.c b/gcc/calls.c
index 622417ff74b..ecb966d6cb6 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -58,6 +58,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "gimple-fold.h"
 
+#include "tree-pretty-print.h"
+
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
 #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
 
@@ -1883,36 +1885,20 @@ fntype_argno_type (tree fntype, unsigned argno)
   return NULL_TREE;
 }
 
-/* Helper to append the "rdwr" attribute specification described
-   by ACCESS to the array ATTRSTR with size STRSIZE.  Used in
+/* Helper to append the "human readable" attribute access specification
+   described by ACCESS to the array ATTRSTR with size STRSIZE.  Used in
    diagnostics.  */
 
 static inline void
 append_attrname (const std::pair<int, attr_access> &access,
 		 char *attrstr, size_t strsize)
 {
-  /* Append the relevant attribute to the string.  This (deliberately)
-     appends the attribute pointer operand even when none was specified.  */
-  size_t len = strlen (attrstr);
-
-  const char* const atname
-    = (access.second.mode == attr_access::read_only
-       ? "read_only"
-       : (access.second.mode == attr_access::write_only
-	  ? "write_only"
-	  : (access.second.mode == attr_access::read_write
-	     ? "read_write" : "none")));
-
-  const char *sep = len ? ", " : "";
-
-  if (access.second.sizarg == UINT_MAX)
-    snprintf (attrstr + len, strsize - len,
-	      "%s%s (%i)", sep, atname,
-	      access.second.ptrarg + 1);
-  else
-    snprintf (attrstr + len, strsize - len,
-	      "%s%s (%i, %i)", sep, atname,
-	      access.second.ptrarg + 1, access.second.sizarg + 1);
+  if (access.second.internal_p)
+    return;
+
+  tree str = access.second.to_external_string ();
+  gcc_assert (strsize >= (size_t) TREE_STRING_LENGTH (str));
+  strcpy (attrstr, TREE_STRING_POINTER (str));
 }
 
 /* Iterate over attribute access read-only, read-write, and write-only
@@ -1939,6 +1925,7 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
     return;
 
   auto_diagnostic_group adg;
+  bool warned = false;
 
   /* A string describing the attributes that the warnings issued by this
      function apply to.  Used to print one informational note per function
@@ -1967,35 +1954,40 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
       if (!access.second.ptr)
 	continue;
 
-      tree argtype = fntype_argno_type (fntype, ptridx);
-      argtype = TREE_TYPE (argtype);
+      tree ptrtype = fntype_argno_type (fntype, ptridx);
+      tree argtype = TREE_TYPE (ptrtype);
 
-      tree size;
+      /* The size of the access by the call.  */
+      tree access_size;
       if (sizidx == -1)
 	{
-	  /* If only the pointer attribute operand was specified
-	     and not size, set SIZE to the size of one element of
-	     the pointed to type to detect smaller objects (null
-	     pointers are diagnosed in this case only if
-	     the pointer is also declared with attribute nonnull.  */
-	  size = size_one_node;
+	  /* If only the pointer attribute operand was specified and
+	     not size, set SIZE to the greater of MINSIZE or size of
+	     one element of the pointed to type to detect smaller
+	     objects (null pointers are diagnosed in this case only
+	     if the pointer is also declared with attribute nonnull.  */
+	  if (access.second.minsize
+	      && access.second.minsize != HOST_WIDE_INT_M1U)
+	    access_size = build_int_cstu (sizetype, access.second.minsize);
+	  else
+	    access_size = size_one_node;
 	}
       else
-	size = rwm->get (sizidx)->size;
+	access_size = rwm->get (sizidx)->size;
 
+      bool warned = false;
+      location_t loc = EXPR_LOCATION (exp);
       tree ptr = access.second.ptr;
       tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
-      if (get_size_range (size, sizrng, true)
+      if (get_size_range (access_size, sizrng, true)
 	  && tree_int_cst_sgn (sizrng[0]) < 0
 	  && tree_int_cst_sgn (sizrng[1]) < 0)
 	{
 	  /* Warn about negative sizes.  */
-	  bool warned = false;
-	  location_t loc = EXPR_LOCATION (exp);
 	  if (tree_int_cst_equal (sizrng[0], sizrng[1]))
 	    warned = warning_at (loc, OPT_Wstringop_overflow_,
 				 "%Kargument %i value %E is negative",
-				 exp, sizidx + 1, size);
+				 exp, sizidx + 1, access_size);
 	  else
 	    warned = warning_at (loc, OPT_Wstringop_overflow_,
 				 "%Kargument %i range [%E, %E] is negative",
@@ -2012,44 +2004,56 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
 	{
 	  if (COMPLETE_TYPE_P (argtype))
 	    {
-	      /* Multiple SIZE by the size of the type the pointer
-		 argument points to.  If it's incomplete the size
-		 is used as is.  */
-	      size = NULL_TREE;
+	      /* Multiply ACCESS_SIZE by the size of the type the pointer
+		 argument points to.  If it's incomplete the size is used
+		 as is.  */
+	      access_size = NULL_TREE;
 	      if (tree argsize = TYPE_SIZE_UNIT (argtype))
 		if (TREE_CODE (argsize) == INTEGER_CST)
 		  {
 		    const int prec = TYPE_PRECISION (sizetype);
 		    wide_int minsize = wi::to_wide (sizrng[0], prec);
 		    minsize *= wi::to_wide (argsize, prec);
-		    size = wide_int_to_tree (sizetype, minsize);
+		    access_size = wide_int_to_tree (sizetype, minsize);
 		  }
 	    }
 	}
       else
-	size = NULL_TREE;
+	access_size = NULL_TREE;
 
-      if (sizidx >= 0
-	  && integer_zerop (ptr)
-	  && tree_int_cst_sgn (sizrng[0]) > 0)
+      if (integer_zerop (ptr))
 	{
-	  /* Warn about null pointers with positive sizes.  This is
-	     different from also declaring the pointer argument with
-	     attribute nonnull when the function accepts null pointers
-	     only when the corresponding size is zero.  */
-	  bool warned = false;
-	  const location_t loc = EXPR_LOC_OR_LOC (ptr, EXPR_LOCATION (exp));
-	  if (tree_int_cst_equal (sizrng[0], sizrng[1]))
-	    warned = warning_at (loc, OPT_Wnonnull,
-				 "%Kargument %i is null but the corresponding "
-				 "size argument %i value is %E",
-				 exp, ptridx + 1, sizidx + 1, size);
-	  else
-	    warned = warning_at (loc, OPT_Wnonnull,
-				 "%Kargument %i is null but the corresponding "
-				 "size argument %i range is [%E, %E]",
-				 exp, ptridx + 1, sizidx + 1,
-				 sizrng[0], sizrng[1]);
+	  if (sizidx >= 0 && tree_int_cst_sgn (sizrng[0]) > 0)
+	    {
+	      /* Warn about null pointers with positive sizes.  This is
+		 different from also declaring the pointer argument with
+		 attribute nonnull when the function accepts null pointers
+		 only when the corresponding size is zero.  */
+	      if (tree_int_cst_equal (sizrng[0], sizrng[1]))
+		warned = warning_at (loc, OPT_Wnonnull,
+				     "%Kargument %i is null but "
+				     "the corresponding size argument %i "
+				     "value is %E",
+				     exp, ptridx + 1, sizidx + 1, access_size);
+	      else
+		warned = warning_at (loc, OPT_Wnonnull,
+				     "%Kargument %i is null but "
+				     "the corresponding size argument %i "
+				     "range is [%E, %E]",
+				     exp, ptridx + 1, sizidx + 1,
+				     sizrng[0], sizrng[1]);
+	    }
+	  else if (access_size && access.second.static_p)
+	    {
+	      /* Warn about null pointers for [static N] array arguments
+		 but do not warn for ordinary (i.e., nonstatic) arrays.  */
+	      warned = warning_at (loc, OPT_Wnonnull,
+				   "%Kargument %i to %<%T[static %E]%> null "
+				   "where non-null expected",
+				   exp, ptridx + 1, argtype,
+				   sizrng[0]);
+	    }
+
 	  if (warned)
 	    {
 	      append_attrname (access, attrstr, sizeof attrstr);
@@ -2058,54 +2062,76 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
 	    }
 	}
 
-      tree objsize = compute_objsize (ptr, 0);
+      access_data data;
+      access_ref* const pobj = (access.second.mode == attr_access::write_only
+				? &data.dst : &data.src);
+      tree objsize = compute_objsize (ptr, 1, pobj);
 
-      tree srcsize;
-      if (access.second.mode == attr_access::write_only)
-	{
-	  /* For a write-only argument there is no source.  */
-	  srcsize = NULL_TREE;
-	}
-      else
+      /* The size of the destination or source object.  */
+      tree dstsize = NULL_TREE, srcsize = NULL_TREE;
+      if (access.second.mode == attr_access::read_only
+	  || access.second.mode == attr_access::none)
 	{
-	  /* For read-only and read-write attributes also set the source
-	     size.  */
+	  /* For a read-only argument there is no destination.  For
+	     no access, set the source as well and differentiate via
+	     the access flag below.  */
 	  srcsize = objsize;
-	  if (access.second.mode == attr_access::read_only
-	      || access.second.mode == attr_access::none)
-	    {
-	      /* For a read-only attribute there is no destination so
-		 clear OBJSIZE.  This emits "reading N bytes" kind of
-		 diagnostics instead of the "writing N bytes" kind,
-		 unless MODE is none.  */
-	      objsize = NULL_TREE;
-	    }
 	}
+      else
+	dstsize = objsize;
 
-      /* Clear the no-warning bit in case it was set in a prior
-	 iteration so that accesses via different arguments are
-	 diagnosed.  */
+      /* Clear the no-warning bit in case it was set by check_access
+	 in a prior iteration so that accesses via different arguments
+	 are diagnosed.  */
       TREE_NO_WARNING (exp) = false;
-      check_access (exp, NULL_TREE, NULL_TREE, size, /*maxread=*/ NULL_TREE,
-		    srcsize, objsize, access.second.mode != attr_access::none);
+      /* Set for accesses by functions explicitly declared with attribute
+	 access other than none, and clear for functions internally decorated
+	 with the attribute for user-defined functions with mode of none.  */
+      const bool read_access
+	= (access.second.mode != attr_access::none
+	   && access.second.mode != attr_access::write_only);
+      const bool write_access
+	= (access.second.mode != attr_access::none
+	   && access.second.mode != attr_access::read_only
+	   && !TYPE_READONLY (argtype));
+      check_access (exp, /*dst=*/ NULL_TREE, /*src=*/ NULL_TREE, access_size,
+		    /*maxread=*/ NULL_TREE, srcsize, dstsize, write_access,
+		    read_access, &data);
 
       if (TREE_NO_WARNING (exp))
-	/* If check_access issued a warning above, append the relevant
-	   attribute to the string.  */
-	append_attrname (access, attrstr, sizeof attrstr);
-    }
+	{
+	  warned = true;
 
-  if (!*attrstr)
-    return;
+	  if (access.second.internal_p)
+	    inform (loc, "referencing argument %u of type %qT",
+		    ptridx + 1, ptrtype);
+	  else
+	    /* If check_access issued a warning above, append the relevant
+	       attribute to the string.  */
+	    append_attrname (access, attrstr, sizeof attrstr);
+	}
+    }
 
-  if (fndecl)
-    inform (DECL_SOURCE_LOCATION (fndecl),
-	    "in a call to function %qD declared with attribute %qs",
-	    fndecl, attrstr);
-  else
-    inform (EXPR_LOCATION (fndecl),
-	    "in a call with type %qT and attribute %qs",
-	    fntype, attrstr);
+  if (*attrstr)
+    {
+      if (fndecl)
+	inform (DECL_SOURCE_LOCATION (fndecl),
+		"in a call to function %qD declared with attribute %qs",
+		fndecl, attrstr);
+      else
+	inform (EXPR_LOCATION (fndecl),
+		"in a call with type %qT and attribute %qs",
+		fntype, attrstr);
+    }
+  else if (warned)
+    {
+      if (fndecl)
+	inform (DECL_SOURCE_LOCATION (fndecl),
+		"in a call to function %qD", fndecl);
+      else
+	inform (EXPR_LOCATION (fndecl),
+		"in a call with type %qT", fntype);
+    }
 
   /* Set the bit in case if was cleared and not set above.  */
   TREE_NO_WARNING (exp) = true;
diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
index c4127b805ab..38e06ba5e62 100644
--- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
@@ -1,7 +1,7 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument" } */
-/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat -Wno-stringop-overflow" { target c } } */
 /* { dg-require-effective-target alloca } */
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/g++.dg/ext/attr-access.C b/gcc/testsuite/g++.dg/ext/attr-access.C
index 3b9c1a36e30..b7b2a5fc4bf 100644
--- a/gcc/testsuite/g++.dg/ext/attr-access.C
+++ b/gcc/testsuite/g++.dg/ext/attr-access.C
@@ -42,8 +42,8 @@ void call_rdwrp1_rdwrr2_O0 (void)
   int32_t x[1] = { };
 
   rdwrp1_rdwrr2 (x, x[0]);
-  rdwrp1_rdwrr2 (x, x[1]);        // { dg-warning "writing 4 bytes into a region of size 0" }
-  rdwrp1_rdwrr2 (x + 1, x[0]);    // { dg-warning "writing 4 bytes into a region of size 0" }
+  rdwrp1_rdwrr2 (x, x[1]);        // { dg-warning "accessing 4 bytes in a region of size 0" }
+  rdwrp1_rdwrr2 (x + 1, x[0]);    // { dg-warning "accessing 4 bytes in a region of size 0" }
 }
 
 void call_wop1_wor2_O0 (void)
@@ -84,12 +84,12 @@ void call_rdwrp1_rdwrr2_O1 (void)
   int32_t &r2 = *(int32_t*)((char*)p1 + 1);
 
   rdwrp1_rdwrr2 (x, x[0]);
-  rdwrp1_rdwrr2 (x, x[1]);        // { dg-warning "writing 4 bytes into a region of size 0" }
-  rdwrp1_rdwrr2 (x + 1, x[0]);    // { dg-warning "writing 4 bytes into a region of size 0" }
+  rdwrp1_rdwrr2 (x, x[1]);        // { dg-warning "accessing 4 bytes in a region of size 0" }
+  rdwrp1_rdwrr2 (x + 1, x[0]);    // { dg-warning "accessing 4 bytes in a region of size 0" }
 
   rdwrp1_rdwrr2 (p0, r0);
-  rdwrp1_rdwrr2 (p0, r2);         // { dg-warning "writing 4 bytes into a region of size 2" }
-  rdwrp1_rdwrr2 (p1, r0);         // { dg-warning "writing 4 bytes into a region of size 3" }
+  rdwrp1_rdwrr2 (p0, r2);         // { dg-warning "accessing 4 bytes in a region of size 2" }
+  rdwrp1_rdwrr2 (p1, r0);         // { dg-warning "accessing 4 bytes in a region of size 3" }
 }
 
 void call_wop1_wor2_O1 (void)
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-30.c b/gcc/testsuite/gcc.dg/Warray-bounds-30.c
index b9965682101..048a95d6dcf 100644
--- a/gcc/testsuite/gcc.dg/Warray-bounds-30.c
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-30.c
@@ -73,8 +73,7 @@ void test_global_int_array (void)
 
   T (&p[min]);      /* { dg-warning "subscript -\[0-9\]+ is \(below|outside\) array bounds of .int\\\[1]." } */
   T (&p[-1]);       /* { dg-warning "subscript -1 is \(below|outside\) array bounds of .int\\\[1]." } */
-  T (&p[0]);
-  T (&p[1]);
+  T (&p[0], &p[1]);
   T (&p[2]);        /* { dg-warning "subscript 2 is \(above|outside\) array bounds of .int\\\[1]." } */
   T (&p[max]);      /* { dg-warning "subscript \[0-9\]+ is \(above|outside\) array bounds of .int\\\[1]." } */
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c
index 6d9fcb9d52e..755ebea5ff4 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c
@@ -20,7 +20,7 @@ typedef __INT32_TYPE__ int32_t;
 /* Exercise null pointer detection.  */
 
 RDONLY (2, 1) void
-rd2_1 (int, const void*);       // { dg-message "in a call to function 'rd2_1' declared with attribute 'read_only \\\(2, 1\\\)" }
+rd2_1 (int, const void*);       // { dg-message "in a call to function 'rd2_1' declared with attribute 'access \\\(read_only, 2, 1\\\)" "note" }
 
 void test_rd2_1 (void)
 {
@@ -44,7 +44,7 @@ void test_rd2_1 (void)
 }
 
 WRONLY (3, 1) void
-wr3_1 (int, int, void*);        // { dg-message "in a call to function 'wr3_1' declared with attribute 'write_only \\\(3, 1\\\)" }
+wr3_1 (int, int, void*);        // { dg-message "in a call to function 'wr3_1' declared with attribute 'access \\\(write_only, 3, 1\\\)" }
 
 void test_wr3_1 (void)
 {
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c
index a21a1f17a2b..049d1c6981c 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c
@@ -23,7 +23,7 @@ extern char d1[1], d2[2], d3[3];
    the attribute without a size operand.  */
 
 RDONLY (1) void
-rd1_int (const int32_t*);   // { dg-message "in a call to function 'rd1_int' declared with attribute 'read_only \\\(1\\\)'" }
+rd1_int (const int32_t*);   // { dg-message "in a call to function 'rd1_int' declared with attribute 'access \\\(read_only, 1\\\)'" "note" }
 
 void test_rd1_int (void)
 {
@@ -39,7 +39,7 @@ void test_rd1_int (void)
    the attribute and with non-zero size.  */
 
 RDONLY (2, 1) void
-rd2_1 (int, const void*);   // { dg-message "in a call to function 'rd2_1' declared with attribute 'read_only \\\(2, 1\\\)" }
+rd2_1 (int, const void*);   // { dg-message "in a call to function 'rd2_1' declared with attribute 'access \\\(read_only, 2, 1\\\)" "note" }
 
 void test_rd2_1 (void)
 {
@@ -49,7 +49,7 @@ void test_rd2_1 (void)
 }
 
 WRONLY (3, 1) void
-wr3_1 (int, int, void*);    // { dg-message "in a call to function 'wr3_1' declared with attribute 'write_only \\\(3, 1\\\)" }
+wr3_1 (int, int, void*);    // { dg-message "in a call to function 'wr3_1' declared with attribute 'access \\\(write_only, 3, 1\\\)" "note" }
 
 void test_wr3_1 (void)
 {
@@ -157,7 +157,7 @@ void test_rd6_1_wr5_2_rd4_3 (void)
 {
   rd6_1_wr5_2_rd4_3 (7, 2, 1, d1, d2, s3);   // { dg-warning "reading 7 bytes from a region of size 3" }
   rd6_1_wr5_2_rd4_3 (3, 8, 1, d1, d2, s3);   // { dg-warning "writing 8 bytes into a region of size 2" }
-  rd6_1_wr5_2_rd4_3 (3, 2, 9, d1, d2, s3);   // { dg-warning "writing 9 bytes into a region of size 1" }
+  rd6_1_wr5_2_rd4_3 (3, 2, 9, d1, d2, s3);   // { dg-warning "accessing 9 bytes in a region of size 1" }
 }
 
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-40.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-40.c
new file mode 100644
index 00000000000..386c92dc7a8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-40.c
@@ -0,0 +1,120 @@
+/* PR c/50584 - No warning for passing small array to C99 static array
+   declarator
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+typedef __INT16_TYPE__ int16_t;
+
+void fa2 (int16_t[2]);
+void fxa2 (int16_t[2]) __attribute__ ((nonnull));
+
+void fas2 (int16_t[static 2]);
+
+void fvla (unsigned n, int16_t[n]);
+
+void test_array_1_dim (void)
+{
+  int16_t a1[1];
+  int16_t a2[2];
+  int16_t i;
+
+  fa2 (0);
+  fa2 (a2);
+  fa2 (a1);                   // { dg-warning "'fa2' accessing 4 bytes in a region of size 2 " }
+  fa2 (&i);                   // { dg-warning "'fa2' accessing 4 bytes in a region of size 2 " }
+
+  fxa2 (0);                   // { dg-warning "\\\[-Wnonnull" }
+  fxa2 (a2);
+  fxa2 (a1);                  // { dg-warning "'fxa2' accessing 4 bytes in a region of size 2 " }
+  fxa2 (&i);                  // { dg-warning "'fxa2' accessing 4 bytes in a region of size 2 " }
+
+  fas2 (0);                   // { dg-warning "\\\[-Wnonnull" }
+  fas2 (a2);
+  fas2 (a1);                  // { dg-warning "'fas2' accessing 4 bytes in a region of size 2 " }
+  fas2 (&i);                  // { dg-warning "'fas2' accessing 4 bytes in a region of size 2 " }
+
+  fvla (1, 0);                // { dg-warning "\\\[-Wnonnull" }
+  fvla (1, &i);
+  fvla (2, a2);
+  fvla (2, a1);               // { dg-warning "'fvla' accessing 4 bytes in a region of size 2 " }
+  fvla (2, &i);               // { dg-warning "'fvla' accessing 4 bytes in a region of size 2 " }
+}
+
+
+void fac2 (const int16_t[2]);
+void fxac2 (const int16_t[2]) __attribute__ ((nonnull));
+
+void facs2 (const int16_t[static 2]);
+
+void fvlac (unsigned n, const int16_t[n]);
+
+void test_const_array_1_dim (void)
+{
+  int16_t a1[1];
+  int16_t a2[2];
+  int16_t i;
+
+  fac2 (0);
+  fac2 (a2);
+  fac2 (a1);                  // { dg-warning "'fac2' reading 4 bytes from a region of size 2 " }
+  fac2 (&i);                  // { dg-warning "'fac2' reading 4 bytes from a region of size 2 " }
+
+  fxac2 (0);                  // { dg-warning "\\\[-Wnonnull" }
+  fxac2 (a2);
+  fxac2 (a1);                 // { dg-warning "'fxac2' reading 4 bytes from a region of size 2 " }
+  fxac2 (&i);                 // { dg-warning "'fxac2' reading 4 bytes from a region of size 2 " }
+
+  facs2 (0);                  // { dg-warning "\\\[-Wnonnull" }
+  facs2 (a2);
+  facs2 (a1);                 // { dg-warning "'facs2' reading 4 bytes from a region of size 2 " }
+  facs2 (&i);                 // { dg-warning "'facs2' reading 4 bytes from a region of size 2 " }
+
+  fvlac (1, 0);               // { dg-warning "\\\[-Wnonnull" }
+  fvlac (1, &i);
+  fvlac (2, a2);
+  fvlac (2, a1);              // { dg-warning "'fvlac' reading 4 bytes from a region of size 2 " }
+  fvlac (2, &i);              // { dg-warning "'fvlac' reading 4 bytes from a region of size 2 " }
+}
+
+
+void fca3x5 (int16_t[3][5]);
+void fcas5x7 (int16_t[static 5][7]);
+
+struct Snx5 { int16_t a3x5[3][5], a2x5[2][5], a1x5[1][5]; };
+struct Snx7 { int16_t a5x7[5][7], a4x7[4][7], a1x7[1][7]; };
+struct S0x7 { int x; int16_t a0x7[0][7]; };
+
+void test_array_2_dim (struct Snx5 *px5, struct Snx7 *px7, struct S0x7 *p0x7)
+{
+  int16_t a0x5[0][5], a1x5[1][5], a2x5[2][5], a3x5[3][5], a4x5[4][5];
+
+  fca3x5 (a3x5);
+  fca3x5 (a4x5);
+  fca3x5 (a2x5);              // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 20" }
+  fca3x5 (a1x5);              // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 10" }
+  fca3x5 (a0x5);              // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 0" }
+
+  fca3x5 (px5->a3x5);
+  fca3x5 (px5->a2x5);         // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 20" }
+  fca3x5 (px5->a1x5);         // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 10" "pr96346" { xfail *-*-* } }
+
+  {
+    int16_t (*pa2x5)[5] = &a2x5[0];
+    fca3x5 (pa2x5);           // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 10" }
+    ++pa2x5;
+    fca3x5 (pa2x5);           // { dg-warning "'fca3x5' accessing 30 bytes " }
+  }
+
+  int16_t a0x7[0][7], a1x7[1][7], a4x7[4][7], a5x7[5][7], a99x7[99][7];
+  fcas5x7 (a99x7);
+  fcas5x7 (a5x7);
+  fcas5x7 (a4x7);             // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 56" }
+  fcas5x7 (a1x7);             // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 14" }
+  fcas5x7 (a0x7);             // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 0" }
+
+  fcas5x7 (px7->a5x7);
+  fcas5x7 (px7->a4x7);        // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 56" }
+  fcas5x7 (px7->a1x7);        // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 14" "pr96346" { xfail *-*-* } }
+
+  fcas5x7 (p0x7->a0x7);       // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 0" "pr96346" { xfail *-*-* } }
+}
diff --git a/gcc/testsuite/gcc.dg/attr-access-2.c b/gcc/testsuite/gcc.dg/attr-access-2.c
new file mode 100644
index 00000000000..7167c756320
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-access-2.c
@@ -0,0 +1,116 @@
+/* PR 50584 - No warning for passing small array to C99 static array declarator
+   Exercise interaction between explicit attribute access and VLA parameters.
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define RW(...) __attribute__ ((access (read_write, __VA_ARGS__)))
+
+
+void f1 (int n, int[n], int);               // { dg-message "designating the bound of variable length array argument 2" "note" }
+
+// Verify that a redundant attribute access doesn't trigger a warning.
+RW (2, 1) void f1 (int n, int[n], int);
+
+RW (2, 3) void f1 (int n, int[n], int);     // { dg-warning "attribute 'access\\\(read_write, 2, 3\\\)' positional argument 2 conflicts with previous designation by argument 1" }
+
+
+/* Verify that applying the attribute to a VLA with an unspecified bound
+   doesn't trigger any warnings, both with and without a size operand.  */
+          void f2 (int, int[*], int);
+RW (2)    void f2 (int, int[*], int);
+RW (2, 3) void f2 (int, int[*], int);
+
+/* Designating a parameter that comes before the VLA is the same as
+   using the standard VLA int[n] syntax.  It might be worth issuing
+   a portability warning suggesting to prefer the standard syntax.  */
+          void f3 (int, int[*], int);
+RW (2, 1) void f3 (int, int[*], int);
+
+/* Designating a parameter that comes after the VLA cannot be expressed
+   using the standard VLA int[n] syntax.  Verify it doesn't trigger
+   a warning.  */
+          void f4 (int, int[*], int);
+RW (2, 3) void f4 (int, int[*], int);
+
+/* Also verify the same on the same declaration.  */
+          void f5 (int[*], int) RW (1, 2);
+RW (1, 2) void f5 (int[*], int);
+RW (1, 2) void f5 (int[*], int) RW (1, 2);
+
+
+/* Verify that designating a VLA parameter with an explicit bound without
+   also designating the same bound parameter triggers a warning (it has
+   a different meaning).  */
+       void f7 (int n, int[n]);         // { dg-message "21:note: designating the bound of variable length array argument 2" "note" }
+RW (2) void f7 (int n, int[n]);         // { dg-warning "attribute 'access\\\(read_write, 2\\\)' missing positional argument 2 provided in previous designation by argument 1" }
+
+          void f8 (int n, int[n]);
+RW (2, 1) void f8 (int n, int[n]);
+
+
+          void f9 (int, char[]);        // { dg-message "25:note: previously declared as an ordinary array 'char\\\[]'" note" }
+RW (2)    void f9 (int n, char a[n])    // { dg-warning "argument 2 of type 'char\\\[n]' declared as a variable length array" }
+                                        // { dg-warning "attribute 'access *\\\(read_write, 2\\\)' positional argument 2 missing in previous designation" "" { target *-*-* } .-1 }
+                                        // { dg-message "24:note: designating the bound of variable length array argument 2" "note" { target *-*-* } .-2 }
+{ (void)&n; (void)&a; }
+
+
+          void f10 (int, char[]);       // { dg-message "26:note: previously declared as an ordinary array 'char\\\[]'" "note" }
+RW (2, 1) void f10 (int n, char a[n])   // { dg-warning "attribute 'access *\\\(read_write, 2, 1\\\)' positional argument 2 missing in previous designation" "pr????" { xfail *-*-* } }
+                                        // { dg-warning "argument 2 of type 'char\\\[n]' declared as a variable length array"  "" { target *-*-* } .-1 }
+{ (void)&n; (void)&a; }
+
+
+/* The following is diagnosed to point out declarations with the T[*]
+   form in headers where specifying the bound is just as important as
+   in the definition (to detect bugs).  */
+          void f11 (int, char[*]);      // { dg-message "previously declared as 'char\\\[\\\*]' with 1 unspecified variable bound" "note" }
+          void f11 (int m, char a[m]);  // { dg-warning "argument 2 of type 'char\\\[m]' declared with 0 unspecified variable bounds" }
+RW (2, 1) void f11 (int n, char arr[n]) // { dg-warning "argument 2 of type 'char\\\[n]' declared with 0 unspecified variable bounds" }
+{ (void)&n; (void)&arr; }
+
+
+/* Verify that redeclaring a function with attribute access applying
+   to an array parameter of any form is not diagnosed.  */
+          void f12__ (int, int[]) RW (2, 1);
+RW (2, 1) void f12__ (int, int[]);
+
+          void f12_3 (int, int[3]) RW (2, 1);
+RW (2, 1) void f12_3 (int, int[3]);
+
+          void f12_n (int n, int[n]) RW (2, 1);
+RW (2, 1) void f12_n (int n, int[n]);
+
+          void f12_x (int, int[*]) RW (2, 1);
+RW (2, 1) void f12_x (int, int[*]);
+
+          void f13__ (int, int[]);
+RW (2, 1) void f13__ (int, int[]);
+
+          void f13_5 (int, int[5]);
+RW (2, 1) void f13_5 (int, int[5]);
+
+          void f13_n (int n, int[n]);
+RW (2, 1) void f13_n (int n, int[n]);
+
+          void f13_x (int, int[*]);
+RW (2, 1) void f13_x (int, int[*]);
+
+RW (2, 1) void f14__ (int, int[]);
+          void f14__ (int, int[]);
+
+RW (2, 1) void f14_7 (int, int[7]);
+          void f14_7 (int, int[7]);
+
+RW (2, 1) void f14_n (int n, int[n]);
+          void f14_n (int n, int[n]);
+
+RW (2, 1) void f14_x (int, int[*]);
+          void f14_x (int, int[*]);
+
+typedef void G1 (int n, int[n], int);
+
+G1 g1;
+
+RW (2, 3) void g1 (int n, int[n], int);     // { dg-warning "24: attribute 'access *\\\(read_write, 2, 3\\\)' positional argument 2 conflicts with previous designation by argument 3" }
+// { dg-message "designating the bound of variable length array argument 2" "note" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/gcc.dg/attr-access-none.c b/gcc/testsuite/gcc.dg/attr-access-none.c
index d983f2fac06..85f9e73b3f4 100644
--- a/gcc/testsuite/gcc.dg/attr-access-none.c
+++ b/gcc/testsuite/gcc.dg/attr-access-none.c
@@ -23,7 +23,7 @@ void nowarn_fnone_pcv1 (void)
 
 
 int __attribute__ ((access (none, 1, 2)))
-fnone_pcv1_2 (const void*, int);  // { dg-message "in a call to function 'fnone_pcv1_2' declared with attribute 'none \\\(1, 2\\\)'" }
+fnone_pcv1_2 (const void*, int);  // { dg-message "in a call to function 'fnone_pcv1_2' declared with attribute 'access \\\(none, 1, 2\\\)'" "note" }
 
 void nowarn_fnone_pcv1_2 (void)
 {
@@ -34,5 +34,5 @@ void nowarn_fnone_pcv1_2 (void)
 void warn_fnone_pcv1_2 (void)
 {
   char a[3];
-  fnone_pcv1_2 (a, 4);        // { dg-warning "expecting 4 bytes in a region of size 3" }
+  fnone_pcv1_2 (a, 4);        // { dg-warning "expecting 4 or more bytes in a region of size 3" }
 }

Reply via email to