diff --git a/changelog.dd b/changelog.dd index 760aef85fdba..f70e3061577d 100644 --- a/changelog.dd +++ b/changelog.dd @@ -5,6 +5,7 @@ $(COMMENT Pending changelog for 2.072. This will get copied to dlang.org and ) $(BUGSTITLE Language Changes, + $(LI $(RELATIVE_LINK2 align_by_ctfe, Align attribute can be used with CTFEable expression.)) ) $(BUGSTITLE Compiler Changes, @@ -13,6 +14,28 @@ $(BUGSTITLE Compiler Changes, ) $(BUGSTITLE Language Changes, + $(LI $(LNAME2 align_by_ctfe, Align attribute can be used with CTFEable expression.) + + $(P Example:) + + --- + version (D_LP64) + enum n = 8; + else + enum n = 4; + align (n) struct Data + { + align (n == 8 ? 2 : 1): + ubyte[3] buffer; + int flags; + } + + version (D_LP64) + static assert(Data.flags.offsetof == 4); + else + static assert(Data.flags.offsetof == 3); + --- + ) ) $(BUGSTITLE Compiler Changes, diff --git a/src/aggregate.d b/src/aggregate.d index 6d1ff7d9b640..3003e1c8cd72 100644 --- a/src/aggregate.d +++ b/src/aggregate.d @@ -120,7 +120,7 @@ public: sc2.inunion = 1; sc2.protection = Prot(PROTpublic); sc2.explicitProtection = 0; - sc2.structalign = STRUCTALIGN_DEFAULT; + sc2.aligndecl = null; sc2.userAttribDecl = null; return sc2; } diff --git a/src/attrib.d b/src/attrib.d index 2206a177ecae..e5d7ae0c835e 100644 --- a/src/attrib.d +++ b/src/attrib.d @@ -78,10 +78,17 @@ public: * If the returned scope != sc, the caller should pop * the scope after it used. */ - static Scope* createNewScope(Scope* sc, StorageClass stc, LINK linkage, Prot protection, int explicitProtection, structalign_t structalign, PINLINE inlining) + static Scope* createNewScope(Scope* sc, StorageClass stc, LINK linkage, + Prot protection, int explicitProtection, AlignDeclaration aligndecl, + PINLINE inlining) { Scope* sc2 = sc; - if (stc != sc.stc || linkage != sc.linkage || !protection.isSubsetOf(sc.protection) || explicitProtection != sc.explicitProtection || structalign != sc.structalign || inlining != sc.inlining) + if (stc != sc.stc || + linkage != sc.linkage || + !protection.isSubsetOf(sc.protection) || + explicitProtection != sc.explicitProtection || + aligndecl !is sc.aligndecl || + inlining != sc.inlining) { // create new one for changes sc2 = sc.copy(); @@ -89,7 +96,7 @@ public: sc2.linkage = linkage; sc2.protection = protection; sc2.explicitProtection = explicitProtection; - sc2.structalign = structalign; + sc2.aligndecl = aligndecl; sc2.inlining = inlining; } return sc2; @@ -352,7 +359,9 @@ public: scstc &= ~(STCsafe | STCtrusted | STCsystem); scstc |= stc; //printf("scstc = x%llx\n", scstc); - return createNewScope(sc, scstc, sc.linkage, sc.protection, sc.explicitProtection, sc.structalign, sc.inlining); + return createNewScope(sc, scstc, sc.linkage, + sc.protection, sc.explicitProtection, sc.aligndecl, + sc.inlining); } override final bool oneMember(Dsymbol* ps, Identifier ident) @@ -499,7 +508,9 @@ public: override Scope* newScope(Scope* sc) { - return createNewScope(sc, sc.stc, this.linkage, sc.protection, sc.explicitProtection, sc.structalign, sc.inlining); + return createNewScope(sc, sc.stc, this.linkage, + sc.protection, sc.explicitProtection, sc.aligndecl, + sc.inlining); } override const(char)* toChars() const @@ -563,7 +574,9 @@ public: { if (pkg_identifiers) semantic(sc); - return createNewScope(sc, sc.stc, sc.linkage, this.protection, 1, sc.structalign, sc.inlining); + return createNewScope(sc, sc.stc, sc.linkage, + this.protection, 1, sc.aligndecl, + sc.inlining); } override void addMember(Scope* sc, ScopeDsymbol sds) @@ -611,23 +624,91 @@ public: extern (C++) final class AlignDeclaration : AttribDeclaration { public: - uint salign; + Expression ealign; + structalign_t salign = STRUCTALIGN_DEFAULT; - extern (D) this(uint sa, Dsymbols* decl) + extern (D) this(Loc loc, Expression ealign, Dsymbols* decl) { super(decl); - salign = sa; + this.loc = loc; + this.ealign = ealign; } override Dsymbol syntaxCopy(Dsymbol s) { assert(!s); - return new AlignDeclaration(salign, Dsymbol.arraySyntaxCopy(decl)); + return new AlignDeclaration(loc, + ealign.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl)); } override Scope* newScope(Scope* sc) { - return createNewScope(sc, sc.stc, sc.linkage, sc.protection, sc.explicitProtection, this.salign, sc.inlining); + return createNewScope(sc, sc.stc, sc.linkage, + sc.protection, sc.explicitProtection, this, + sc.inlining); + } + + override void setScope(Scope* sc) + { + //printf("AlignDeclaration::setScope() %p\n", this); + if (ealign && decl) + Dsymbol.setScope(sc); // for forward reference + return AttribDeclaration.setScope(sc); + } + + override void semantic2(Scope* sc) + { + getAlignment(); + super.semantic2(sc); + } + + structalign_t getAlignment() + { + if (!ealign) + return STRUCTALIGN_DEFAULT; + + if (auto sc = _scope) + { + _scope = null; + + sc = sc.startCTFE(); + ealign = ealign.semantic(sc); + ealign = resolveProperties(sc, ealign); + sc = sc.endCTFE(); + + auto errorPositiveInteger() + { + .error(loc, "positive integer expected, not %s", ealign.toChars()); + return STRUCTALIGN_DEFAULT; + } + + if (ealign.op == TOKerror) + return STRUCTALIGN_DEFAULT; + if (!ealign.type) + return errorPositiveInteger(); + Type tb = ealign.type.toBasetype(); + if (!tb.isintegral()) + return errorPositiveInteger(); + if (tb.ty == Tchar || tb.ty == Twchar || tb.ty == Tdchar || tb.ty == Tbool) + return errorPositiveInteger(); + + ealign = ealign.ctfeInterpret(); + if (ealign.op == TOKerror) + return STRUCTALIGN_DEFAULT; + + auto n = ealign.toInteger(); + if (n < 1 || structalign_t.max < n) + return errorPositiveInteger(); + + if (n & (n - 1)) + { + .error(loc, "alignment must be a power of 2, not %u", cast(structalign_t)n); + return STRUCTALIGN_DEFAULT; + } + + salign = cast(structalign_t)n; + } + return salign; } override void accept(Visitor v) @@ -642,7 +723,6 @@ extern (C++) final class AnonDeclaration : AttribDeclaration { public: bool isunion; - structalign_t alignment; int sem; // 1 if successful semantic() uint anonoffset; // offset of anonymous struct uint anonstructsize; // size of anonymous struct @@ -663,22 +743,23 @@ public: override void setScope(Scope* sc) { - super.setScope(sc); - alignment = sc.structalign; + if (decl) + Dsymbol.setScope(sc); + return AttribDeclaration.setScope(sc); } override void semantic(Scope* sc) { //printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", this); assert(sc.parent); - Dsymbol p = sc.parent.pastMixin(); - AggregateDeclaration ad = p.isAggregateDeclaration(); + auto p = sc.parent.pastMixin(); + auto ad = p.isAggregateDeclaration(); if (!ad) { .error(loc, "%s can only be a part of an aggregate, not %s %s", kind(), p.kind(), p.toChars()); return; } - alignment = sc.structalign; + if (decl) { sc = sc.push(); @@ -746,6 +827,9 @@ public: anonalignsize = 1; } + assert(_scope); + auto alignment = _scope.alignment(); + /* Given the anon 'member's size and alignment, * go ahead and place it. */ @@ -833,7 +917,9 @@ public: else if (e.isBool(false)) inlining = PINLINEnever; } - return createNewScope(sc, sc.stc, sc.linkage, sc.protection, sc.explicitProtection, sc.structalign, inlining); + return createNewScope(sc, sc.stc, sc.linkage, + sc.protection, sc.explicitProtection, sc.aligndecl, + inlining); } return sc; } diff --git a/src/attrib.h b/src/attrib.h index 08cdf1ead00d..63597a4d617f 100644 --- a/src/attrib.h +++ b/src/attrib.h @@ -117,11 +117,15 @@ class ProtDeclaration : public AttribDeclaration class AlignDeclaration : public AttribDeclaration { public: - unsigned salign; + Expression *ealign; + structalign_t salign; - AlignDeclaration(unsigned sa, Dsymbols *decl); + AlignDeclaration(Loc loc, Expression *ealign, Dsymbols *decl); Dsymbol *syntaxCopy(Dsymbol *s); Scope *newScope(Scope *sc); + void setScope(Scope *sc); + void semantic2(Scope *sc); + structalign_t getAlignment(); void accept(Visitor *v) { v->visit(this); } }; @@ -129,7 +133,6 @@ class AnonDeclaration : public AttribDeclaration { public: bool isunion; - structalign_t alignment; int sem; // 1 if successful semantic() unsigned anonoffset; // offset of anonymous struct unsigned anonstructsize; // size of anonymous struct diff --git a/src/declaration.d b/src/declaration.d index 36c5118f6af5..5a1a8d30b6d7 100644 --- a/src/declaration.d +++ b/src/declaration.d @@ -1120,7 +1120,7 @@ public: /* If scope's alignment is the default, use the type's alignment, * otherwise the scope overrrides. */ - alignment = sc.structalign; + alignment = sc.alignment(); if (alignment == STRUCTALIGN_DEFAULT) alignment = type.alignment(); // use type's alignment diff --git a/src/dscope.d b/src/dscope.d index b0f189523a1a..2e3f9e86c687 100644 --- a/src/dscope.d +++ b/src/dscope.d @@ -143,7 +143,7 @@ struct Scope size_t fieldinit_dim; // alignment for struct members - structalign_t structalign = STRUCTALIGN_DEFAULT; + AlignDeclaration aligndecl; // linkage for external functions LINK linkage = LINKd; @@ -722,7 +722,7 @@ struct Scope this.scontinue = sc.scontinue; this.fes = sc.fes; this.callsc = sc.callsc; - this.structalign = sc.structalign; + this.aligndecl = sc.aligndecl; this.func = sc.func; this.slabel = sc.slabel; this.linkage = sc.linkage; @@ -745,4 +745,12 @@ struct Scope this.prevAnchor = sc.prevAnchor; this.userAttribDecl = sc.userAttribDecl; } + + structalign_t alignment() + { + if (aligndecl) + return aligndecl.getAlignment(); + else + return STRUCTALIGN_DEFAULT; + } } diff --git a/src/dstruct.d b/src/dstruct.d index ea4d0703f0f1..00612d39573e 100644 --- a/src/dstruct.d +++ b/src/dstruct.d @@ -301,7 +301,7 @@ public: { protection = sc.protection; - alignment = sc.structalign; + alignment = sc.alignment(); storage_class |= sc.stc; if (storage_class & STCdeprecated) diff --git a/src/func.d b/src/func.d index e7208c4fce38..fb86746377ce 100644 --- a/src/func.d +++ b/src/func.d @@ -1442,7 +1442,7 @@ public: sc2.stc &= ~(STCauto | STCscope | STCstatic | STCabstract | STCdeprecated | STCoverride | STC_TYPECTOR | STCfinal | STCtls | STCgshared | STCref | STCreturn | STCproperty | STCnothrow | STCpure | STCsafe | STCtrusted | STCsystem); sc2.protection = Prot(PROTpublic); sc2.explicitProtection = 0; - sc2.structalign = STRUCTALIGN_DEFAULT; + sc2.aligndecl = null; if (this.ident != Id.require && this.ident != Id.ensure) sc2.flags = sc.flags & ~SCOPEcontract; sc2.flags &= ~SCOPEcompile; diff --git a/src/hdrgen.d b/src/hdrgen.d index ecf2e3018f8f..9780061210f4 100644 --- a/src/hdrgen.d +++ b/src/hdrgen.d @@ -1201,10 +1201,10 @@ public: override void visit(AlignDeclaration d) { - if (d.salign == STRUCTALIGN_DEFAULT) - buf.printf("align"); + if (!d.ealign) + buf.printf("align "); else - buf.printf("align (%d)", d.salign); + buf.printf("align (%s) ", d.ealign.toChars()); visit(cast(AttribDeclaration)d); } diff --git a/src/parse.d b/src/parse.d index bef6264235cb..835f77d7e8d9 100644 --- a/src/parse.d +++ b/src/parse.d @@ -226,7 +226,8 @@ struct PrefixAttributes Expression depmsg; LINK link; Prot protection; - uint alignment; + bool setAlignment; + Expression ealign; Expressions* udas; const(char)* comment; } @@ -972,59 +973,38 @@ public: } case TOKalign: { + const attrLoc = token.loc; + nextToken(); - uint n; + Expression e = null; // default if (token.value == TOKlparen) { nextToken(); - if (token.value == TOKint32v && token.uns64value > 0) - { - if (token.uns64value & (token.uns64value - 1)) - error("align(%s) must be a power of 2", token.toChars()); - n = cast(uint)token.uns64value; - } - else - { - error("positive integer expected, not %s", token.toChars()); - n = 1; - } - nextToken(); + e = parseAssignExp(); check(TOKrparen); } - else - n = STRUCTALIGN_DEFAULT; // default - if (pAttrs.alignment != 0) + if (pAttrs.setAlignment) { const(char)* s1 = ""; OutBuffer buf1; - if (n != STRUCTALIGN_DEFAULT) + if (e) { - buf1.printf("(%d)", n); + buf1.printf("(%s)", e.toChars()); s1 = buf1.peekString(); } - if (pAttrs.alignment != n) - { - OutBuffer buf2; - const(char)* s2 = ""; - if (pAttrs.alignment != STRUCTALIGN_DEFAULT) - { - buf2.printf("(%d)", pAttrs.alignment); - s2 = buf2.peekString(); - } - error("conflicting alignment attribute align%s and align%s", s2, s1); - } - else - error("redundant alignment attribute align%s", s1); + error("redundant alignment attribute align%s", s1); } - pAttrs.alignment = n; + pAttrs.setAlignment = true; + pAttrs.ealign = e; a = parseBlock(pLastDecl, pAttrs); - if (pAttrs.alignment != 0) + if (pAttrs.setAlignment) { - s = new AlignDeclaration(pAttrs.alignment, a); - pAttrs.alignment = 0; + s = new AlignDeclaration(attrLoc, pAttrs.ealign, a); + pAttrs.setAlignment = false; + pAttrs.ealign = null; } break; } @@ -3944,7 +3924,8 @@ public: return ts; } - void parseStorageClasses(ref StorageClass storage_class, ref LINK link, ref uint structalign, ref Expressions* udas) + void parseStorageClasses(ref StorageClass storage_class, ref LINK link, + ref bool setAlignment, ref Expression ealign, ref Expressions* udas) { StorageClass stc; bool sawLinkage = false; // seen a linkage declaration @@ -4063,21 +4044,13 @@ public: case TOKalign: { nextToken(); + setAlignment = true; if (token.value == TOKlparen) { nextToken(); - if (token.value == TOKint32v && token.uns64value > 0) - structalign = cast(uint)token.uns64value; - else - { - error("positive integer expected, not %s", token.toChars()); - structalign = 1; - } - nextToken(); + ealign = parseExpression(); check(TOKrparen); } - else - structalign = STRUCTALIGN_DEFAULT; // default continue; } default: @@ -4103,7 +4076,8 @@ public: Identifier ident; TOK tok = TOKreserved; LINK link = linkage; - uint structalign = 0; + bool setAlignment = false; + Expression ealign; auto loc = token.loc; Expressions* udas = null; Token* tk; @@ -4197,9 +4171,10 @@ public: storage_class = STCundefined; link = linkage; - structalign = 0; + setAlignment = false; + ealign = null; udas = null; - parseStorageClasses(storage_class, link, structalign, udas); + parseStorageClasses(storage_class, link, setAlignment, ealign, udas); if (udas) error("user defined attributes not allowed for %s declarations", Token.toChars(tok)); @@ -4260,9 +4235,12 @@ public: // alias StorageClasses type ident; } - parseStorageClasses(storage_class, link, structalign, udas); + parseStorageClasses(storage_class, link, setAlignment, ealign, udas); - if (token.value == TOKstruct || token.value == TOKunion || token.value == TOKclass || token.value == TOKinterface) + if (token.value == TOKstruct || + token.value == TOKunion || + token.value == TOKclass || + token.value == TOKinterface) { Dsymbol s = parseAggregate(); auto a = new Dsymbols(); @@ -4274,9 +4252,9 @@ public: a = new Dsymbols(); a.push(s); } - if (structalign != 0) + if (setAlignment) { - s = new AlignDeclaration(structalign, a); + s = new AlignDeclaration(s.loc, ealign, a); a = new Dsymbols(); a.push(s); } @@ -4502,11 +4480,11 @@ public: auto tempdecl = new TemplateDeclaration(loc, ident, tpl, null, a2, 0); s = tempdecl; } - if (structalign != 0) + if (setAlignment) { auto ax = new Dsymbols(); ax.push(s); - s = new AlignDeclaration(structalign, ax); + s = new AlignDeclaration(v.loc, ealign, ax); } if (link != linkage) { diff --git a/src/scope.h b/src/scope.h index 0a3ecf7a3d04..69ca2db5239a 100644 --- a/src/scope.h +++ b/src/scope.h @@ -105,7 +105,9 @@ struct Scope unsigned *fieldinit; size_t fieldinit_dim; - structalign_t structalign; // alignment for struct members + // alignment for struct members + AlignDeclaration *aligndecl; + LINK linkage; // linkage for external functions PINLINE inlining; // inlining strategy for functions @@ -153,6 +155,8 @@ struct Scope ClassDeclaration *getClassScope(); AggregateDeclaration *getStructClassScope(); void setNoFree(); + + structalign_t alignment(); }; #endif /* DMD_SCOPE_H */ diff --git a/test/compilable/extra-files/header1.di b/test/compilable/extra-files/header1.di index f932d3c042ab..06625fa62991 100644 --- a/test/compilable/extra-files/header1.di +++ b/test/compilable/extra-files/header1.di @@ -323,7 +323,7 @@ version (unittest) { public {} extern (C) {} - align{} + align {} } template Foo10334(T) if (Bar10334!()) { diff --git a/test/compilable/extra-files/header1i.di b/test/compilable/extra-files/header1i.di index 44f9b4f06343..e7e4987056be 100644 --- a/test/compilable/extra-files/header1i.di +++ b/test/compilable/extra-files/header1i.di @@ -448,7 +448,7 @@ version (unittest) { public {} extern (C) {} - align{} + align {} } template Foo10334(T) if (Bar10334!()) { diff --git a/test/compilable/extra-files/header2.d b/test/compilable/extra-files/header2.d index b0d8019e2c4d..f211c784c433 100644 --- a/test/compilable/extra-files/header2.d +++ b/test/compilable/extra-files/header2.d @@ -139,3 +139,13 @@ void test13275() foreach (shared (int) e; [1,2]) {} foreach (shared const(int) e; [1,2]) {} } + +// 9766 +align (1) struct S9766 +{ +align (true ? 2 : 3): + int var1; + +align: + int var2; +} diff --git a/test/compilable/extra-files/header2.di b/test/compilable/extra-files/header2.di index d35bf91e0032..d729b6c8a275 100644 --- a/test/compilable/extra-files/header2.di +++ b/test/compilable/extra-files/header2.di @@ -97,3 +97,11 @@ void foo11217()(inout int[] arr) { } void test13275(); +align (1) struct S9766 +{ + align (true ? 2 : 3) + { + int var1; + align int var2; + } +} diff --git a/test/compilable/extra-files/header2i.di b/test/compilable/extra-files/header2i.di index dbc88fe4dc31..c578dbc27e7e 100644 --- a/test/compilable/extra-files/header2i.di +++ b/test/compilable/extra-files/header2i.di @@ -199,3 +199,11 @@ void test13275() { } } +align (1) struct S9766 +{ + align (true ? 2 : 3) + { + int var1; + align int var2; + } +} diff --git a/test/compilable/test9766.d b/test/compilable/test9766.d new file mode 100644 index 000000000000..3cfc22f0464c --- /dev/null +++ b/test/compilable/test9766.d @@ -0,0 +1,77 @@ +// PERMUTE_ARGS: + +size_t getAlign9766(size_t n) { return n; } + +struct S9766 +{ +align(getAlign9766(1)): + ubyte[5] pad1; + ubyte var1; + +align(getAlign9766(2)): + ubyte[5] pad2; + ubyte var2; + +align(getAlign9766(4)): + ubyte[5] pad3; + ubyte var3; + +align(getAlign9766(8)): + ubyte[5] pad4; + ubyte var4; +} + +static assert(S9766.pad1.offsetof == 0); +static assert(S9766.var1.offsetof == 5); + +static assert(S9766.pad2.offsetof == 6); +static assert(S9766.var2.offsetof == 12); + +static assert(S9766.pad3.offsetof == 16); +static assert(S9766.var3.offsetof == 24); + +static assert(S9766.pad4.offsetof == 32); +static assert(S9766.var4.offsetof == 40); + +union U9766 +{ + struct + { + align(getAlign9766(1)): + ubyte[5] pad1; + ubyte var1; + + align(getAlign9766(2)): + ubyte[5] pad2; + ubyte var2; + + align(getAlign9766(4)): + ubyte[5] pad3; + ubyte var3; + + align(getAlign9766(8)): + ubyte[5] pad4; + ubyte var4; + } +} + +static assert(U9766.pad1.offsetof == 0); +static assert(U9766.var1.offsetof == 5); + +static assert(U9766.pad2.offsetof == 6); +static assert(U9766.var2.offsetof == 12); + +static assert(U9766.pad3.offsetof == 16); +static assert(U9766.var3.offsetof == 24); + +static assert(U9766.pad4.offsetof == 32); +static assert(U9766.var4.offsetof == 40); + +struct TestMaxAlign +{ +align(1u << 31): + ubyte a; + ubyte b; +} + +static assert(TestMaxAlign.b.offsetof == 2147483648u); diff --git a/test/fail_compilation/fail9766.d b/test/fail_compilation/fail9766.d new file mode 100644 index 000000000000..394333712733 --- /dev/null +++ b/test/fail_compilation/fail9766.d @@ -0,0 +1,27 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail9766.d(14): Error: positive integer expected, not Foo!int +fail_compilation/fail9766.d(17): Error: positive integer expected, not -1 +fail_compilation/fail9766.d(20): Error: positive integer expected, not 0 +fail_compilation/fail9766.d(23): Error: alignment must be a power of 2, not 3 +fail_compilation/fail9766.d(26): Error: alignment must be a power of 2, not 2147483649 +--- +*/ + +template Foo(T) {} + +align(Foo!int) +struct S9766a {} + +align(-1) +struct S9766b {} + +align(0) +struct S9766c {} + +align(3) +struct S9766d {} + +align((1u << 31) + 1) +struct S9766e {} diff --git a/test/fail_compilation/parseStc2.d b/test/fail_compilation/parseStc2.d index 840024a3472e..24bc822026e8 100644 --- a/test/fail_compilation/parseStc2.d +++ b/test/fail_compilation/parseStc2.d @@ -55,9 +55,9 @@ TEST_OUTPUT: --- fail_compilation/parseStc2.d(63): Error: redundant alignment attribute align fail_compilation/parseStc2.d(64): Error: redundant alignment attribute align(1) -fail_compilation/parseStc2.d(65): Error: conflicting alignment attribute align and align(1) -fail_compilation/parseStc2.d(66): Error: conflicting alignment attribute align(1) and align -fail_compilation/parseStc2.d(67): Error: conflicting alignment attribute align(1) and align(2) +fail_compilation/parseStc2.d(65): Error: redundant alignment attribute align(1) +fail_compilation/parseStc2.d(66): Error: redundant alignment attribute align +fail_compilation/parseStc2.d(67): Error: redundant alignment attribute align(2) --- */ align align void f11() {}