Skip to content

Commit

Permalink
Implement bit field sanity checks
Browse files Browse the repository at this point in the history
  • Loading branch information
rikkimax committed Feb 11, 2025
1 parent 5170f3f commit 53f5bb4
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 8 deletions.
61 changes: 61 additions & 0 deletions compiler/src/dmd/semantic3.d
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ enum LOG = false;

/*************************************
* Does semantic analysis on function bodies.
* Also does struct field sanity checks.
*/
void semantic3(Dsymbol dsym, Scope* sc)
{
Expand Down Expand Up @@ -1550,6 +1551,9 @@ private extern(C++) final class Semantic3Visitor : Visitor
return;

StructDeclaration sd = ad.isStructDeclaration();
if (sd !is null && !sanityCheckOfStructFields(sd))
return;

Check warning on line 1555 in compiler/src/dmd/semantic3.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/semantic3.d#L1555

Added line #L1555 was not covered by tests

if (!sc) // from runDeferredSemantic3 for TypeInfo generation
{
assert(sd);
Expand Down Expand Up @@ -1753,3 +1757,60 @@ extern (D) bool checkClosure(FuncDeclaration fd)

return true;
}

private:

bool sanityCheckOfStructFields(StructDeclaration sd)
{
//printf("%s:\n", sd.ident.toChars);
uint storageSize;
uint bitsSoFar;
BitFieldDeclaration startOfZero;

foreach(field; sd.fields)
{
//printf(" %s: offset=%d, _linkage=%d\n", field.ident.toChars, field.offset, field._linkage);
//printf(" bitsSoFar=%d\n", bitsSoFar);

BitFieldDeclaration bfd = field.isBitFieldDeclaration();
if (!bfd)
{
bitsSoFar = 0;
storageSize = 0;
startOfZero = null;
continue;
}

//printf(" fieldWidth=%d, bitOffset=%d\n", bfd.fieldWidth, bfd.bitOffset);

if (storageSize == 0)
storageSize = target.fieldalign(bfd.type.toBasetype()) * 8;

if (bitsSoFar == 0)
startOfZero = null;

if (bfd.bitOffset != bitsSoFar && bfd._linkage == LINK.d)
{
if (startOfZero)

Check warning on line 1794 in compiler/src/dmd/semantic3.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/semantic3.d#L1794

Added line #L1794 was not covered by tests
{
.error(startOfZero.loc, "Unpredictable bit field layout detected starting at bit field `%s`", startOfZero.ident.toChars);
.errorSupplemental(field.loc, "Bit offset for `%s` expected %d, actual %d", field.ident.toChars, bitsSoFar, bfd.bitOffset);
.errorSupplemental(field.loc, "To disable this check specify an extern that is not D i.e. `extern(C)`");

Check warning on line 1798 in compiler/src/dmd/semantic3.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/semantic3.d#L1796-L1798

Added lines #L1796 - L1798 were not covered by tests
}
else
{
.error(field.loc, "Unpredictable bit field layout detected for bit field `%s`", field.ident.toChars);
.errorSupplemental(field.loc, "Bit offset expected %d, actual %d", bitsSoFar, bfd.bitOffset);
.errorSupplemental(field.loc, "To disable this check specify an extern that is not D i.e. `extern(C)`");

Check warning on line 1804 in compiler/src/dmd/semantic3.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/semantic3.d#L1802-L1804

Added lines #L1802 - L1804 were not covered by tests
}
}

if (bfd.bitOffset == 0)
startOfZero = bfd;

bitsSoFar += bfd.fieldWidth;
bitsSoFar %= storageSize;
}

return true;
}
1 change: 1 addition & 0 deletions compiler/test/compilable/dbitfield.d
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ static assert(testc() == 1);

union U
{
extern(C):
uint a:3;
uint b:1;
ulong c:64;
Expand Down
5 changes: 3 additions & 2 deletions compiler/test/fail_compilation/biterrors5.d
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/* REQUIRED_ARGS: -preview=bitfields
* TEST_OUTPUT:
---
fail_compilation/biterrors5.d(23): Error: bitfield symbol expected not struct `biterrors5.S`
fail_compilation/biterrors5.d(24): Error: bitfield symbol expected not variable `biterrors5.test0.i`
fail_compilation/biterrors5.d(24): Error: bitfield symbol expected not struct `biterrors5.S`
fail_compilation/biterrors5.d(25): Error: bitfield symbol expected not variable `biterrors5.test0.i`
---
*/

struct S
{
extern(C):
int a,b;
int :2, c:3;
}
Expand Down
50 changes: 50 additions & 0 deletions compiler/test/fail_compilation/bitinsane.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* REQUIRED_ARGS: -preview=bitfields
* TEST_OUTPUT:
---
fail_compilation/bitinsane.d(124): Error: Unpredictable bit field layout detected starting at bit field `i`
fail_compilation/bitinsane.d(125): Bit offset for `j` expected 5, actual 0
fail_compilation/bitinsane.d(125): To disable this check specify an extern that is not D i.e. `extern(C)`
fail_compilation/bitinsane.d(132): Error: Unpredictable bit field layout detected starting at bit field `x`
fail_compilation/bitinsane.d(133): Bit offset for `y` expected 5, actual 0
fail_compilation/bitinsane.d(133): To disable this check specify an extern that is not D i.e. `extern(C)`
---
*/

#line 100

struct Stuff_System {
extern(System):
int before1;
ubyte k1:4;
ubyte k2:4;
ubyte i:5;
ubyte j:4;
int after1;

struct {
int before2;
ubyte w1:4;
ubyte w2:4;
ubyte x:5;
ubyte y:4;
int after2;
}
}

struct Stuff_D {
int before1;
ubyte k1:4;
ubyte k2:4;
ubyte i:5;
ubyte j:4;
int after1;

struct {
int before2;
ubyte w1:4;
ubyte w2:4;
ubyte x:5;
ubyte y:4;
int after2;
}
}
1 change: 1 addition & 0 deletions compiler/test/runnable/bit.d
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ void test1()

struct S
{
extern(C):
uint a:3;
uint b:1;
ulong c:64;
Expand Down
3 changes: 3 additions & 0 deletions compiler/test/runnable/dbitfields.d
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ void test3()

struct S4
{
extern(C):
int i1;
uint a:2, b:31;
}
Expand All @@ -84,6 +85,7 @@ void test4()

struct S5
{
extern(C):
int i1;
uint a:2, :0, b:5;
}
Expand Down Expand Up @@ -178,6 +180,7 @@ static assert(test7s2() == -2);

struct S24257
{
extern(C):
uint : 15;
bool done : 1;
}
Expand Down
5 changes: 3 additions & 2 deletions compiler/test/runnable/dbitfieldsms.d
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import core.stdc.stdio;
int is64bit() { return size_t.sizeof == 8; } // otherwise assume 32 bit

/*************************************************************/
extern(C):

struct T0 { ubyte x:1; }; //
struct T1 { short x:1; }; //
Expand All @@ -75,7 +76,7 @@ struct S8B { ubyte a; short b:1; ubyte c:2; }; //
struct S8C { ubyte a; int b:1; }; //
struct S9 { ubyte a; ubyte b:2; short c:9; }; //
//struct S10 { }; //
struct S11 { int :0; }; // differs from C in that C sizeof is 4
extern(D) struct S11 { int :0; }; // differs from C in that C sizeof is 4
struct S12 { int :0; int x; }; //
struct S13 { uint x:12; uint x1:1; uint x2:1; uint x3:1; uint x4:1; int w; }; //
struct S14 { ubyte a; ubyte b:4; int c:30; }; //
Expand Down Expand Up @@ -104,7 +105,7 @@ struct A10 { ushort a:8; ubyte b; }; //
struct A11 { ubyte a; int b:5, c:11, :0, d:8; //
struct { int ee:8; } };

int main()
extern(D) int main()
{
/* MS produces identical results for 32 and 64 bit compiles,
* DM is 32 bit only
Expand Down
6 changes: 4 additions & 2 deletions compiler/test/runnable/dbitfieldsposix32.d
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ S8A = 2 2 | 2 2
S8B = 2 2 | 2 2
S8C = 4 4 | 4 4
S9 = 4 2 | 4 2
S10 = 1 1 | 0 1
S11 = 1 1 | 0 1
S10 = 0 1 | 0 1
S11 = 0 1 | 0 1
S12 = 4 4 | 4 4
S13 = 8 4 | 8 4
S14 = 8 4 | 8 4
Expand Down Expand Up @@ -55,6 +55,7 @@ import core.stdc.stdio;
int is64bit() { return size_t.sizeof == 8; } // otherwise assume 32 bit

/*************************************************************/
extern(C):

struct T0 { ubyte x:1; }; // 1 1
struct T1 { short x:1; }; // 2 2
Expand Down Expand Up @@ -104,6 +105,7 @@ struct A10 { ushort a:8; ubyte b; }; // 2 2
struct A11 { ubyte a; int b:5, c:11, :0, d:8; // 12 4
struct { int ee:8; } };

extern(D):
int main()
{
printf("T0 = %d %d | 1 1\n", cast(int)T0.sizeof, cast(int)T0.alignof);
Expand Down
6 changes: 4 additions & 2 deletions compiler/test/runnable/dbitfieldsposix64.d
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ S8A = 2 2 | 2 2
S8B = 2 2 | 2 2
S8C = 4 4 | 4 4
S9 = 4 2 | 4 2
S10 = 1 1 | 0 1
S11 = 1 1 | 0 1
S10 = 0 1 | 0 1
S11 = 0 1 | 0 1
S12 = 4 4 | 4 4
S13 = 8 4 | 8 4
S14 = 8 4 | 8 4
Expand Down Expand Up @@ -55,6 +55,7 @@ import core.stdc.stdio;
int is64bit() { return size_t.sizeof == 8; } // otherwise assume 32 bit

/*************************************************************/
extern(C):

struct T0 { ubyte x:1; }; // 1 1
struct T1 { short x:1; }; // 2 2
Expand Down Expand Up @@ -104,6 +105,7 @@ struct A10 { ushort a:8; ubyte b; }; // 2 2
struct A11 { ubyte a; int b:5, c:11, :0, d:8; // 12 4
struct { int ee:8; } };

extern(D):
int main()
{
printf("T0 = %d %d | 1 1\n", cast(int)T0.sizeof, cast(int)T0.alignof);
Expand Down

0 comments on commit 53f5bb4

Please sign in to comment.