Skip to content

Commit 170903a

Browse files
committed
add bitfields to D (dlang#13568)
1 parent 159dd03 commit 170903a

25 files changed

+1422
-23
lines changed

changelog/bitfields.dd

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Add bit fields to D
2+
3+
They work just like the bit fields in ImportC do.
4+
5+
https://github.com/dlang/dlang.org/pull/3190
6+
7+
---
8+
struct B
9+
{
10+
int x:3, y:2;
11+
}
12+
13+
static assert(B.sizeof == 4);
14+
15+
int vaporator(B b)
16+
{
17+
b.x = 4;
18+
b.y = 2;
19+
return b.x + b.y; // returns 6
20+
}
21+
---

src/dmd/cli.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,7 @@ dmd -cov -unittest myprog.d
845845
"implement https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1008.md (@nogc Throwable)"),
846846
Feature("dip1021", "useDIP1021",
847847
"implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md (Mutable function arguments)"),
848+
Feature("bitfields", "bitfields", "add bitfields https://github.com/dlang/dlang.org/pull/3190"),
848849
Feature("fieldwise", "fieldwise", "use fieldwise comparisons for struct equality"),
849850
Feature("fixAliasThis", "fixAliasThis",
850851
"when a symbol is resolved, check alias this scope before going to upper scopes"),

src/dmd/declaration.d

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,6 +1697,32 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
16971697
v.visit(this);
16981698
}
16991699

1700+
/***********************************
1701+
* Retrieve the .min or .max values.
1702+
* Only valid after semantic analysis.
1703+
* Params:
1704+
* id = Id.min or Id.max
1705+
* Returns:
1706+
* the min or max value
1707+
*/
1708+
final ulong getMinMax(Identifier id)
1709+
{
1710+
const width = fieldWidth;
1711+
const uns = type.isunsigned();
1712+
const min = id == Id.min;
1713+
ulong v;
1714+
assert(width != 0); // should have been rejected in semantic pass
1715+
if (width == ulong.sizeof * 8)
1716+
v = uns ? (min ? ulong.min : ulong.max)
1717+
: (min ? long.min : long.max);
1718+
else
1719+
v = uns ? (min ? 0
1720+
: (1L << width) - 1)
1721+
: (min ? -(1L << (width - 1))
1722+
: (1L << (width - 1)) - 1);
1723+
return v;
1724+
}
1725+
17001726
override final void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
17011727
{
17021728
//printf("BitFieldDeclaration::setFieldOffset(ad: %s, field: %s)\n", ad.toChars(), toChars());

src/dmd/dsymbolsem.d

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,11 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
12041204
if (dsym.errors)
12051205
return;
12061206

1207+
if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration())
1208+
{
1209+
dsym.error("bit-field must be member of struct, union, or class");
1210+
}
1211+
12071212
sc = sc.startCTFE();
12081213
auto width = dsym.width.expressionSemantic(sc);
12091214
sc = sc.endCTFE();

src/dmd/expressionsem.d

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12642,6 +12642,22 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
1264212642
Expression e = new IntegerExp(exp.loc, actualAlignment, Type.tsize_t);
1264312643
return e;
1264412644
}
12645+
else if ((exp.ident == Id.max || exp.ident == Id.min) &&
12646+
exp.e1.isVarExp() &&
12647+
exp.e1.isVarExp().var.isBitFieldDeclaration())
12648+
{
12649+
// For `x.max` and `x.min` get the max/min of the bitfield, not the max/min of its type
12650+
auto bf = exp.e1.isVarExp().var.isBitFieldDeclaration();
12651+
return new IntegerExp(exp.loc, bf.getMinMax(exp.ident), bf.type);
12652+
}
12653+
else if ((exp.ident == Id.max || exp.ident == Id.min) &&
12654+
exp.e1.isDotVarExp() &&
12655+
exp.e1.isDotVarExp().var.isBitFieldDeclaration())
12656+
{
12657+
// For `x.max` and `x.min` get the max/min of the bitfield, not the max/min of its type
12658+
auto bf = exp.e1.isDotVarExp().var.isBitFieldDeclaration();
12659+
return new IntegerExp(exp.loc, bf.getMinMax(exp.ident), bf.type);
12660+
}
1264512661
else if (cfile && exp.ident == Id.__sizeof && exp.e1.isStringExp())
1264612662
{
1264712663
// Sizeof string literal includes the terminating 0
@@ -12655,7 +12671,9 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
1265512671
flag = 0;
1265612672
Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0));
1265712673
if (e)
12674+
{
1265812675
e = e.expressionSemantic(sc);
12676+
}
1265912677
return e;
1266012678
}
1266112679
}

src/dmd/frontend.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ struct Param final
507507
bool ehnogc;
508508
FeatureState dtorFields;
509509
bool fieldwise;
510+
bool bitfields;
510511
FeatureState rvalueRefParam;
511512
CppStdRevision cplusplus;
512513
bool markdown;
@@ -632,6 +633,7 @@ struct Param final
632633
inclusiveInContracts(),
633634
ehnogc(),
634635
fieldwise(),
636+
bitfields(),
635637
cplusplus((CppStdRevision)201103u),
636638
markdown(true),
637639
vmarkdown(),
@@ -705,7 +707,7 @@ struct Param final
705707
mapfile()
706708
{
707709
}
708-
Param(bool obj, bool link = true, bool dll = false, bool lib = false, bool multiobj = false, bool oneobj = false, bool trace = false, bool tracegc = false, bool verbose = false, bool vcg_ast = false, bool showColumns = false, bool vtls = false, bool vtemplates = false, bool vtemplatesListInstances = false, bool vgc = false, bool vfield = false, bool vcomplex = true, bool vin = false, uint8_t symdebug = 0u, bool symdebugref = false, bool optimize = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool stackstomp = false, bool useUnitTests = false, bool useInline = false, FeatureState useDIP25 = (FeatureState)-1, FeatureState useDIP1000 = (FeatureState)-1, bool useDIP1021 = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, PIC pic = (PIC)0u, bool color = false, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool nofloat = false, bool ignoreUnsupportedPragmas = false, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool noSharedAccess = false, bool previewIn = false, bool shortenedMethods = false, bool betterC = false, bool addMain = false, bool allInst = false, bool fix16997 = true, bool fixAliasThis = false, bool inclusiveInContracts = false, bool ehnogc = false, FeatureState dtorFields = (FeatureState)-1, bool fieldwise = false, FeatureState rvalueRefParam = (FeatureState)-1, CppStdRevision cplusplus = (CppStdRevision)201103u, bool markdown = true, bool vmarkdown = false, bool showGaggedErrors = false, bool printErrorContext = false, bool manual = false, bool usage = false, bool mcpuUsage = false, bool transitionUsage = false, bool checkUsage = false, bool checkActionUsage = false, bool revertUsage = false, bool previewUsage = false, bool externStdUsage = false, bool hcUsage = false, bool logo = false, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, uint32_t errorLimit = 20u, _d_dynamicArray< const char > argv0 = {}, Array<const char* > modFileAliasStrings = Array<const char* >(), Array<const char* >* imppath = nullptr, Array<const char* >* fileImppath = nullptr, _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, bool doDocComments = false, _d_dynamicArray< const char > docdir = {}, _d_dynamicArray< const char > docname = {}, Array<const char* > ddocfiles = Array<const char* >(), bool doHdrGeneration = false, _d_dynamicArray< const char > hdrdir = {}, _d_dynamicArray< const char > hdrname = {}, bool hdrStripPlainFunctions = true, CxxHeaderMode doCxxHdrGeneration = (CxxHeaderMode)0u, _d_dynamicArray< const char > cxxhdrdir = {}, _d_dynamicArray< const char > cxxhdrname = {}, bool doJsonGeneration = false, _d_dynamicArray< const char > jsonfilename = {}, JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, OutBuffer* mixinOut = nullptr, const char* mixinFile = nullptr, int32_t mixinLines = 0, uint32_t debuglevel = 0u, Array<const char* >* debugids = nullptr, uint32_t versionlevel = 0u, Array<const char* >* versionids = nullptr, _d_dynamicArray< const char > defaultlibname = {}, _d_dynamicArray< const char > debuglibname = {}, _d_dynamicArray< const char > mscrtlib = {}, _d_dynamicArray< const char > moduleDepsFile = {}, OutBuffer* moduleDeps = nullptr, bool emitMakeDeps = false, _d_dynamicArray< const char > makeDepsFile = {}, Array<const char* > makeDeps = Array<const char* >(), MessageStyle messageStyle = (MessageStyle)0u, bool run = false, Array<const char* > runargs = Array<const char* >(), Array<const char* > objfiles = Array<const char* >(), Array<const char* > linkswitches = Array<const char* >(), Array<bool > linkswitchIsForCC = Array<bool >(), Array<const char* > libfiles = Array<const char* >(), Array<const char* > dllfiles = Array<const char* >(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}) :
710+
Param(bool obj, bool link = true, bool dll = false, bool lib = false, bool multiobj = false, bool oneobj = false, bool trace = false, bool tracegc = false, bool verbose = false, bool vcg_ast = false, bool showColumns = false, bool vtls = false, bool vtemplates = false, bool vtemplatesListInstances = false, bool vgc = false, bool vfield = false, bool vcomplex = true, bool vin = false, uint8_t symdebug = 0u, bool symdebugref = false, bool optimize = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool stackstomp = false, bool useUnitTests = false, bool useInline = false, FeatureState useDIP25 = (FeatureState)-1, FeatureState useDIP1000 = (FeatureState)-1, bool useDIP1021 = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, PIC pic = (PIC)0u, bool color = false, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool nofloat = false, bool ignoreUnsupportedPragmas = false, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool noSharedAccess = false, bool previewIn = false, bool shortenedMethods = false, bool betterC = false, bool addMain = false, bool allInst = false, bool fix16997 = true, bool fixAliasThis = false, bool inclusiveInContracts = false, bool ehnogc = false, FeatureState dtorFields = (FeatureState)-1, bool fieldwise = false, bool bitfields = false, FeatureState rvalueRefParam = (FeatureState)-1, CppStdRevision cplusplus = (CppStdRevision)201103u, bool markdown = true, bool vmarkdown = false, bool showGaggedErrors = false, bool printErrorContext = false, bool manual = false, bool usage = false, bool mcpuUsage = false, bool transitionUsage = false, bool checkUsage = false, bool checkActionUsage = false, bool revertUsage = false, bool previewUsage = false, bool externStdUsage = false, bool hcUsage = false, bool logo = false, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, uint32_t errorLimit = 20u, _d_dynamicArray< const char > argv0 = {}, Array<const char* > modFileAliasStrings = Array<const char* >(), Array<const char* >* imppath = nullptr, Array<const char* >* fileImppath = nullptr, _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, bool doDocComments = false, _d_dynamicArray< const char > docdir = {}, _d_dynamicArray< const char > docname = {}, Array<const char* > ddocfiles = Array<const char* >(), bool doHdrGeneration = false, _d_dynamicArray< const char > hdrdir = {}, _d_dynamicArray< const char > hdrname = {}, bool hdrStripPlainFunctions = true, CxxHeaderMode doCxxHdrGeneration = (CxxHeaderMode)0u, _d_dynamicArray< const char > cxxhdrdir = {}, _d_dynamicArray< const char > cxxhdrname = {}, bool doJsonGeneration = false, _d_dynamicArray< const char > jsonfilename = {}, JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, OutBuffer* mixinOut = nullptr, const char* mixinFile = nullptr, int32_t mixinLines = 0, uint32_t debuglevel = 0u, Array<const char* >* debugids = nullptr, uint32_t versionlevel = 0u, Array<const char* >* versionids = nullptr, _d_dynamicArray< const char > defaultlibname = {}, _d_dynamicArray< const char > debuglibname = {}, _d_dynamicArray< const char > mscrtlib = {}, _d_dynamicArray< const char > moduleDepsFile = {}, OutBuffer* moduleDeps = nullptr, bool emitMakeDeps = false, _d_dynamicArray< const char > makeDepsFile = {}, Array<const char* > makeDeps = Array<const char* >(), MessageStyle messageStyle = (MessageStyle)0u, bool run = false, Array<const char* > runargs = Array<const char* >(), Array<const char* > objfiles = Array<const char* >(), Array<const char* > linkswitches = Array<const char* >(), Array<bool > linkswitchIsForCC = Array<bool >(), Array<const char* > libfiles = Array<const char* >(), Array<const char* > dllfiles = Array<const char* >(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}) :
709711
obj(obj),
710712
link(link),
711713
dll(dll),
@@ -759,6 +761,7 @@ struct Param final
759761
ehnogc(ehnogc),
760762
dtorFields(dtorFields),
761763
fieldwise(fieldwise),
764+
bitfields(bitfields),
762765
rvalueRefParam(rvalueRefParam),
763766
cplusplus(cplusplus),
764767
markdown(markdown),
@@ -5859,6 +5862,7 @@ class BitFieldDeclaration : public VarDeclaration
58595862
BitFieldDeclaration* syntaxCopy(Dsymbol* s);
58605863
BitFieldDeclaration* isBitFieldDeclaration();
58615864
void accept(Visitor* v);
5865+
uint64_t getMinMax(Identifier* id);
58625866
void setFieldOffset(AggregateDeclaration* ad, FieldState& fieldState, bool isunion);
58635867
};
58645868

src/dmd/globals.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ extern (C++) struct Param
162162
FeatureState dtorFields; // destruct fields of partially constructed objects
163163
// https://issues.dlang.org/show_bug.cgi?id=14246
164164
bool fieldwise; // do struct equality testing field-wise rather than by memcmp()
165+
bool bitfields; // support C style bit fields
165166
FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters
166167
// https://dconf.org/2019/talks/alexandrescu.html
167168
// https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a

src/dmd/globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ struct Param
147147
FeatureState dtorFields; // destruct fields of partially constructed objects
148148
// https://issues.dlang.org/show_bug.cgi?id=14246
149149
bool fieldwise; // do struct equality testing field-wise rather than by memcmp()
150+
bool bitfields; // support C style bit fields
150151
FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters
151152
CppStdRevision cplusplus; // version of C++ name mangling to support
152153
bool markdown; // enable Markdown replacements in Ddoc

src/dmd/parse.d

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
289289
return true;
290290
}
291291

292+
/************************************
293+
* Parse declarations and definitions
294+
* Params:
295+
* once = !=0 means parse exactly one decl or def
296+
* pLastDecl = set to last decl or def parsed
297+
* pAttrs = keep track of attributes
298+
* Returns:
299+
* array of declared symbols
300+
*/
292301
AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null)
293302
{
294303
AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration
@@ -4298,7 +4307,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
42984307
* These can be:
42994308
* 1. declarations at global/class level
43004309
* 2. declarations at statement level
4301-
* Return array of Declaration *'s.
4310+
* Returns:
4311+
* array of Declarations.
43024312
*/
43034313
private AST.Dsymbols* parseDeclarations(bool autodecl, PrefixAttributes!AST* pAttrs, const(char)* comment)
43044314
{
@@ -4459,6 +4469,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
44594469
else if (t != tfirst)
44604470
error("multiple declarations must have the same type, not `%s` and `%s`", tfirst.toChars(), t.toChars());
44614471

4472+
if (token.value == TOK.colon && !ident && t.ty != Tfunction)
4473+
{
4474+
// Unnamed bit field
4475+
ident = Identifier.generateAnonymousId("BitField");
4476+
}
4477+
44624478
bool isThis = (t.ty == Tident && (cast(AST.TypeIdentifier)t).ident == Id.This && token.value == TOK.assign);
44634479
if (ident)
44644480
checkCstyleTypeSyntax(loc, t, alt, ident);
@@ -4591,19 +4607,39 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
45914607
}
45924608
else if (ident)
45934609
{
4610+
AST.Expression width;
4611+
if (token.value == TOK.colon)
4612+
{
4613+
nextToken();
4614+
width = parseCondExp();
4615+
}
4616+
45944617
AST.Initializer _init = null;
45954618
if (token.value == TOK.assign)
45964619
{
45974620
nextToken();
45984621
_init = parseInitializer();
45994622
}
46004623

4601-
auto v = new AST.VarDeclaration(loc, t, ident, _init);
4602-
v.storage_class = storage_class;
4603-
if (pAttrs)
4604-
pAttrs.storageClass = STC.undefined_;
4605-
4606-
AST.Dsymbol s = v;
4624+
AST.Dsymbol s;
4625+
if (width)
4626+
{
4627+
if (!global.params.bitfields)
4628+
error("use -preview=bitfields for bitfield support");
4629+
if (_init)
4630+
error("initializer not allowed for bit-field declaration");
4631+
if (storage_class)
4632+
error("storage class not allowed for bit-field declaration");
4633+
s = new AST.BitFieldDeclaration(width.loc, t, ident, width);
4634+
}
4635+
else
4636+
{
4637+
auto v = new AST.VarDeclaration(loc, t, ident, _init);
4638+
v.storage_class = storage_class;
4639+
if (pAttrs)
4640+
pAttrs.storageClass = STC.undefined_;
4641+
s = v;
4642+
}
46074643

46084644
if (tpl && _init)
46094645
{
@@ -4616,7 +4652,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
46164652
{
46174653
auto ax = new AST.Dsymbols();
46184654
ax.push(s);
4619-
s = new AST.AlignDeclaration(v.loc, ealign, ax);
4655+
s = new AST.AlignDeclaration(s.loc, ealign, ax);
46204656
}
46214657
if (link != linkage)
46224658
{
@@ -4646,12 +4682,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
46464682
default:
46474683
if (loc.linnum != token.loc.linnum)
46484684
{
4649-
error("semicolon needed to end declaration of `%s`, instead of `%s`", v.toChars(), token.toChars());
4650-
errorSupplemental(loc, "`%s` declared here", v.toChars());
4685+
error("semicolon needed to end declaration of `%s`, instead of `%s`", s.toChars(), token.toChars());
4686+
errorSupplemental(loc, "`%s` declared here", s.toChars());
46514687
}
46524688
else
46534689
{
4654-
error("semicolon needed to end declaration of `%s` instead of `%s`", v.toChars(), token.toChars());
4690+
error("semicolon needed to end declaration of `%s` instead of `%s`", s.toChars(), token.toChars());
46554691
}
46564692
break;
46574693
}

0 commit comments

Comments
 (0)