diff --git a/dmd/declaration.d b/dmd/declaration.d index fcc3a19814..b0c0d43e69 100644 --- a/dmd/declaration.d +++ b/dmd/declaration.d @@ -1710,6 +1710,19 @@ version (IN_LLVM) * This is a shell around a back end symbol */ extern (C++) final class SymbolDeclaration : Declaration +{ +version (IN_LLVM) +{ + AggregateDeclaration dsym; + + extern (D) this(const ref Loc loc, AggregateDeclaration dsym) + { + super(loc, dsym.ident); + this.dsym = dsym; + storage_class |= STC.const_; + } +} +else { StructDeclaration dsym; @@ -1719,6 +1732,7 @@ extern (C++) final class SymbolDeclaration : Declaration this.dsym = dsym; storage_class |= STC.const_; } +} // Eliminate need for dynamic_cast override inout(SymbolDeclaration) isSymbolDeclaration() inout diff --git a/dmd/declaration.h b/dmd/declaration.h index a22181cd66..2fcf764e56 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -273,7 +273,11 @@ public: class SymbolDeclaration : public Declaration { public: +#if IN_LLVM + AggregateDeclaration *dsym; +#else StructDeclaration *dsym; +#endif // Eliminate need for dynamic_cast SymbolDeclaration *isSymbolDeclaration() { return (SymbolDeclaration *)this; } diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index d5527aec18..eb4daf5886 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -2139,6 +2139,12 @@ public: } else if (SymbolDeclaration s = d.isSymbolDeclaration()) { +version (IN_LLVM) +{ + // exclude void[]-typed `__traits(initSymbol)` (LDC extension) + if (s.type.toBasetype().ty != Tstruct) + return CTFEExp.cantexp; +} // Struct static initializers, for example e = s.dsym.type.defaultInitLiteral(loc); if (e.op == TOK.error) diff --git a/dmd/id.d b/dmd/id.d index f147a9c9b8..0f4e8d93d3 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -520,7 +520,8 @@ immutable Msgtable[] msgtable = // IN_LLVM: LDC-specific traits. { "targetCPU" }, { "targetHasFeature" }, - + { "initSymbol" }, + // IN_LLVM: LDC-specific attributes { "ldc" }, { "attributes" }, diff --git a/dmd/traits.d b/dmd/traits.d index e10f1a3114..74f7dfa2b2 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -1922,6 +1922,25 @@ else } version (IN_LLVM) { + if (e.ident == Id.initSymbol) + { + if (dim != 1) + return dimError(1); + + auto o = (*e.args)[0]; + Type t = isType(o); + AggregateDeclaration ad = t ? isAggregate(t) : null; + if (!ad) + { + e.error("aggregate type expected as argument to __traits(initSymbol)"); + return ErrorExp.get(); + } + + Declaration d = new SymbolDeclaration(ad.loc, ad); + d.type = Type.tvoid.arrayOf().constOf(); + d.storage_class |= STC.rvalue; + return new VarExp(ad.loc, d); + } if (Expression ret = semanticTraitsHook(e, sc)) { return ret; diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index e4d2b2a728..3cb3f75e51 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1573,23 +1573,32 @@ DValue *DtoSymbolAddress(const Loc &loc, Type *type, Declaration *decl) { } if (SymbolDeclaration *sdecl = decl->isSymbolDeclaration()) { - // this seems to be the static initialiser for structs - Type *sdecltype = sdecl->type->toBasetype(); - IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars()); - assert(sdecltype->ty == Tstruct); - TypeStruct *ts = static_cast(sdecltype); - StructDeclaration *sd = ts->sym; - assert(sd); - DtoResolveStruct(sd); + // this is the static initialiser (init symbol) for aggregates + AggregateDeclaration *ad = sdecl->dsym; + IF_LOG Logger::print("Sym: ad=%s\n", ad->toChars()); + DtoResolveDsymbol(ad); + auto sd = ad->isStructDeclaration(); + // LDC extension: void[]-typed `__traits(initSymbol)`, for classes too + auto tb = sdecl->type->toBasetype(); + if (tb->ty != Tstruct) { + assert(tb->ty == Tarray && tb->nextOf()->ty == Tvoid); + const auto size = DtoConstSize_t(ad->structsize); + llvm::Constant *ptr = + sd && sd->zeroInit + ? getNullValue(getVoidPtrType()) + : DtoBitCast(getIrAggr(ad)->getInitSymbol(), getVoidPtrType()); + return new DSliceValue(type, size, ptr); + } + + assert(sd); if (sd->zeroInit) { error(loc, "no init symbol for zero-initialized struct"); fatal(); } LLValue *initsym = getIrAggr(sd)->getInitSymbol(); - initsym = DtoBitCast(initsym, DtoType(ts->pointerTo())); - return new DLValue(type, initsym); + return new DLValue(type, DtoBitCast(initsym, DtoPtrToType(sd->type))); } llvm_unreachable("Unimplemented VarExp type"); diff --git a/gen/toconstelem.cpp b/gen/toconstelem.cpp index 6d802165a0..2d57494f51 100644 --- a/gen/toconstelem.cpp +++ b/gen/toconstelem.cpp @@ -71,14 +71,16 @@ public: LOG_SCOPE; if (SymbolDeclaration *sdecl = e->var->isSymbolDeclaration()) { - // this seems to be the static initialiser for structs - Type *sdecltype = sdecl->type->toBasetype(); - IF_LOG Logger::print("Sym: type=%s\n", sdecltype->toChars()); - assert(sdecltype->ty == Tstruct); - TypeStruct *ts = static_cast(sdecltype); - DtoResolveStruct(ts->sym); - result = getIrAggr(ts->sym)->getDefaultInit(); - return; + // This is the static initialiser (init symbol) for aggregates. + // Exclude void[]-typed `__traits(initSymbol)` (LDC extension). + if (sdecl->type->toBasetype()->ty == Tstruct) { + const auto sd = sdecl->dsym->isStructDeclaration(); + assert(sd); + IF_LOG Logger::print("Sym: sd=%s\n", sd->toChars()); + DtoResolveStruct(sd); + result = getIrAggr(sd)->getDefaultInit(); + return; + } } if (TypeInfoDeclaration *ti = e->var->isTypeInfoDeclaration()) { diff --git a/gen/toir.cpp b/gen/toir.cpp index b8b93bbc93..50b0b6d446 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -2755,13 +2755,11 @@ bool toInPlaceConstruction(DLValue *lhs, Expression *rhs) { // to initialize a `S[1]` lhs with a `S` rhs. if (auto ve = rhs->isVarExp()) { if (auto symdecl = ve->var->isSymbolDeclaration()) { - Type *t = symdecl->type->toBasetype(); - if (auto ts = t->isTypeStruct()) { - // this is the static initializer for a struct (init symbol) - StructDeclaration *sd = ts->sym; + // exclude void[]-typed `__traits(initSymbol)` (LDC extension) + if (symdecl->type->toBasetype()->ty == Tstruct) { + auto sd = symdecl->dsym->isStructDeclaration(); assert(sd); DtoResolveStruct(sd); - if (sd->zeroInit) { Logger::println("success, zeroing out"); DtoMemSetZero(DtoLVal(lhs)); diff --git a/tests/semantic/traits_initSymbol.d b/tests/semantic/traits_initSymbol.d new file mode 100644 index 0000000000..9e17ceb170 --- /dev/null +++ b/tests/semantic/traits_initSymbol.d @@ -0,0 +1,30 @@ +// RUN: %ldc -run %s + +struct Zero { int x; } + +struct NonZero { long x = 1; } + +class C { short x = 123; } + +void main() +{ + auto zeroInit = __traits(initSymbol, Zero); + static assert(is(typeof(zeroInit) == const(void[]))); + assert(zeroInit.ptr is null && zeroInit.length == Zero.sizeof); + + auto nonZeroInit = __traits(initSymbol, NonZero); + static assert(is(typeof(nonZeroInit) == const(void[]))); + assert(nonZeroInit.ptr !is null && nonZeroInit.length == NonZero.sizeof); + assert(cast(const(long[])) nonZeroInit == [1L]); + + auto cInit = __traits(initSymbol, C); + static assert(is(typeof(cInit) == const(void[]))); + assert(cInit.ptr !is null && cInit.length == __traits(classInstanceSize, C)); + scope c = new C; + import core.stdc.string; + assert(memcmp(cast(void*) c, cInit.ptr, cInit.length) == 0); + + static assert(!__traits(compiles, __traits(initSymbol, int))); + static assert(!__traits(compiles, __traits(initSymbol, Zero[1]))); + static assert(!__traits(compiles, __traits(initSymbol, 123))); +}