Skip to content

Commit bba8af0

Browse files
committed
Fix _Generic selection for pointer-to-array types
Use type compatibility matching instead of exact equality in _Generic. Also fix array type compatibility check to follow C11 6.2.7 standard: Arrays with unspecified size are compatible with specified sizes. Fixes: #8690 Fixes: #8243
1 parent 4fe3ade commit bba8af0

File tree

5 files changed

+117
-5
lines changed

5 files changed

+117
-5
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Test case for _Generic with array typedef
2+
// This should reproduce the issue from GitHub issue #8243
3+
4+
typedef struct m_string_s
5+
{
6+
int s, a;
7+
char *ptr;
8+
} m_string_t[1];
9+
10+
#define TEST_GENERIC(x) \
11+
_Generic((x), struct m_string_s * : 42, int : 1, float : 2, default : 99)
12+
13+
int main(void)
14+
{
15+
m_string_t s; // Array type that should decay to pointer
16+
int i = 5;
17+
float f = 3.14f;
18+
char c = 'x';
19+
20+
// Test that array typedef matches pointer type due to array-to-pointer decay
21+
assert(TEST_GENERIC(s) == 42); // Should match struct m_string_s *
22+
23+
// Test that other types work as expected
24+
assert(TEST_GENERIC(i) == 1); // Should match int
25+
assert(TEST_GENERIC(f) == 2); // Should match float
26+
assert(TEST_GENERIC(c) == 99); // Should match default (char)
27+
28+
return 0;
29+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
^warning: ignoring
8+
--
9+
_Generic with array typedef should match pointer type due to array decay
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
4+
int main(void)
5+
{
6+
// Test case from issue #8690
7+
// A generic selection on a value of type int(*)[5] should match an arm for int(*)[]
8+
int arr[5];
9+
assert(_Generic(&arr, int(*)[] : true));
10+
11+
// Additional test cases to ensure compatibility matching works correctly
12+
13+
// Test with different array sizes - should all match int(*)[]
14+
int arr2[10];
15+
assert(_Generic(&arr2, int(*)[] : true));
16+
17+
int arr3[1];
18+
assert(_Generic(&arr3, int(*)[] : true));
19+
20+
// Test with character arrays
21+
char carr[20];
22+
assert(_Generic(&carr, char(*)[] : true, default : false));
23+
24+
// Test with default case as fallback
25+
int *ptr;
26+
int result = _Generic(ptr, int(*)[] : 1, default : 2);
27+
assert(result == 2);
28+
29+
// Test pointer to array of specific size should also work
30+
int(*ptr_to_arr)[5] = &arr;
31+
assert(_Generic(ptr_to_arr, int(*)[] : true));
32+
33+
return 0;
34+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
^warning: ignoring
8+
^CONVERSION ERROR$
9+
--
10+
Test _Generic selection with pointer-to-array types (issue #8690).
11+
Pointer to array with specified size should match pointer to array with unspecified size.

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,23 @@ bool c_typecheck_baset::gcc_types_compatible_p(
130130
else if(type1.id()==ID_array &&
131131
type2.id()==ID_array)
132132
{
133-
return gcc_types_compatible_p(
134-
to_array_type(type1).element_type(),
135-
to_array_type(type2).element_type()); // ignore size
133+
// For C11 6.2.7: array types are compatible if element types are
134+
// compatible and both size specifiers are present and equal, OR
135+
// one or both size specifiers are absent.
136+
if(!gcc_types_compatible_p(
137+
to_array_type(type1).element_type(),
138+
to_array_type(type2).element_type()))
139+
return false;
140+
141+
const array_typet &a_type1 = to_array_type(type1);
142+
const array_typet &a_type2 = to_array_type(type2);
143+
144+
// If either size is absent (incomplete), arrays are compatible
145+
if(!a_type1.is_complete() || !a_type2.is_complete())
146+
return true;
147+
148+
// Both sizes are present - they must be equal
149+
return a_type1.size() == a_type2.size();
136150
}
137151
else if(type1.id()==ID_code &&
138152
type2.id()==ID_code)
@@ -156,6 +170,12 @@ bool c_typecheck_baset::gcc_types_compatible_p(
156170

157171
return true;
158172
}
173+
else if(
174+
type1.id() == ID_array &&
175+
type2 == pointer_type(to_array_type(type1).element_type()))
176+
{
177+
return true;
178+
}
159179
else
160180
{
161181
if(type1==type2)
@@ -463,9 +483,18 @@ void c_typecheck_baset::typecheck_expr_main(exprt &expr)
463483
{
464484
if(irep.get(ID_type_arg) == ID_default)
465485
default_match = static_cast<const exprt &>(irep.find(ID_value));
466-
else if(op_type == static_cast<const typet &>(irep.find(ID_type_arg)))
486+
else
467487
{
468-
assoc_match = static_cast<const exprt &>(irep.find(ID_value));
488+
const typet &assoc_type =
489+
static_cast<const typet &>(irep.find(ID_type_arg));
490+
// Use type compatibility matching instead of exact equality.
491+
// This allows pointer-to-array types with specified dimensions
492+
// to match pointer-to-array types with unspecified dimensions,
493+
// which is required by C11 6.5.1.1 Generic selection.
494+
if(gcc_types_compatible_p(op_type, assoc_type))
495+
{
496+
assoc_match = static_cast<const exprt &>(irep.find(ID_value));
497+
}
469498
}
470499
}
471500

0 commit comments

Comments
 (0)