PR middle-end/105604 - ICE: in tree_to_shwi with vla in struct and sprintf
gcc/ChangeLog:
PR middle-end/105604
* gimple-ssa-sprintf.cc (set_aggregate_size_and_offset): Add comments.
(get_origin_and_offset_r): Remove null handling. Handle variable array
sizes.
(get_origin_and_offset): Handle null argument here. Simplify.
(alias_offset): Update comment.
* pointer-query.cc (field_at_offset): Update comment. Handle members
of variable-length types.
gcc/testsuite/ChangeLog:
PR middle-end/105604
* gcc.dg/Wrestrict-24.c: New test.
* gcc.dg/Wrestrict-25.c: New test.
* gcc.dg/Wrestrict-26.c: New test.
Co-authored-by: Richard Biener <rguenther@suse.de>
(cherry picked from commit 10d1986aee
)
This commit is contained in:
parent
e919fae01b
commit
740771a620
|
@ -2232,8 +2232,9 @@ format_character (const directive &dir, tree arg, pointer_query &ptr_qry)
|
|||
}
|
||||
|
||||
/* If TYPE is an array or struct or union, increment *FLDOFF by the starting
|
||||
offset of the member that *OFF point into and set *FLDSIZE to its size
|
||||
in bytes and decrement *OFF by the same. Otherwise do nothing. */
|
||||
offset of the member that *OFF points into if one can be determined and
|
||||
set *FLDSIZE to its size in bytes and decrement *OFF by the same.
|
||||
Otherwise do nothing. */
|
||||
|
||||
static void
|
||||
set_aggregate_size_and_offset (tree type, HOST_WIDE_INT *fldoff,
|
||||
|
@ -2249,9 +2250,9 @@ set_aggregate_size_and_offset (tree type, HOST_WIDE_INT *fldoff,
|
|||
if (array_elt_at_offset (type, *off, &index, &arrsize))
|
||||
{
|
||||
*fldoff += index;
|
||||
*off -= index;
|
||||
*fldsize = arrsize;
|
||||
}
|
||||
/* Otherwise leave *FLDOFF et al. unchanged. */
|
||||
}
|
||||
else if (RECORD_OR_UNION_TYPE_P (type))
|
||||
{
|
||||
|
@ -2269,11 +2270,12 @@ set_aggregate_size_and_offset (tree type, HOST_WIDE_INT *fldoff,
|
|||
*fldoff += index;
|
||||
*off -= index;
|
||||
}
|
||||
/* Otherwise leave *FLDOFF et al. unchanged. */
|
||||
}
|
||||
}
|
||||
|
||||
/* For an expression X of pointer type, recursively try to find the same
|
||||
origin (object or pointer) as Y it references and return such a Y.
|
||||
/* For an expression X of pointer type, recursively try to find its origin
|
||||
(either object DECL or pointer such as PARM_DECL) Y and return such a Y.
|
||||
When X refers to an array element or struct member, set *FLDOFF to
|
||||
the offset of the element or member from the beginning of the "most
|
||||
derived" object and *FLDSIZE to its size. When nonnull, set *OFF to
|
||||
|
@ -2284,9 +2286,6 @@ static tree
|
|||
get_origin_and_offset_r (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *fldsize,
|
||||
HOST_WIDE_INT *off)
|
||||
{
|
||||
if (!x)
|
||||
return NULL_TREE;
|
||||
|
||||
HOST_WIDE_INT sizebuf = -1;
|
||||
if (!fldsize)
|
||||
fldsize = &sizebuf;
|
||||
|
@ -2308,23 +2307,33 @@ get_origin_and_offset_r (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *fldsize,
|
|||
|
||||
case ARRAY_REF:
|
||||
{
|
||||
tree offset = TREE_OPERAND (x, 1);
|
||||
HOST_WIDE_INT idx = (tree_fits_uhwi_p (offset)
|
||||
? tree_to_uhwi (offset) : HOST_WIDE_INT_MAX);
|
||||
tree sub = TREE_OPERAND (x, 1);
|
||||
unsigned HOST_WIDE_INT idx =
|
||||
tree_fits_uhwi_p (sub) ? tree_to_uhwi (sub) : HOST_WIDE_INT_MAX;
|
||||
|
||||
tree eltype = TREE_TYPE (x);
|
||||
if (TREE_CODE (eltype) == INTEGER_TYPE)
|
||||
tree elsz = array_ref_element_size (x);
|
||||
unsigned HOST_WIDE_INT elbytes =
|
||||
tree_fits_shwi_p (elsz) ? tree_to_shwi (elsz) : HOST_WIDE_INT_MAX;
|
||||
|
||||
unsigned HOST_WIDE_INT byteoff = idx * elbytes;
|
||||
|
||||
if (byteoff < HOST_WIDE_INT_MAX
|
||||
&& elbytes < HOST_WIDE_INT_MAX
|
||||
&& byteoff / elbytes == idx)
|
||||
{
|
||||
/* For in-bounds constant offsets into constant-sized arrays
|
||||
bump up *OFF, and for what's likely arrays or structs of
|
||||
arrays, also *FLDOFF, as necessary. */
|
||||
if (off)
|
||||
*off = idx;
|
||||
*off += byteoff;
|
||||
if (elbytes > 1)
|
||||
*fldoff += byteoff;
|
||||
}
|
||||
else if (idx < HOST_WIDE_INT_MAX)
|
||||
*fldoff += idx * int_size_in_bytes (eltype);
|
||||
else
|
||||
*fldoff = idx;
|
||||
*fldoff = HOST_WIDE_INT_MAX;
|
||||
|
||||
x = TREE_OPERAND (x, 0);
|
||||
return get_origin_and_offset_r (x, fldoff, fldsize, nullptr);
|
||||
return get_origin_and_offset_r (x, fldoff, fldsize, off);
|
||||
}
|
||||
|
||||
case MEM_REF:
|
||||
|
@ -2350,8 +2359,14 @@ get_origin_and_offset_r (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *fldsize,
|
|||
|
||||
case COMPONENT_REF:
|
||||
{
|
||||
tree foff = component_ref_field_offset (x);
|
||||
tree fld = TREE_OPERAND (x, 1);
|
||||
*fldoff += int_byte_position (fld);
|
||||
if (!tree_fits_shwi_p (foff)
|
||||
|| !tree_fits_shwi_p (DECL_FIELD_BIT_OFFSET (fld)))
|
||||
return x;
|
||||
*fldoff += (tree_to_shwi (foff)
|
||||
+ (tree_to_shwi (DECL_FIELD_BIT_OFFSET (fld))
|
||||
/ BITS_PER_UNIT));
|
||||
|
||||
get_origin_and_offset_r (fld, fldoff, fldsize, off);
|
||||
x = TREE_OPERAND (x, 0);
|
||||
|
@ -2411,30 +2426,25 @@ get_origin_and_offset_r (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *fldsize,
|
|||
return x;
|
||||
}
|
||||
|
||||
/* Nonrecursive version of the above. */
|
||||
/* Nonrecursive version of the above.
|
||||
The function never returns null unless X is null to begin with. */
|
||||
|
||||
static tree
|
||||
get_origin_and_offset (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *off,
|
||||
HOST_WIDE_INT *fldsize = nullptr)
|
||||
{
|
||||
if (!x)
|
||||
return NULL_TREE;
|
||||
|
||||
HOST_WIDE_INT sizebuf;
|
||||
if (!fldsize)
|
||||
fldsize = &sizebuf;
|
||||
|
||||
/* Invalidate *FLDSIZE. */
|
||||
*fldsize = -1;
|
||||
*fldoff = *off = 0;
|
||||
|
||||
*fldoff = *off = *fldsize = 0;
|
||||
tree orig = get_origin_and_offset_r (x, fldoff, fldsize, off);
|
||||
if (!orig)
|
||||
return NULL_TREE;
|
||||
|
||||
if (!*fldoff && *off == *fldsize)
|
||||
{
|
||||
*fldoff = *off;
|
||||
*off = 0;
|
||||
}
|
||||
|
||||
return orig;
|
||||
return get_origin_and_offset_r (x, fldoff, fldsize, off);
|
||||
}
|
||||
|
||||
/* If ARG refers to the same (sub)object or array element as described
|
||||
|
@ -2454,7 +2464,8 @@ alias_offset (tree arg, HOST_WIDE_INT *arg_size,
|
|||
return HOST_WIDE_INT_MIN;
|
||||
|
||||
/* The two arguments may refer to the same object. If they both refer
|
||||
to a struct member, see if the members are one and the same. */
|
||||
to a struct member, see if the members are one and the same. If so,
|
||||
return the offset into the member. */
|
||||
HOST_WIDE_INT arg_off = 0, arg_fld = 0;
|
||||
|
||||
tree arg_orig = get_origin_and_offset (arg, &arg_fld, &arg_off, arg_size);
|
||||
|
|
|
@ -2447,9 +2447,13 @@ field_at_offset (tree type, tree start_after, HOST_WIDE_INT off,
|
|||
/* The offset of FLD within its immediately enclosing structure. */
|
||||
HOST_WIDE_INT fldpos = next_pos < 0 ? int_byte_position (fld) : next_pos;
|
||||
|
||||
tree typesize = TYPE_SIZE_UNIT (fldtype);
|
||||
if (typesize && TREE_CODE (typesize) != INTEGER_CST)
|
||||
/* Bail if FLD is a variable length member. */
|
||||
return NULL_TREE;
|
||||
|
||||
/* If the size is not available the field is a flexible array
|
||||
member. Treat this case as success. */
|
||||
tree typesize = TYPE_SIZE_UNIT (fldtype);
|
||||
HOST_WIDE_INT fldsize = (tree_fits_uhwi_p (typesize)
|
||||
? tree_to_uhwi (typesize)
|
||||
: off);
|
||||
|
@ -2463,7 +2467,11 @@ field_at_offset (tree type, tree start_after, HOST_WIDE_INT off,
|
|||
{
|
||||
/* If OFF is equal to the offset of the next field continue
|
||||
to it and skip the array/struct business below. */
|
||||
next_pos = int_byte_position (next_fld);
|
||||
tree pos = byte_position (next_fld);
|
||||
if (!tree_fits_shwi_p (pos))
|
||||
/* Bail if NEXT_FLD is a variable length member. */
|
||||
return NULL_TREE;
|
||||
next_pos = tree_to_shwi (pos);
|
||||
*nextoff = *fldoff + next_pos;
|
||||
if (*nextoff == off && TREE_CODE (type) != UNION_TYPE)
|
||||
continue;
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/* PR tree-optimization/105604 - ICE: in tree_to_shwi with vla in struct
|
||||
and sprintf
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wrestrict" } */
|
||||
|
||||
extern int sprintf (char*, const char*, ...);
|
||||
|
||||
extern void* sink (void*, ...);
|
||||
|
||||
struct {
|
||||
long users;
|
||||
long size;
|
||||
char *data;
|
||||
} * main_trans;
|
||||
|
||||
void *main___trans_tmp_1;
|
||||
|
||||
int users = 0;
|
||||
|
||||
void test (void)
|
||||
{
|
||||
struct {
|
||||
long users;
|
||||
long size;
|
||||
char *data;
|
||||
int links[users];
|
||||
char buf[];
|
||||
} *trans = sink (0);
|
||||
|
||||
trans->data = trans->buf;
|
||||
main___trans_tmp_1 = trans;
|
||||
main_trans = main___trans_tmp_1;
|
||||
sprintf (main_trans->data, "test");
|
||||
sink (main_trans->data);
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/* PR tree-optimization/105604 - ICE: in tree_to_shwi with vla in struct
|
||||
and sprintf
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wrestrict" } */
|
||||
|
||||
extern int sprintf (char*, const char*, ...);
|
||||
|
||||
void* sink (void*);
|
||||
|
||||
|
||||
void sprintf_S_a8_an_bn (int n, int i, int j)
|
||||
{
|
||||
struct {
|
||||
char a8[8], an[n], bn[n];
|
||||
} *p = sink (0);
|
||||
|
||||
{
|
||||
char *d = p->a8 + i;
|
||||
char *s = p->a8;
|
||||
sprintf (d, "%s", s); // { dg-warning "argument 3 may overlap" }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->a8;
|
||||
char *s = p->a8 + j;
|
||||
sprintf (d, "%s", s); // { dg-warning "argument 3 may overlap" }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->a8 + i;
|
||||
char *s = p->a8 + j;
|
||||
sprintf (d, "%s", s); // { dg-warning "argument 3 may overlap" }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->a8 + i;
|
||||
char *s = p->an;
|
||||
sprintf (d, "%s", s);
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->a8;
|
||||
char *s = p->an + j;
|
||||
sprintf (d, "%s", s);
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->a8 + i;
|
||||
char *s = p->an + j;
|
||||
sprintf (d, "%s", s);
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
/* The IL makes it impossible to rule out an overlap between
|
||||
p->a8 + i and p->bn + i so the "may overlap" warning triggers. */
|
||||
char *d = p->a8 + i;
|
||||
char *s = p->bn;
|
||||
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->a8;
|
||||
char *s = p->bn + j;
|
||||
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->a8 + i;
|
||||
char *s = p->bn + j;
|
||||
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->an + i;
|
||||
char *s = p->bn;
|
||||
sprintf (d, "%s", s);
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->an;
|
||||
char *s = p->bn + j;
|
||||
sprintf (d, "%s", s);
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->an + i;
|
||||
char *s = p->bn + j;
|
||||
sprintf (d, "%s", s);
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->an + i;
|
||||
char *s = p->a8;
|
||||
sprintf (d, "%s", s);
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->an;
|
||||
char *s = p->a8 + j;
|
||||
sprintf (d, "%s", s);
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->an + i;
|
||||
char *s = p->a8 + j;
|
||||
sprintf (d, "%s", s);
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->bn + i;
|
||||
char *s = p->a8;
|
||||
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->bn;
|
||||
char *s = p->a8 + j;
|
||||
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->bn + i;
|
||||
char *s = p->a8 + j;
|
||||
sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" "pr??????" { xfail *-*-* } }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->bn + i;
|
||||
char *s = p->bn;
|
||||
sprintf (d, "%s", s); // { dg-warning "may overlap" }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->bn;
|
||||
char *s = p->bn + j;
|
||||
sprintf (d, "%s", s); // { dg-warning "may overlap" }
|
||||
sink (p);
|
||||
}
|
||||
|
||||
{
|
||||
char *d = p->bn + i;
|
||||
char *s = p->bn + j;
|
||||
sprintf (d, "%s", s); // { dg-warning "may overlap" }
|
||||
sink (p);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/* Verify that sprintf calls with arrays or struct of arrays don't
|
||||
cause -Wrestrict false positives.
|
||||
{ dg-do compile }
|
||||
{ dg-options "-O2 -Wall -Wrestrict -ftrack-macro-expansion=0" } */
|
||||
|
||||
#define sprintf(d, f, ...) (sprintf (d, f, __VA_ARGS__), sink (d))
|
||||
|
||||
extern void sink (void*, ...);
|
||||
extern int (sprintf) (char*, const char*, ...);
|
||||
|
||||
extern char ca[][2][8];
|
||||
|
||||
void test_array_of_arrays (void)
|
||||
{
|
||||
sprintf (ca[0][0], "%s", ca[0][0]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (ca[0][0], "%s", ca[0][1]);
|
||||
sprintf (ca[0][0], "%s", ca[1][0]);
|
||||
sprintf (ca[0][0], "%s", ca[1][1]);
|
||||
|
||||
sprintf (ca[0][1], "%s", ca[0][0]);
|
||||
sprintf (ca[0][1], "%s", ca[0][1]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (ca[0][1], "%s", ca[1][0]);
|
||||
sprintf (ca[0][1], "%s", ca[1][1]);
|
||||
|
||||
sprintf (ca[1][0], "%s", ca[0][0]);
|
||||
sprintf (ca[1][0], "%s", ca[0][1]);
|
||||
sprintf (ca[1][0], "%s", ca[1][0]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (ca[1][0], "%s", ca[1][1]);
|
||||
|
||||
sprintf (ca[1][1], "%s", ca[0][0]);
|
||||
sprintf (ca[1][1], "%s", ca[0][1]);
|
||||
sprintf (ca[1][1], "%s", ca[1][0]);
|
||||
sprintf (ca[1][1], "%s", ca[1][1]); // { dg-warning "-Wrestrict" }
|
||||
}
|
||||
|
||||
|
||||
struct A
|
||||
{
|
||||
char a[2][2][8];
|
||||
char b[2][2][8];
|
||||
char c[2][2][8];
|
||||
};
|
||||
|
||||
extern struct A aa[][2];
|
||||
|
||||
void test_array_of_structs (void)
|
||||
{
|
||||
// Verify that calls with the same elements of the same array trigger
|
||||
// warnings as expected.
|
||||
sprintf (aa[0][0].a[0][0], "%s", aa[0][0].a[0][0]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[0][0].a[0][1], "%s", aa[0][0].a[0][1]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[0][0].a[1][0], "%s", aa[0][0].a[1][0]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[0][0].a[1][1], "%s", aa[0][0].a[1][1]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[0][1].a[0][0], "%s", aa[0][1].a[0][0]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[0][1].a[0][1], "%s", aa[0][1].a[0][1]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[0][1].a[1][0], "%s", aa[0][1].a[1][0]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[0][1].a[1][1], "%s", aa[0][1].a[1][1]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[1][0].a[0][0], "%s", aa[1][0].a[0][0]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[1][0].a[0][1], "%s", aa[1][0].a[0][1]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[1][0].a[1][0], "%s", aa[1][0].a[1][0]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[1][0].a[1][1], "%s", aa[1][0].a[1][1]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[1][1].a[0][0], "%s", aa[1][1].a[0][0]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[1][1].a[0][1], "%s", aa[1][1].a[0][1]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[1][1].a[1][0], "%s", aa[1][1].a[1][0]); // { dg-warning "-Wrestrict" }
|
||||
sprintf (aa[1][1].a[1][1], "%s", aa[1][1].a[1][1]); // { dg-warning "-Wrestrict" }
|
||||
|
||||
#define NOWARN()
|
||||
|
||||
// Exhaustively verify that calls with different elements of the same
|
||||
// array don't cause false positives.
|
||||
#undef NOWARN
|
||||
#define NOWARN(D, S) \
|
||||
sprintf (D[0][0], "%s", S[0][0]); \
|
||||
sprintf (D[0][0], "%s", S[0][1]); \
|
||||
sprintf (D[0][0], "%s", S[1][0]); \
|
||||
sprintf (D[0][0], "%s", S[1][1]); \
|
||||
sprintf (D[0][1], "%s", S[0][0]); \
|
||||
sprintf (D[0][1], "%s", S[0][1]); \
|
||||
sprintf (D[0][1], "%s", S[1][0]); \
|
||||
sprintf (D[0][1], "%s", S[1][1]); \
|
||||
sprintf (D[1][0], "%s", S[0][0]); \
|
||||
sprintf (D[1][0], "%s", S[0][1]); \
|
||||
sprintf (D[1][0], "%s", S[1][0]); \
|
||||
sprintf (D[1][0], "%s", S[1][1]); \
|
||||
sprintf (D[1][1], "%s", S[0][0]); \
|
||||
sprintf (D[1][1], "%s", S[0][1]); \
|
||||
sprintf (D[1][1], "%s", S[1][0]); \
|
||||
sprintf (D[1][1], "%s", S[1][1])
|
||||
|
||||
NOWARN (aa[0][0].a, aa[0][1].a);
|
||||
NOWARN (aa[0][0].a, aa[1][0].a);
|
||||
NOWARN (aa[0][0].a, aa[1][1].a);
|
||||
|
||||
NOWARN (aa[0][1].a, aa[0][0].a);
|
||||
NOWARN (aa[0][1].a, aa[1][0].a);
|
||||
NOWARN (aa[0][1].a, aa[1][1].a);
|
||||
|
||||
NOWARN (aa[1][0].a, aa[0][0].a);
|
||||
NOWARN (aa[1][0].a, aa[0][1].a);
|
||||
NOWARN (aa[1][0].a, aa[1][1].a);
|
||||
|
||||
#define NOWARN_MEM(M1, M2) \
|
||||
NOWARN (aa[0][0].M1, aa[0][0].M2); \
|
||||
NOWARN (aa[0][0].M1, aa[0][1].M2); \
|
||||
NOWARN (aa[0][0].M1, aa[1][0].M2); \
|
||||
NOWARN (aa[0][0].M1, aa[1][1].M2)
|
||||
|
||||
NOWARN_MEM (a, b);
|
||||
NOWARN_MEM (a, c);
|
||||
NOWARN_MEM (b, a);
|
||||
NOWARN_MEM (b, c);
|
||||
NOWARN_MEM (c, a);
|
||||
NOWARN_MEM (c, b);
|
||||
}
|
Loading…
Reference in New Issue