Skip to content

Commit c9abf5c

Browse files
authored
[ClangImporter] Fix handling of bitfields in unions (#14412) (#14427)
Previously this caused an assertion failure in +Asserts builds and incorrect behavior in -Asserts builds. rdar://problem/37242238 (cherry picked from commit c06cc01)
1 parent 4eaabd1 commit c9abf5c

File tree

3 files changed

+125
-1
lines changed

3 files changed

+125
-1
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3087,11 +3087,13 @@ namespace {
30873087

30883088
// Bitfields are imported as computed properties with Clang-generated
30893089
// accessors.
3090+
bool isBitField = false;
30903091
if (auto field = dyn_cast<clang::FieldDecl>(nd)) {
30913092
if (field->isBitField()) {
30923093
// We can't represent this struct completely in SIL anymore,
30933094
// but we're still able to define a memberwise initializer.
30943095
hasUnreferenceableStorage = true;
3096+
isBitField = true;
30953097

30963098
makeBitFieldAccessors(Impl,
30973099
const_cast<clang::RecordDecl *>(decl),
@@ -3105,7 +3107,7 @@ namespace {
31053107
// Indirect fields are created as computed property accessible the
31063108
// fields on the anonymous field from which they are injected.
31073109
makeIndirectFieldAccessors(Impl, ind, members, result, VD);
3108-
} else if (decl->isUnion()) {
3110+
} else if (decl->isUnion() && !isBitField) {
31093111
// Union fields should only be available indirectly via a computed
31103112
// property. Since the union is made of all of the fields at once,
31113113
// this is a trivial accessor that casts self to the correct
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include <stdint.h>
2+
#include <string.h>
3+
4+
union PlainUnion {
5+
uint32_t whole;
6+
unsigned char first;
7+
};
8+
9+
struct PlainBitfield {
10+
uint32_t offset;
11+
uint32_t first: 8;
12+
uint32_t : 0;
13+
};
14+
_Static_assert(sizeof(struct PlainBitfield) == sizeof(uint64_t),
15+
"must fit in 64 bits");
16+
17+
struct PlainIndirect {
18+
uint32_t offset;
19+
struct {
20+
uint32_t whole;
21+
};
22+
};
23+
24+
union BitfieldUnion {
25+
uint32_t whole;
26+
uint32_t first: 8;
27+
};
28+
29+
struct BitfieldIndirect {
30+
uint32_t offset;
31+
struct {
32+
uint32_t first: 8;
33+
uint32_t : 0;
34+
};
35+
};
36+
37+
struct UnionIndirect {
38+
uint32_t offset;
39+
union {
40+
uint32_t whole;
41+
unsigned char first;
42+
};
43+
};
44+
45+
struct BitfieldUnionIndirect {
46+
uint32_t offset;
47+
union {
48+
uint32_t whole;
49+
uint32_t first: 8;
50+
};
51+
};
52+
53+
void populate(void *memory) {
54+
const uint32_t value = 0x11223344;
55+
memcpy(memory, &value, sizeof(value));
56+
}
57+
58+
void populateAtOffset(void *memory) {
59+
const uint32_t value = 0x11223344;
60+
memcpy((char *)memory + sizeof(uint32_t), &value, sizeof(value));
61+
}
62+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %target-build-swift %s -import-objc-header %S/Inputs/unions-and-bitfields.h -disable-bridging-pch -o %t
2+
// RUN: %target-run %t
3+
// REQUIRES: executable_test
4+
5+
// The -disable-bridging-pch above isn't actually relevant to the test; however,
6+
// precompiled headers don't play nice with the way we include the platform
7+
// module map on non-Apple platforms. See
8+
// https://bugs.llvm.org/show_bug.cgi?id=36245.
9+
10+
import StdlibUnittest
11+
12+
var suite = TestSuite("UnionsAndBitfields")
13+
14+
suite.test("PlainUnion") {
15+
var x = PlainUnion()
16+
populate(&x)
17+
expectEqual(0x11223344, x.whole)
18+
expectTrue(x.first == 0x11 || x.first == 0x44)
19+
}
20+
21+
suite.test("PlainBitfield") {
22+
var x = PlainBitfield()
23+
populateAtOffset(&x)
24+
expectTrue(x.first == 0x11 || x.first == 0x44)
25+
}
26+
27+
suite.test("PlainIndirect") {
28+
var x = PlainIndirect()
29+
populateAtOffset(&x)
30+
expectEqual(0x11223344, x.whole)
31+
}
32+
33+
suite.test("BitfieldUnion") {
34+
var x = BitfieldUnion()
35+
populate(&x)
36+
expectEqual(0x11223344, x.whole)
37+
expectTrue(x.first == 0x11 || x.first == 0x44)
38+
}
39+
40+
suite.test("BitfieldIndirect") {
41+
var x = BitfieldIndirect()
42+
populateAtOffset(&x)
43+
expectTrue(x.first == 0x11 || x.first == 0x44)
44+
}
45+
46+
suite.test("UnionIndirect") {
47+
var x = UnionIndirect()
48+
populateAtOffset(&x)
49+
expectEqual(0x11223344, x.whole)
50+
expectTrue(x.first == 0x11 || x.first == 0x44)
51+
}
52+
53+
suite.test("BitfieldUnionIndirect") {
54+
var x = BitfieldUnionIndirect()
55+
populateAtOffset(&x)
56+
expectEqual(0x11223344, x.whole)
57+
expectTrue(x.first == 0x11 || x.first == 0x44)
58+
}
59+
60+
runAllTests()

0 commit comments

Comments
 (0)