Correct handling of indices into arrays with elements larger than 1 (PR c++/96511)

Resolves:
PR c++/96511 - Incorrect -Wplacement-new on POINTER_PLUS into an array with 4-byte elements
PR middle-end/96384 - bogus -Wstringop-overflow= storing into multidimensional array with index in range

gcc/ChangeLog:

	PR c++/96511
	PR middle-end/96384
	* builtins.c (get_range): Return full range of type when neither
	value nor its range is available.  Fail for ranges inverted due
	to the signedness of offsets.
	(compute_objsize): Handle more special array members.  Handle
	POINTER_PLUS_EXPR and VIEW_CONVERT_EXPR that come up in front end
	code.
	(access_ref::offset_bounded): Define new member function.
	* builtins.h (access_ref::eval): New data member.
	(access_ref::offset_bounded): New member function.
	(access_ref::offset_zero): New member function.
	(compute_objsize): Declare a new overload.
	* gimple-array-bounds.cc (array_bounds_checker::check_array_ref): Use
	enum special_array_member.
	* tree.c (component_ref_size): Use special_array_member.
	* tree.h (special_array_member): Define a new type.
	(component_ref_size): Change signature.

gcc/cp/ChangeLog:

	PR c++/96511
	PR middle-end/96384
	* init.c (warn_placement_new_too_small): Call builtin_objsize instead
	of duplicating what it does.

gcc/testsuite/ChangeLog:

	PR c++/96511
	PR middle-end/96384
	* g++.dg/init/strlen.C: Add expected warning.
	* g++.dg/warn/Wplacement-new-size-1.C: Relax warnings.
	* g++.dg/warn/Wplacement-new-size-2.C: Same.
	* g++.dg/warn/Wplacement-new-size-6.C: Same.
	* gcc.dg/Warray-bounds-58.c: Adjust
	* gcc.dg/Wstringop-overflow-37.c: Same.
	* g++.dg/warn/Wplacement-new-size-7.C: New test.
This commit is contained in:
Martin Sebor 2020-10-09 13:56:53 -06:00
parent 71dbabccbf
commit de05c19d5f
13 changed files with 392 additions and 324 deletions

View file

@ -200,7 +200,7 @@ static void expand_builtin_sync_synchronize (void);
access_ref::access_ref (tree bound /* = NULL_TREE */, access_ref::access_ref (tree bound /* = NULL_TREE */,
bool minaccess /* = false */) bool minaccess /* = false */)
: ref () : ref (), eval ([](tree x){ return x; }), trail1special (true)
{ {
/* Set to valid. */ /* Set to valid. */
offrng[0] = offrng[1] = 0; offrng[0] = offrng[1] = 0;
@ -4370,10 +4370,34 @@ static bool
get_range (tree x, gimple *stmt, signop sgn, offset_int r[2], get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
range_query *rvals /* = NULL */) range_query *rvals /* = NULL */)
{ {
tree type = TREE_TYPE (x);
if (TREE_CODE (x) != INTEGER_CST
&& TREE_CODE (x) != SSA_NAME)
{
if (TYPE_UNSIGNED (type))
{
if (sgn == SIGNED)
type = signed_type_for (type);
}
else if (sgn == UNSIGNED)
type = unsigned_type_for (type);
r[0] = wi::to_offset (TYPE_MIN_VALUE (type));
r[1] = wi::to_offset (TYPE_MAX_VALUE (type));
return x;
}
wide_int wr[2]; wide_int wr[2];
if (!get_range (x, stmt, wr, rvals)) if (!get_range (x, stmt, wr, rvals))
return false; return false;
/* Only convert signed integers or unsigned sizetype to a signed
offset and avoid converting large positive values in narrower
types to negative offsets. */
if (TYPE_UNSIGNED (type)
&& wr[0].get_precision () < TYPE_PRECISION (sizetype))
sgn = UNSIGNED;
r[0] = offset_int::from (wr[0], sgn); r[0] = offset_int::from (wr[0], sgn);
r[1] = offset_int::from (wr[1], sgn); r[1] = offset_int::from (wr[1], sgn);
return true; return true;
@ -4394,9 +4418,11 @@ get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
to influence code generation or optimization. */ to influence code generation or optimization. */
static bool static bool
compute_objsize (tree ptr, int ostype, access_ref *pref, compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
bitmap *visited, range_query *rvals /* = NULL */) range_query *rvals)
{ {
STRIP_NOPS (ptr);
const bool addr = TREE_CODE (ptr) == ADDR_EXPR; const bool addr = TREE_CODE (ptr) == ADDR_EXPR;
if (addr) if (addr)
ptr = TREE_OPERAND (ptr, 0); ptr = TREE_OPERAND (ptr, 0);
@ -4408,26 +4434,29 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr))) if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
return false; return false;
tree size = decl_init_size (ptr, false);
if (!size || TREE_CODE (size) != INTEGER_CST)
return false;
pref->ref = ptr; pref->ref = ptr;
if (tree size = decl_init_size (ptr, false))
if (TREE_CODE (size) == INTEGER_CST)
{
pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size); pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
return true; return true;
} }
pref->sizrng[0] = 0;
pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
return true;
}
const tree_code code = TREE_CODE (ptr); const tree_code code = TREE_CODE (ptr);
if (code == COMPONENT_REF) if (code == COMPONENT_REF)
{ {
tree ref = TREE_OPERAND (ptr, 0);
tree field = TREE_OPERAND (ptr, 1); tree field = TREE_OPERAND (ptr, 1);
if (ostype == 0) if (ostype == 0)
{ {
/* For raw memory functions like memcpy bail if the size /* For raw memory functions like memcpy bail if the size
of the enclosing object cannot be determined. */ of the enclosing object cannot be determined. */
tree ref = TREE_OPERAND (ptr, 0);
if (!compute_objsize (ref, ostype, pref, visited, rvals) if (!compute_objsize (ref, ostype, pref, visited, rvals)
|| !pref->ref) || !pref->ref)
return false; return false;
@ -4449,20 +4478,28 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
return false; return false;
pref->ref = field; pref->ref = field;
/* Only return constant sizes for now while callers depend
on it. INT0LEN is true for interior zero-length arrays. */ /* SAM is set for array members that might need special treatment. */
bool int0len = false; special_array_member sam;
tree size = component_ref_size (ptr, &int0len); tree size = component_ref_size (ptr, &sam);
if (int0len) if (sam == special_array_member::int_0)
{
pref->sizrng[0] = pref->sizrng[1] = 0; pref->sizrng[0] = pref->sizrng[1] = 0;
return true; else if (!pref->trail1special && sam == special_array_member::trail_1)
} pref->sizrng[0] = pref->sizrng[1] = 1;
else if (size && TREE_CODE (size) == INTEGER_CST)
if (!size || TREE_CODE (size) != INTEGER_CST)
return false;
pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size); pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
else
{
/* When the size of the member is unknown it's either a flexible
array member or a trailing special array member (either zero
length or one-element). Set the size to the maximum minus
the constant size of the type. */
pref->sizrng[0] = 0;
pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
if (tree recsize = TYPE_SIZE_UNIT (TREE_TYPE (ref)))
if (TREE_CODE (recsize) == INTEGER_CST)
pref->sizrng[1] -= wi::to_offset (recsize);
}
return true; return true;
} }
@ -4492,7 +4529,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
return false; return false;
offset_int orng[2]; offset_int orng[2];
tree off = TREE_OPERAND (ptr, 1); tree off = pref->eval (TREE_OPERAND (ptr, 1));
if (!get_range (off, NULL, SIGNED, orng, rvals)) if (!get_range (off, NULL, SIGNED, orng, rvals))
/* Fail unless the size of the object is zero. */ /* Fail unless the size of the object is zero. */
return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1]; return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1];
@ -4522,11 +4559,22 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
if (ostype && TREE_CODE (eltype) == ARRAY_TYPE) if (ostype && TREE_CODE (eltype) == ARRAY_TYPE)
{ {
/* Execpt for the permissive raw memory functions which /* Except for the permissive raw memory functions which use
use the size of the whole object determined above, the size of the whole object determined above, use the size
use the size of the referenced array. */ of the referenced array. Because the overall offset is from
pref->sizrng[0] = pref->offrng[0] + orng[0] + sz; the beginning of the complete array object add this overall
pref->sizrng[1] = pref->offrng[1] + orng[1] + sz; offset to the size of array. */
offset_int sizrng[2] =
{
pref->offrng[0] + orng[0] + sz,
pref->offrng[1] + orng[1] + sz
};
if (sizrng[1] < sizrng[0])
std::swap (sizrng[0], sizrng[1]);
if (sizrng[0] >= 0 && sizrng[0] <= pref->sizrng[0])
pref->sizrng[0] = sizrng[0];
if (sizrng[1] >= 0 && sizrng[1] <= pref->sizrng[1])
pref->sizrng[1] = sizrng[1];
} }
} }
@ -4535,6 +4583,28 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
return true; return true;
} }
else if (code == POINTER_PLUS_EXPR)
{
tree ref = TREE_OPERAND (ptr, 0);
if (!compute_objsize (ref, ostype, pref, visited, rvals))
return false;
offset_int orng[2];
tree off = pref->eval (TREE_OPERAND (ptr, 1));
if (!get_range (off, NULL, SIGNED, orng, rvals))
/* Fail unless the size of the object is zero. */
return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1];
pref->offrng[0] += orng[0];
pref->offrng[1] += orng[1];
return true;
}
else if (code == VIEW_CONVERT_EXPR)
{
ptr = TREE_OPERAND (ptr, 0);
return compute_objsize (ptr, ostype, pref, visited, rvals);
}
if (TREE_CODE (ptr) == SSA_NAME) if (TREE_CODE (ptr) == SSA_NAME)
{ {
@ -12504,3 +12574,14 @@ builtin_with_linkage_p (tree decl)
} }
return false; return false;
} }
/* Return true if OFFRNG is bounded to a subrange of offset values
valid for the largest possible object. */
bool
access_ref::offset_bounded () const
{
tree min = TYPE_MIN_VALUE (ptrdiff_type_node);
tree max = TYPE_MAX_VALUE (ptrdiff_type_node);
return wi::to_offset (min) <= offrng[0] && offrng[1] <= wi::to_offset (max);
}

View file

@ -173,6 +173,22 @@ struct access_ref
For string functions the size of the actual access is For string functions the size of the actual access is
further constrained by the length of the string. */ further constrained by the length of the string. */
offset_int bndrng[2]; offset_int bndrng[2];
/* Return true if OFFRNG is the constant zero. */
bool offset_zero () const
{
return offrng[0] == 0 && offrng[1] == 0;
}
/* Return true if OFFRNG is bounded to a subrange of possible offset
values. */
bool offset_bounded () const;
/* Used to fold integer expressions when called from front ends. */
tree (*eval)(tree);
/* Set if trailing one-element arrays should be treated as flexible
array members. */
bool trail1special;
}; };
/* Describes a pair of references used in an access by built-in /* Describes a pair of references used in an access by built-in

View file

@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see
#include "attribs.h" #include "attribs.h"
#include "asan.h" #include "asan.h"
#include "stor-layout.h" #include "stor-layout.h"
#include "builtins.h"
static bool begin_init_stmts (tree *, tree *); static bool begin_init_stmts (tree *, tree *);
static tree finish_init_stmts (bool, tree, tree); static tree finish_init_stmts (bool, tree, tree);
@ -2564,27 +2565,6 @@ throw_bad_array_new_length (void)
return build_cxx_call (fn, 0, NULL, tf_warning_or_error); return build_cxx_call (fn, 0, NULL, tf_warning_or_error);
} }
/* Attempt to find the initializer for flexible array field T in the
initializer INIT, when non-null. Returns the initializer when
successful and NULL otherwise. */
static tree
find_flexarray_init (tree t, tree init)
{
if (!init || init == error_mark_node)
return NULL_TREE;
unsigned HOST_WIDE_INT idx;
tree field, elt;
/* Iterate over all top-level initializer elements. */
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), idx, field, elt)
/* If the member T is found, return it. */
if (field == t)
return elt;
return NULL_TREE;
}
/* Attempt to verify that the argument, OPER, of a placement new expression /* Attempt to verify that the argument, OPER, of a placement new expression
refers to an object sufficiently large for an object of TYPE or an array refers to an object sufficiently large for an object of TYPE or an array
of NELTS of such objects when NELTS is non-null, and issue a warning when of NELTS of such objects when NELTS is non-null, and issue a warning when
@ -2601,17 +2581,6 @@ warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
{ {
location_t loc = cp_expr_loc_or_input_loc (oper); location_t loc = cp_expr_loc_or_input_loc (oper);
/* The number of bytes to add to or subtract from the size of the provided
buffer based on an offset into an array or an array element reference.
Although intermediate results may be negative (as in a[3] - 2) a valid
final result cannot be. */
offset_int adjust = 0;
/* True when the size of the entire destination object should be used
to compute the possibly optimistic estimate of the available space. */
bool use_obj_size = false;
/* True when the reference to the destination buffer is an ADDR_EXPR. */
bool addr_expr = false;
STRIP_NOPS (oper); STRIP_NOPS (oper);
/* Using a function argument or a (non-array) variable as an argument /* Using a function argument or a (non-array) variable as an argument
@ -2625,181 +2594,16 @@ warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
/* Evaluate any constant expressions. */ /* Evaluate any constant expressions. */
size = fold_non_dependent_expr (size); size = fold_non_dependent_expr (size);
/* Handle the common case of array + offset expression when the offset access_ref ref;
is a constant. */ ref.eval = [](tree x){ return fold_non_dependent_expr (x); };
if (TREE_CODE (oper) == POINTER_PLUS_EXPR) ref.trail1special = warn_placement_new < 2;
{ tree objsize = compute_objsize (oper, 1, &ref);
/* If the offset is compile-time constant, use it to compute a more if (!objsize)
accurate estimate of the size of the buffer. Since the operand
of POINTER_PLUS_EXPR is represented as an unsigned type, convert
it to signed first.
Otherwise, use the size of the entire array as an optimistic
estimate (this may lead to false negatives). */
tree adj = TREE_OPERAND (oper, 1);
adj = fold_for_warn (adj);
if (CONSTANT_CLASS_P (adj))
adjust += wi::to_offset (convert (ssizetype, adj));
else
use_obj_size = true;
oper = TREE_OPERAND (oper, 0);
STRIP_NOPS (oper);
}
if (TREE_CODE (oper) == TARGET_EXPR)
oper = TREE_OPERAND (oper, 1);
else if (TREE_CODE (oper) == ADDR_EXPR)
{
addr_expr = true;
oper = TREE_OPERAND (oper, 0);
}
STRIP_NOPS (oper);
if (TREE_CODE (oper) == ARRAY_REF
&& (addr_expr || TREE_CODE (TREE_TYPE (oper)) == ARRAY_TYPE))
{
/* Similar to the offset computed above, see if the array index
is a compile-time constant. If so, and unless the offset was
not a compile-time constant, use the index to determine the
size of the buffer. Otherwise, use the entire array as
an optimistic estimate of the size. */
const_tree adj = fold_non_dependent_expr (TREE_OPERAND (oper, 1));
if (!use_obj_size && CONSTANT_CLASS_P (adj))
adjust += wi::to_offset (adj);
else
{
use_obj_size = true;
adjust = 0;
}
oper = TREE_OPERAND (oper, 0);
}
/* Refers to the declared object that constains the subobject referenced
by OPER. When the object is initialized, makes it possible to determine
the actual size of a flexible array member used as the buffer passed
as OPER to placement new. */
tree var_decl = NULL_TREE;
/* True when operand is a COMPONENT_REF, to distinguish flexible array
members from arrays of unspecified size. */
bool compref = TREE_CODE (oper) == COMPONENT_REF;
/* For COMPONENT_REF (i.e., a struct member) the size of the entire
enclosing struct. Used to validate the adjustment (offset) into
an array at the end of a struct. */
offset_int compsize = 0;
/* Descend into a struct or union to find the member whose address
is being used as the argument. */
if (TREE_CODE (oper) == COMPONENT_REF)
{
tree comptype = TREE_TYPE (TREE_OPERAND (oper, 0));
compsize = wi::to_offset (TYPE_SIZE_UNIT (comptype));
tree op0 = oper;
while (TREE_CODE (op0 = TREE_OPERAND (op0, 0)) == COMPONENT_REF);
STRIP_ANY_LOCATION_WRAPPER (op0);
if (VAR_P (op0))
var_decl = op0;
oper = TREE_OPERAND (oper, 1);
}
STRIP_ANY_LOCATION_WRAPPER (oper);
tree opertype = TREE_TYPE (oper);
if ((addr_expr || !INDIRECT_TYPE_P (opertype))
&& (VAR_P (oper)
|| TREE_CODE (oper) == FIELD_DECL
|| TREE_CODE (oper) == PARM_DECL))
{
/* A possibly optimistic estimate of the number of bytes available
in the destination buffer. */
offset_int bytes_avail = 0;
/* True when the estimate above is in fact the exact size
of the destination buffer rather than an estimate. */
bool exact_size = true;
/* Treat members of unions and members of structs uniformly, even
though the size of a member of a union may be viewed as extending
to the end of the union itself (it is by __builtin_object_size). */
if ((VAR_P (oper) || use_obj_size)
&& DECL_SIZE_UNIT (oper)
&& tree_fits_uhwi_p (DECL_SIZE_UNIT (oper)))
{
/* Use the size of the entire array object when the expression
refers to a variable or its size depends on an expression
that's not a compile-time constant. */
bytes_avail = wi::to_offset (DECL_SIZE_UNIT (oper));
exact_size = !use_obj_size;
}
else if (tree opersize = TYPE_SIZE_UNIT (opertype))
{
/* Use the size of the type of the destination buffer object
as the optimistic estimate of the available space in it.
Use the maximum possible size for zero-size arrays and
flexible array members (except of initialized objects
thereof). */
if (TREE_CODE (opersize) == INTEGER_CST)
bytes_avail = wi::to_offset (opersize);
}
if (bytes_avail == 0)
{
if (var_decl)
{
/* Constructing into a buffer provided by the flexible array
member of a declared object (which is permitted as a G++
extension). If the array member has been initialized,
determine its size from the initializer. Otherwise,
the array size is zero. */
if (tree init = find_flexarray_init (oper,
DECL_INITIAL (var_decl)))
bytes_avail = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (init)));
}
else
bytes_avail = (wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node))
- compsize);
}
tree_code oper_code = TREE_CODE (opertype);
if (compref && oper_code == ARRAY_TYPE)
{
tree nelts = array_type_nelts_top (opertype);
tree nelts_cst = maybe_constant_value (nelts);
if (TREE_CODE (nelts_cst) == INTEGER_CST
&& integer_onep (nelts_cst)
&& !var_decl
&& warn_placement_new < 2)
return; return;
}
/* Reduce the size of the buffer by the adjustment computed above offset_int bytes_avail = wi::to_offset (objsize);
from the offset and/or the index into the array. */
if (bytes_avail < adjust || adjust < 0)
bytes_avail = 0;
else
{
tree elttype = (TREE_CODE (opertype) == ARRAY_TYPE
? TREE_TYPE (opertype) : opertype);
if (tree eltsize = TYPE_SIZE_UNIT (elttype))
{
bytes_avail -= adjust * wi::to_offset (eltsize);
if (bytes_avail < 0)
bytes_avail = 0;
}
}
/* The minimum amount of space needed for the allocation. This
is an optimistic estimate that makes it possible to detect
placement new invocation for some undersize buffers but not
others. */
offset_int bytes_need; offset_int bytes_need;
if (nelts)
nelts = fold_for_warn (nelts);
if (CONSTANT_CLASS_P (size)) if (CONSTANT_CLASS_P (size))
bytes_need = wi::to_offset (size); bytes_need = wi::to_offset (size);
else if (nelts && CONSTANT_CLASS_P (nelts)) else if (nelts && CONSTANT_CLASS_P (nelts))
@ -2813,43 +2617,73 @@ warn_placement_new_too_small (tree type, tree nelts, tree size, tree oper)
return; return;
} }
if (bytes_avail < bytes_need) if (bytes_avail >= bytes_need)
{ return;
/* True when the size to mention in the warning is exact as opposed
to "at least N". */
const bool exact_size = (ref.offrng[0] == ref.offrng[1]
|| ref.sizrng[1] - ref.offrng[0] == 0);
tree opertype = ref.ref ? TREE_TYPE (ref.ref) : TREE_TYPE (oper);
bool warned = false;
if (nelts)
nelts = fold_for_warn (nelts);
if (nelts) if (nelts)
if (CONSTANT_CLASS_P (nelts)) if (CONSTANT_CLASS_P (nelts))
warning_at (loc, OPT_Wplacement_new_, warned = warning_at (loc, OPT_Wplacement_new_,
exact_size ? (exact_size
"placement new constructing an object of type " ? G_("placement new constructing an object "
"%<%T [%wu]%> and size %qwu in a region of type %qT " "of type %<%T [%wu]%> and size %qwu "
"and size %qwi" "in a region of type %qT and size %qwi")
: "placement new constructing an object of type " : G_("placement new constructing an object "
"%<%T [%wu]%> and size %qwu in a region of type %qT " "of type %<%T [%wu]%> and size %qwu "
"and size at most %qwu", "in a region of type %qT and size "
type, tree_to_uhwi (nelts), bytes_need.to_uhwi (), "at most %qwu")),
type, tree_to_uhwi (nelts),
bytes_need.to_uhwi (),
opertype, bytes_avail.to_uhwi ()); opertype, bytes_avail.to_uhwi ());
else else
warning_at (loc, OPT_Wplacement_new_, warned = warning_at (loc, OPT_Wplacement_new_,
exact_size ? (exact_size
"placement new constructing an array of objects " ? G_("placement new constructing an array "
"of type %qT and size %qwu in a region of type %qT " "of objects of type %qT and size %qwu "
"and size %qwi" "in a region of type %qT and size %qwi")
: "placement new constructing an array of objects " : G_("placement new constructing an array "
"of type %qT and size %qwu in a region of type %qT " "of objects of type %qT and size %qwu "
"and size at most %qwu", "in a region of type %qT and size "
"at most %qwu")),
type, bytes_need.to_uhwi (), opertype, type, bytes_need.to_uhwi (), opertype,
bytes_avail.to_uhwi ()); bytes_avail.to_uhwi ());
else else
warning_at (loc, OPT_Wplacement_new_, warned = warning_at (loc, OPT_Wplacement_new_,
exact_size ? (exact_size
"placement new constructing an object of type %qT " ? G_("placement new constructing an object "
"and size %qwu in a region of type %qT and size %qwi" "of type %qT and size %qwu in a region "
: "placement new constructing an object of type %qT " "of type %qT and size %qwi")
"and size %qwu in a region of type %qT and size " : G_("placement new constructing an object "
"at most %qwu", "of type %qT "
"and size %qwu in a region of type %qT "
"and size at most %qwu")),
type, bytes_need.to_uhwi (), opertype, type, bytes_need.to_uhwi (), opertype,
bytes_avail.to_uhwi ()); bytes_avail.to_uhwi ());
}
} if (!warned || !ref.ref)
return;
if (ref.offrng[0] == 0 || !ref.offset_bounded ())
/* Avoid mentioning the offset when its lower bound is zero
or when it's impossibly large. */
inform (DECL_SOURCE_LOCATION (ref.ref),
"%qD declared here", ref.ref);
else if (ref.offrng[0] == ref.offrng[1])
inform (DECL_SOURCE_LOCATION (ref.ref),
"at offset %wi from %qD declared here",
ref.offrng[0].to_shwi (), ref.ref);
else
inform (DECL_SOURCE_LOCATION (ref.ref),
"at offset [%wi, %wi] from %qD declared here",
ref.offrng[0].to_shwi (), ref.offrng[1].to_shwi (), ref.ref);
} }
/* True if alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__. */ /* True if alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__. */

View file

@ -188,7 +188,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
tree decl = NULL_TREE; tree decl = NULL_TREE;
/* Set for accesses to interior zero-length arrays. */ /* Set for accesses to interior zero-length arrays. */
bool interior_zero_len = false; special_array_member sam{ };
tree up_bound_p1; tree up_bound_p1;
@ -220,7 +220,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
{ {
/* Try to determine the size of the trailing array from /* Try to determine the size of the trailing array from
its initializer (if it has one). */ its initializer (if it has one). */
if (tree refsize = component_ref_size (arg, &interior_zero_len)) if (tree refsize = component_ref_size (arg, &sam))
if (TREE_CODE (refsize) == INTEGER_CST) if (TREE_CODE (refsize) == INTEGER_CST)
maxbound = refsize; maxbound = refsize;
} }
@ -325,7 +325,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
"array subscript %E is below array bounds of %qT", "array subscript %E is below array bounds of %qT",
low_sub, artype); low_sub, artype);
if (!warned && interior_zero_len) if (!warned && sam == special_array_member::int_0)
warned = warning_at (location, OPT_Wzero_length_bounds, warned = warning_at (location, OPT_Wzero_length_bounds,
(TREE_CODE (low_sub) == INTEGER_CST (TREE_CODE (low_sub) == INTEGER_CST
? G_("array subscript %E is outside the bounds " ? G_("array subscript %E is outside the bounds "

View file

@ -29,7 +29,7 @@ test_dynamic_type (S *p)
// distinguish invalid cases from ones like it that might be valid. // distinguish invalid cases from ones like it that might be valid.
// If/when GIMPLE changes to make this possible this test can be // If/when GIMPLE changes to make this possible this test can be
// removed. // removed.
char *q = new (p->a) char [16]; char *q = new (p->a) char [16]; // { dg-warning "\\\[-Wplacement-new" }
init (q); init (q);

View file

@ -66,8 +66,9 @@ struct BA2 { int i; A2 a2; };
void fBx (BAx *pbx, BAx &rbx) void fBx (BAx *pbx, BAx &rbx)
{ {
BAx bax; BAx bax;
new (bax.ax.a) char; // { dg-warning "placement" } // The uninitialized flexible array takes up the bytes of padding.
new (bax.ax.a) Int16; // { dg-warning "placement" } new (bax.ax.a) char;
new (bax.ax.a) Int16;
new (bax.ax.a) Int32; // { dg-warning "placement" } new (bax.ax.a) Int32; // { dg-warning "placement" }
new (pbx->ax.a) char; new (pbx->ax.a) char;
@ -84,9 +85,12 @@ void fBx1 ()
{ {
static BAx bax1 = { 1, /* Ax = */ { 2, /* a[] = */ {} } }; static BAx bax1 = { 1, /* Ax = */ { 2, /* a[] = */ {} } };
new (bax1.ax.a) char; // { dg-warning "placement" } // The empty flexible array takes up the bytes of padding.
new (bax1.ax.a) char[2]; // { dg-warning "placement" } new (bax1.ax.a) char;
new (bax1.ax.a) Int16; // { dg-warning "placement" } new (bax1.ax.a) char[2];
new (bax1.ax.a) Int16;
new (bax1.ax.a) char[3];
new (bax1.ax.a) char[4]; // { dg-warning "placement" }
new (bax1.ax.a) Int32; // { dg-warning "placement" } new (bax1.ax.a) Int32; // { dg-warning "placement" }
} }

View file

@ -124,9 +124,13 @@ struct BA2 { int i; A2 a2; };
void fBx (BAx *pbx, BAx &rbx) void fBx (BAx *pbx, BAx &rbx)
{ {
BAx bax; BAx bax;
new (bax.ax.a) char; // { dg-warning "placement" } // The uninitialized flexible array takes up the bytes of padding.
new (bax.ax.a) Int16; // { dg-warning "placement" } new (bax.ax.a) char;
new (bax.ax.a) Int16;
new (bax.ax.a) char[3];
new (bax.ax.a) Int32; // { dg-warning "placement" } new (bax.ax.a) Int32; // { dg-warning "placement" }
new (bax.ax.a) char[4]; // { dg-warning "placement" }
new (bax.ax.a) char[5]; // { dg-warning "placement" }
new (pbx->ax.a) char; new (pbx->ax.a) char;
new (rbx.ax.a) char; new (rbx.ax.a) char;
@ -142,10 +146,14 @@ void fBx1 ()
{ {
static BAx bax1 = { 1, /* Ax = */ { 2, /* a[] = */ {} } }; static BAx bax1 = { 1, /* Ax = */ { 2, /* a[] = */ {} } };
new (bax1.ax.a) char; // { dg-warning "placement" } // The empty flexible array takes up the bytes of padding.
new (bax1.ax.a) char[2]; // { dg-warning "placement" } new (bax1.ax.a) char;
new (bax1.ax.a) Int16; // { dg-warning "placement" } new (bax1.ax.a) char[2];
new (bax1.ax.a) Int16;
new (bax1.ax.a) char[3];
new (bax1.ax.a) Int32; // { dg-warning "placement" } new (bax1.ax.a) Int32; // { dg-warning "placement" }
new (bax1.ax.a) char[4]; // { dg-warning "placement" }
new (bax1.ax.a) char[5]; // { dg-warning "placement" }
} }
void fB0 (BA0 *pb0, BA0 &rb0) void fB0 (BA0 *pb0, BA0 &rb0)

View file

@ -17,9 +17,10 @@ void fBx1 ()
{ {
static BAx bax1 = { 1, /* Ax = */ { 2, /* a[] = */ { 3 } } }; // { dg-error "initialization of flexible array member in a nested context" } static BAx bax1 = { 1, /* Ax = */ { 2, /* a[] = */ { 3 } } }; // { dg-error "initialization of flexible array member in a nested context" }
new (bax1.ax.a) char; // { dg-warning "placement" } // The first three bytes of the flexible array member live in the padding.
new (bax1.ax.a) char[2]; // { dg-warning "placement" } new (bax1.ax.a) char;
new (bax1.ax.a) Int16; // { dg-warning "placement" } new (bax1.ax.a) char[2];
new (bax1.ax.a) Int16;
new (bax1.ax.a) Int32; // { dg-warning "placement" } new (bax1.ax.a) Int32; // { dg-warning "placement" }
} }
@ -27,10 +28,11 @@ void fBx2 ()
{ {
static BAx bax2 = { 1, /* Ax = */ { 2, /* a[] = */ { 3, 4 } } }; // { dg-error "initialization of flexible array member in a nested context" } static BAx bax2 = { 1, /* Ax = */ { 2, /* a[] = */ { 3, 4 } } }; // { dg-error "initialization of flexible array member in a nested context" }
new (bax2.ax.a) char; // { dg-warning "placement" } // The first three bytes of the flexible array member live in the padding.
new (bax2.ax.a) char[2]; // { dg-warning "placement" } new (bax2.ax.a) char;
new (bax2.ax.a) char[3]; // { dg-warning "placement" } new (bax2.ax.a) char[2];
new (bax2.ax.a) Int16; // { dg-warning "placement" } new (bax2.ax.a) char[3];
new (bax2.ax.a) Int16;
new (bax2.ax.a) char[4]; // { dg-warning "placement" } new (bax2.ax.a) char[4]; // { dg-warning "placement" }
new (bax2.ax.a) Int32; // { dg-warning "placement" } new (bax2.ax.a) Int32; // { dg-warning "placement" }
} }
@ -39,10 +41,11 @@ void fBx3 ()
{ {
static BAx bax2 = { 1, /* Ax = */ { 3, /* a[] = */ { 4, 5, 6 } } }; // { dg-error "initialization of flexible array member in a nested context" } static BAx bax2 = { 1, /* Ax = */ { 3, /* a[] = */ { 4, 5, 6 } } }; // { dg-error "initialization of flexible array member in a nested context" }
new (bax2.ax.a) char; // { dg-warning "placement" } // The first three bytes of the flexible array member live in the padding.
new (bax2.ax.a) char[2]; // { dg-warning "placement" } new (bax2.ax.a) char;
new (bax2.ax.a) Int16; // { dg-warning "placement" } new (bax2.ax.a) char[2];
new (bax2.ax.a) char[3]; // { dg-warning "placement" } new (bax2.ax.a) Int16;
new (bax2.ax.a) char[3];
new (bax2.ax.a) char[4]; // { dg-warning "placement" } new (bax2.ax.a) char[4]; // { dg-warning "placement" }
new (bax2.ax.a) Int32; // { dg-warning "placement" } new (bax2.ax.a) Int32; // { dg-warning "placement" }
} }

View file

@ -0,0 +1,82 @@
/* PR c++/96511 - Incorrect -Wplacement-new on POINTER_PLUS into an array
with 4-byte elements
{ dg-do compile }
{ dg-options "-Wall" } */
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
typedef __SIZE_TYPE__ size_t;
void* operator new (size_t, void *p) { return p; }
void test_a1_int16 ()
{
int16_t a3[3]; // { dg-message "declared here" }
new (a3) int16_t;
new (a3 + 1) int16_t;
new (a3 + 2) int16_t; // { dg-bogus "\\\[-Wplacement-new" }
new (&a3[1]) int16_t;
new (&a3[0] + 1) int16_t;
new (&a3[0] + 2) int16_t; // { dg-bogus "\\\[-Wplacement-new" }
new (&a3[0] + 3) int16_t; // { dg-warning "\\\[-Wplacement-new" }
}
void test_a1_int32 ()
{
int16_t a3[3];
new (a3 + 1) int32_t; // { dg-bogus "\\\[-Wplacement-new" }
new (&a3[1]) int32_t;
new (&a3[0] + 1) int32_t; // { dg-bogus "\\\[-Wplacement-new" }
new (&a3[0] + 2) int32_t; // { dg-warning "\\\[-Wplacement-new" }
}
void test_a2 ()
{
int16_t a23[2][3];
new (a23 + 1) int16_t; // { dg-bogus "\\\[-Wplacement-new" }
new (&a23[1]) int16_t;
new (&a23[2]) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][0] + 1) int16_t;
new (&a23[0][0] + 2) int16_t;
// Deriving a pointer to the next array from one to an element of
// the prior array isn't valid even if the resulting pointer points
// to an element of the larger array. Verify it's diagnosed.
new (&a23[0][0] + 3) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][0] + 4) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][0] + 5) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][0] + 6) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][1] + 1) int16_t;
new (&a23[0][1] + 2) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][1] + 3) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][1] + 4) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][1] + 5) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][1] + 6) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][2] + 1) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][2] + 2) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][2] + 3) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][2] + 4) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][2] + 5) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[0][2] + 6) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[1][0]) int16_t;
new (&a23[1][0] + 1) int16_t;
new (&a23[1][0] + 2) int16_t;
new (&a23[1][0] + 3) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[1][0] + 4) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[1][1]) int16_t;
new (&a23[1][2]) int16_t;
new (&a23[1][2] + 1) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[1][3]) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[1][3] + 1) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[2][0]) int16_t; // { dg-warning "\\\[-Wplacement-new" }
new (&a23[2][0] + 1) int16_t; // { dg-warning "\\\[-Wplacement-new" }
}

View file

@ -1,5 +1,5 @@
/* { dg-do compile } /* { dg-do compile }
{ dg-options "-O2 -Wall" } */ { dg-options "-O2 -Wall -Wno-stringop-overread" } */
typedef __SIZE_TYPE__ size_t; typedef __SIZE_TYPE__ size_t;
@ -15,7 +15,7 @@ void fa0_extern (void)
{ {
sink (strlen (ea0.a - 2)); // { dg-warning "\\\[-Warray-bounds" } sink (strlen (ea0.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (ea0.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } } sink (strlen (ea0.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ea0.a)); // { dg-warning "\\\[-Wstringop-overread" "pr93514" } sink (strlen (ea0.a)); // valid just-past-the-end offset
sink (strlen (ea0.a + 1)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } sink (strlen (ea0.a + 1)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
} }
@ -25,7 +25,7 @@ void fa0_static (void)
{ {
sink (strlen (sa0.a - 2)); // { dg-warning "\\\[-Warray-bounds" } sink (strlen (sa0.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (sa0.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } } sink (strlen (sa0.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (sa0.a)); // { dg-warning "\\\[-Wstringop-overread" "pr93514" } sink (strlen (sa0.a)); // valid just-past-the-end offset
sink (strlen (sa0.a + 1)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } sink (strlen (sa0.a + 1)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
} }
@ -52,14 +52,14 @@ void fax_static (void)
sink (strlen (ax0.a - 2)); // { dg-warning "\\\[-Warray-bounds" } sink (strlen (ax0.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (ax0.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } } sink (strlen (ax0.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax0.a)); sink (strlen (ax0.a));
sink (strlen (ax0.a + 1)); // { dg-warning "\\\[-Wstringop-overread" "pr93514" } sink (strlen (ax0.a + 1)); // valid just-past-the-end offset
sink (strlen (ax0.a + 2)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } sink (strlen (ax0.a + 2)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
sink (strlen (ax1.a - 2)); // { dg-warning "\\\[-Warray-bounds" } sink (strlen (ax1.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (ax1.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } } sink (strlen (ax1.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax1.a)); sink (strlen (ax1.a));
sink (strlen (ax1.a + 1)); sink (strlen (ax1.a + 1));
sink (strlen (ax1.a + 2)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" "pr93514" } sink (strlen (ax1.a + 2)); // valid just-past-the-end offset
sink (strlen (ax1.a + 3)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } sink (strlen (ax1.a + 3)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
sink (strlen (ax2.a - 2)); // { dg-warning "\\\[-Warray-bounds" } sink (strlen (ax2.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
@ -67,7 +67,7 @@ void fax_static (void)
sink (strlen (ax2.a)); sink (strlen (ax2.a));
sink (strlen (ax2.a + 1)); sink (strlen (ax2.a + 1));
sink (strlen (ax2.a + 2)); sink (strlen (ax2.a + 2));
sink (strlen (ax2.a + 3)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" "pr93514" } sink (strlen (ax2.a + 3)); // valid just-past-the-end offset
sink (strlen (ax2.a + 4)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } sink (strlen (ax2.a + 4)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
sink (strlen (ax3.a - 2)); // { dg-warning "\\\[-Warray-bounds" } sink (strlen (ax3.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
@ -76,6 +76,6 @@ void fax_static (void)
sink (strlen (ax3.a + 1)); sink (strlen (ax3.a + 1));
sink (strlen (ax3.a + 2)); sink (strlen (ax3.a + 2));
sink (strlen (ax3.a + 3)); sink (strlen (ax3.a + 3));
sink (strlen (ax3.a + 4)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" "pr93514" } sink (strlen (ax3.a + 4)); // valid just-past-the-end offset
sink (strlen (ax3.a + 5)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" } sink (strlen (ax3.a + 5)); // { dg-warning "\\\[-Warray-bounds|-Wstringop-overread" }
} }

View file

@ -184,6 +184,18 @@ void test_note (const char *s)
sink (a); sink (a);
} }
{
char a[1][1][2]; // { dg-message "at offset 2 into " }
strncpy (a[0][1], s, 3); // { dg-warning "writing 3 bytes into a region of size 0 " }
sink (a);
}
{
char a[1][2][2]; // { dg-message "destination object" }
strncpy (a[0][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
sink (a);
}
{ {
char a[1][2][2]; // { dg-message "at offset 2 into " } char a[1][2][2]; // { dg-message "at offset 2 into " }
strncpy (a[0][1], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " } strncpy (a[0][1], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " }
@ -192,7 +204,13 @@ void test_note (const char *s)
{ {
char a[1][2][2]; // { dg-message "at offset 4 into " } char a[1][2][2]; // { dg-message "at offset 4 into " }
strncpy (a[1][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 2 " } strncpy (a[1][0], s, 3); // { dg-warning "writing 3 bytes into a region of size 0 " }
sink (a);
}
{
char a[2][1][2]; // { dg-message "at offset 2 into " }
strncpy (a[0][1], s, 3); // { dg-warning "writing 3 bytes into a region of size 0 " }
sink (a); sink (a);
} }

View file

@ -13640,20 +13640,21 @@ get_initializer_for (tree init, tree decl)
/* Determines the size of the member referenced by the COMPONENT_REF /* Determines the size of the member referenced by the COMPONENT_REF
REF, using its initializer expression if necessary in order to REF, using its initializer expression if necessary in order to
determine the size of an initialized flexible array member. determine the size of an initialized flexible array member.
If non-null, *INTERIOR_ZERO_LENGTH is set when REF refers to If non-null, set *ARK when REF refers to an interior zero-length
an interior zero-length array. array or a trailing one-element array.
Returns the size as sizetype (which might be zero for an object Returns the size as sizetype (which might be zero for an object
with an uninitialized flexible array member) or null if the size with an uninitialized flexible array member) or null if the size
cannot be determined. */ cannot be determined. */
tree tree
component_ref_size (tree ref, bool *interior_zero_length /* = NULL */) component_ref_size (tree ref, special_array_member *sam /* = NULL */)
{ {
gcc_assert (TREE_CODE (ref) == COMPONENT_REF); gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
bool int_0_len = false; special_array_member arkbuf;
if (!interior_zero_length) if (!sam)
interior_zero_length = &int_0_len; sam = &arkbuf;
*sam = special_array_member::none;
/* The object/argument referenced by the COMPONENT_REF and its type. */ /* The object/argument referenced by the COMPONENT_REF and its type. */
tree arg = TREE_OPERAND (ref, 0); tree arg = TREE_OPERAND (ref, 0);
@ -13675,9 +13676,16 @@ component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
more than one element. */ more than one element. */
return memsize; return memsize;
*interior_zero_length = zero_length && !trailing; if (zero_length)
if (*interior_zero_length) {
if (trailing)
*sam = special_array_member::trail_0;
else
{
*sam = special_array_member::int_0;
memsize = NULL_TREE; memsize = NULL_TREE;
}
}
if (!zero_length) if (!zero_length)
if (tree dom = TYPE_DOMAIN (memtype)) if (tree dom = TYPE_DOMAIN (memtype))
@ -13688,9 +13696,13 @@ component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
{ {
offset_int minidx = wi::to_offset (min); offset_int minidx = wi::to_offset (min);
offset_int maxidx = wi::to_offset (max); offset_int maxidx = wi::to_offset (max);
if (maxidx - minidx > 0) offset_int neltsm1 = maxidx - minidx;
if (neltsm1 > 0)
/* MEMBER is an array with more than one element. */ /* MEMBER is an array with more than one element. */
return memsize; return memsize;
if (neltsm1 == 0)
*sam = special_array_member::trail_1;
} }
/* For a refernce to a zero- or one-element array member of a union /* For a refernce to a zero- or one-element array member of a union
@ -13708,7 +13720,7 @@ component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
tree base = get_addr_base_and_unit_offset (ref, &baseoff); tree base = get_addr_base_and_unit_offset (ref, &baseoff);
if (!base || !VAR_P (base)) if (!base || !VAR_P (base))
{ {
if (!*interior_zero_length) if (*sam != special_array_member::int_0)
return NULL_TREE; return NULL_TREE;
if (TREE_CODE (arg) != COMPONENT_REF) if (TREE_CODE (arg) != COMPONENT_REF)
@ -13728,7 +13740,7 @@ component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
/* Determine the base type of the referenced object. If it's /* Determine the base type of the referenced object. If it's
the same as ARGTYPE and MEMBER has a known size, return it. */ the same as ARGTYPE and MEMBER has a known size, return it. */
tree bt = basetype; tree bt = basetype;
if (!*interior_zero_length) if (*sam != special_array_member::int_0)
while (TREE_CODE (bt) == ARRAY_TYPE) while (TREE_CODE (bt) == ARRAY_TYPE)
bt = TREE_TYPE (bt); bt = TREE_TYPE (bt);
bool typematch = useless_type_conversion_p (argtype, bt); bool typematch = useless_type_conversion_p (argtype, bt);
@ -13768,7 +13780,7 @@ component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
if (DECL_P (base) if (DECL_P (base)
&& DECL_EXTERNAL (base) && DECL_EXTERNAL (base)
&& bt == basetype && bt == basetype
&& !*interior_zero_length) && *sam != special_array_member::int_0)
/* The size of a flexible array member of an extern struct /* The size of a flexible array member of an extern struct
with no initializer cannot be determined (it's defined with no initializer cannot be determined (it's defined
in another translation unit and can have an initializer in another translation unit and can have an initializer

View file

@ -5292,12 +5292,22 @@ extern bool array_at_struct_end_p (tree);
by EXP. This does not include any offset in DECL_FIELD_BIT_OFFSET. */ by EXP. This does not include any offset in DECL_FIELD_BIT_OFFSET. */
extern tree component_ref_field_offset (tree); extern tree component_ref_field_offset (tree);
/* Describes a "special" array member due to which component_ref_size
returns null. */
enum struct special_array_member
{
none, /* Not a special array member. */
int_0, /* Interior array member with size zero. */
trail_0, /* Trailing array member with size zero. */
trail_1 /* Trailing array member with one element. */
};
/* Return the size of the member referenced by the COMPONENT_REF, using /* Return the size of the member referenced by the COMPONENT_REF, using
its initializer expression if necessary in order to determine the size its initializer expression if necessary in order to determine the size
of an initialized flexible array member. The size might be zero for of an initialized flexible array member. The size might be zero for
an object with an uninitialized flexible array member or null if it an object with an uninitialized flexible array member or null if it
cannot be determined. */ cannot be determined. */
extern tree component_ref_size (tree, bool * = NULL); extern tree component_ref_size (tree, special_array_member * = NULL);
extern int tree_map_base_eq (const void *, const void *); extern int tree_map_base_eq (const void *, const void *);
extern unsigned int tree_map_base_hash (const void *); extern unsigned int tree_map_base_hash (const void *);