Mercurial > projects > ddmd
diff dmd/StructDeclaration.d @ 0:10317f0c89a5
Initial commit
author | korDen |
---|---|
date | Sat, 24 Oct 2009 08:42:06 +0400 |
parents | |
children | 427f8aa74d28 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmd/StructDeclaration.d Sat Oct 24 08:42:06 2009 +0400 @@ -0,0 +1,851 @@ +module dmd.StructDeclaration; + +import dmd.AggregateDeclaration; +import dmd.FuncDeclaration; +import dmd.DeclarationExp; +import dmd.VoidInitializer; +import dmd.Initializer; +import dmd.ExpInitializer; +import dmd.TOK; +import dmd.Statement; +import dmd.VarExp; +import dmd.CompoundStatement; +import dmd.AssignExp; +import dmd.DotVarExp; +import dmd.AddrExp; +import dmd.CastExp; +import dmd.PostBlitDeclaration; +import dmd.Lexer; +import dmd.ExpStatement; +import dmd.DotIdExp; +import dmd.TypeSArray; +import dmd.ThisExp; +import dmd.ThisDeclaration; +import dmd.TypeFunction; +import dmd.Argument; +import dmd.Id; +import dmd.TY; +import dmd.LINK; +import dmd.Type; +import dmd.DsymbolTable; +import dmd.ArrayTypes; +import dmd.Loc; +import dmd.STC; +import dmd.Identifier; +import dmd.TemplateInstance; +import dmd.Dsymbol; +import dmd.Scope; +import dmd.OutBuffer; +import dmd.HdrGenState; +import dmd.PROT; +import dmd.TypeStruct; +import dmd.expression.Util; +import dmd.Expression; +import dmd.IdentifierExp; +import dmd.PtrExp; +import dmd.CallExp; +import dmd.ReturnStatement; +import dmd.ScopeDsymbol; +import dmd.Module; +import dmd.VarDeclaration; +import dmd.InvariantDeclaration; +import dmd.NewDeclaration; +import dmd.DeleteDeclaration; +import dmd.Global; + +import dmd.backend.dt_t; +import dmd.backend.Util; +import dmd.backend.SC; +import dmd.backend.DT; +import dmd.backend.FL; +import dmd.backend.glue; + +class StructDeclaration : AggregateDeclaration +{ + bool zeroInit; // true if initialize with 0 fill + +version (DMDV2) { + int hasIdentityAssign; // !=0 if has identity opAssign + FuncDeclaration cpctor; // generated copy-constructor, if any + + FuncDeclarations postblits; // Array of postblit functions + FuncDeclaration postblit; // aggregate postblit +} + + this(Loc loc, Identifier id) + { + super(loc, id); + + // For forward references + type = new TypeStruct(this); + + postblits = new FuncDeclarations(); /// + } + + Dsymbol syntaxCopy(Dsymbol s) + { + assert(false); + } + + void semantic(Scope sc) + { + int i; + Scope sc2; + + //printf("+StructDeclaration.semantic(this=%p, '%s')\n", this, toChars()); + + //static int count; if (++count == 20) *(char*)0=0; + + assert(type); + if (!members) // if forward reference + return; + + if (symtab) + { if (!scope_) + return; // semantic() already completed + } + else + symtab = new DsymbolTable(); + + Scope scx = null; + if (scope_) + { sc = scope_; + scx = scope_; // save so we don't make redundant copies + scope_ = null; + } + + parent = sc.parent; + type = type.semantic(loc, sc); +version (STRUCTTHISREF) { + handle = type; +} else { + handle = type.pointerTo(); +} + structalign = sc.structalign; + protection = sc.protection; + storage_class |= sc.stc; + if (sc.stc & STC.STCdeprecated) + isdeprecated = 1; + assert(!isAnonymous()); + if (sc.stc & STC.STCabstract) + error("structs, unions cannot be abstract"); +version (DMDV2) { + if (storage_class & STC.STCimmutable) + type = type.invariantOf(); + else if (storage_class & STC.STCconst) + type = type.constOf(); + else if (storage_class & STC.STCshared) + type = type.sharedOf(); +} + + if (sizeok == 0) // if not already done the addMember step + { + int hasfunctions = 0; + for (i = 0; i < members.dim; i++) + { + Dsymbol s = cast(Dsymbol)members.data[i]; + //printf("adding member '%s' to '%s'\n", s.toChars(), this.toChars()); + s.addMember(sc, this, 1); + if (s.isFuncDeclaration()) + hasfunctions = 1; + } + + // If nested struct, add in hidden 'this' pointer to outer scope + if (hasfunctions && !(storage_class & STC.STCstatic)) + { Dsymbol s = toParent2(); + if (s) + { + AggregateDeclaration ad = s.isAggregateDeclaration(); + FuncDeclaration fd = s.isFuncDeclaration(); + + TemplateInstance ti; + if (ad && (ti = ad.parent.isTemplateInstance()) !is null && ti.isnested || fd) + { isnested = true; + Type t; + if (ad) + t = ad.handle; + else if (fd) + { + AggregateDeclaration add = fd.isMember2(); + if (add) + t = add.handle; + else + t = Type.tvoidptr; + } + else + assert(0); + if (t.ty == TY.Tstruct) + t = Type.tvoidptr; // t should not be a ref type + assert(!vthis); + vthis = new ThisDeclaration(loc, t); + //vthis.storage_class |= STC.STCref; + members.push(cast(void*)vthis); + } + } + } + } + + sizeok = 0; + sc2 = sc.push(this); + sc2.stc &= storage_class & STC.STC_TYPECTOR; + sc2.parent = this; + if (isUnionDeclaration()) + sc2.inunion = 1; + sc2.protection = PROT.PROTpublic; + sc2.explicitProtection = 0; + + int members_dim = members.dim; + for (i = 0; i < members_dim; i++) + { + Dsymbol s = cast(Dsymbol)members.data[i]; + s.semantic(sc2); + if (isUnionDeclaration()) + sc2.offset = 0; +static if (false) { + if (sizeok == 2) + { //printf("forward reference\n"); + break; + } +} + Type t; + if (s.isDeclaration() && + (t = s.isDeclaration().type) !is null && + t.toBasetype().ty == TY.Tstruct) + { StructDeclaration sd = cast(StructDeclaration)t.toDsymbol(sc); + if (sd.isnested) + error("inner struct %s cannot be a field", sd.toChars()); + } + } + + /* The TypeInfo_Struct is expecting an opEquals and opCmp with + * a parameter that is a pointer to the struct. But if there + * isn't one, but is an opEquals or opCmp with a value, write + * another that is a shell around the value: + * int opCmp(struct *p) { return opCmp(*p); } + */ + + TypeFunction tfeqptr; + { + Arguments arguments = new Arguments; + Argument arg = new Argument(STC.STCin, handle, Id.p, null); + + arguments.push(cast(void*)arg); + tfeqptr = new TypeFunction(arguments, Type.tint32, 0, LINK.LINKd); + tfeqptr = cast(TypeFunction)tfeqptr.semantic(Loc(0), sc); + } + + TypeFunction tfeq; + { + Arguments arguments = new Arguments; + Argument arg = new Argument(STC.STCin, type, null, null); + + arguments.push(cast(void*)arg); + tfeq = new TypeFunction(arguments, Type.tint32, 0, LINK.LINKd); + tfeq = cast(TypeFunction)tfeq.semantic(Loc(0), sc); + } + + Identifier id = Id.eq; + for (int j = 0; j < 2; j++) + { + Dsymbol s = search_function(this, id); + FuncDeclaration fdx = s ? s.isFuncDeclaration() : null; + if (fdx) + { FuncDeclaration fd = fdx.overloadExactMatch(tfeqptr); + if (!fd) + { fd = fdx.overloadExactMatch(tfeq); + if (fd) + { // Create the thunk, fdptr + FuncDeclaration fdptr = new FuncDeclaration(loc, loc, fdx.ident, STC.STCundefined, tfeqptr); + Expression e = new IdentifierExp(loc, Id.p); + e = new PtrExp(loc, e); + Expressions args = new Expressions(); + args.push(cast(void*)e); + e = new IdentifierExp(loc, id); + e = new CallExp(loc, e, args); + fdptr.fbody = new ReturnStatement(loc, e); + ScopeDsymbol ss = fdx.parent.isScopeDsymbol(); + assert(ss); + ss.members.push(cast(void*)fdptr); + fdptr.addMember(sc, ss, 1); + fdptr.semantic(sc2); + } + } + } + + id = Id.cmp; + } +version (DMDV2) { + dtor = buildDtor(sc2); + postblit = buildPostBlit(sc2); + cpctor = buildCpCtor(sc2); + buildOpAssign(sc2); +} + + sc2.pop(); + + if (sizeok == 2) + { // semantic() failed because of forward references. + // Unwind what we did, and defer it for later + fields.setDim(0); + structsize = 0; + alignsize = 0; + structalign = 0; + + scope_ = scx ? scx : new Scope(sc); + scope_.setNoFree(); + scope_.module_.addDeferredSemantic(this); + //printf("\tdeferring %s\n", toChars()); + return; + } + + // 0 sized struct's are set to 1 byte + if (structsize == 0) + { + structsize = 1; + alignsize = 1; + } + + // Round struct size up to next alignsize boundary. + // This will ensure that arrays of structs will get their internals + // aligned properly. + structsize = (structsize + alignsize - 1) & ~(alignsize - 1); + + sizeok = 1; + Module.dprogress++; + + //printf("-StructDeclaration.semantic(this=%p, '%s')\n", this, toChars()); + + // Determine if struct is all zeros or not + zeroInit = true; + for (i = 0; i < fields.dim; i++) + { + Dsymbol s = cast(Dsymbol)fields.data[i]; + VarDeclaration vd = s.isVarDeclaration(); + if (vd && !vd.isDataseg()) + { + if (vd.init) + { + // Should examine init to see if it is really all 0's + zeroInit = false; + break; + } + else + { + if (!vd.type.isZeroInit(loc)) + { + zeroInit = false; + break; + } + } + } + } + + /* Look for special member functions. + */ +version (DMDV2) { + ctor = search(Loc(0), Id.ctor, 0); +} + inv = cast(InvariantDeclaration)search(Loc(0), Id.classInvariant, 0); + aggNew = cast(NewDeclaration)search(Loc(0), Id.classNew, 0); + aggDelete = cast(DeleteDeclaration)search(Loc(0), Id.classDelete, 0); + + if (sc.func) + { + semantic2(sc); + semantic3(sc); + } + } + + void toCBuffer(OutBuffer buf, HdrGenState* hgs) + { + assert(false); + } + + string mangle() + { + //printf("StructDeclaration.mangle() '%s'\n", toChars()); + return Dsymbol.mangle(); + } + + string kind() + { + assert(false); + } + + /******************************************* + * We need an opAssign for the struct if + * it has a destructor or a postblit. + * We need to generate one if a user-specified one does not exist. + */ + bool needOpAssign() + { +static if (false) { + printf("StructDeclaration.needOpAssign() %s\n", toChars()); +} + if (hasIdentityAssign) + goto Ldontneed; + + if (dtor || postblit) + goto Lneed; + + /* If any of the fields need an opAssign, then we + * need it too. + */ + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol s = cast(Dsymbol)fields.data[i]; + VarDeclaration v = s.isVarDeclaration(); + assert(v && v.storage_class & STC.STCfield); + if (v.storage_class & STC.STCref) + continue; + Type tv = v.type.toBasetype(); + while (tv.ty == TY.Tsarray) + { TypeSArray ta = cast(TypeSArray)tv; + tv = tv.nextOf().toBasetype(); + } + if (tv.ty == TY.Tstruct) + { TypeStruct ts = cast(TypeStruct)tv; + StructDeclaration sd = ts.sym; + if (sd.needOpAssign()) + goto Lneed; + } + } + Ldontneed: +static if (false) { + printf("\tdontneed\n"); +} + return false; + + Lneed: +static if (false) { + printf("\tneed\n"); +} + return true; + } + + /****************************************** + * Build opAssign for struct. + * S* opAssign(S s) { ... } + */ + FuncDeclaration buildOpAssign(Scope sc) + { + if (!needOpAssign()) + return null; + + //printf("StructDeclaration.buildOpAssign() %s\n", toChars()); + + FuncDeclaration fop = null; + + Argument param = new Argument(STC.STCnodtor, type, Id.p, null); + Arguments fparams = new Arguments; + fparams.push(cast(void*)param); + Type ftype = new TypeFunction(fparams, handle, false, LINK.LINKd); +version (STRUCTTHISREF) { + (cast(TypeFunction)ftype).isref = 1; +} + + fop = new FuncDeclaration(Loc(0), Loc(0), Id.assign, STC.STCundefined, ftype); + + Expression e = null; + if (postblit) + { /* Swap: + * tmp = *this; *this = s; tmp.dtor(); + */ + //printf("\tswap copy\n"); + Identifier idtmp = Lexer.uniqueId("__tmp"); + VarDeclaration tmp; + AssignExp ec = null; + if (dtor) + { + tmp = new VarDeclaration(Loc(0), type, idtmp, new VoidInitializer(Loc(0))); + tmp.noauto = true; + e = new DeclarationExp(Loc(0), tmp); + + Expression e2; +version (STRUCTTHISREF) { + e2 = new ThisExp(Loc(0)); +} else { + e2 = new PtrExp(Loc(0), new ThisExp(Loc(0))); +} + ec = new AssignExp(Loc(0), new VarExp(Loc(0), tmp), e2); + ec.op = TOK.TOKblit; + e = Expression.combine(e, ec); + } + Expression e2; +version (STRUCTTHISREF) { + e2 = new ThisExp(Loc(0)); +} else { + e2 = new PtrExp(Loc(0), new ThisExp(Loc(0))); +} + + ec = new AssignExp(Loc(0), e2, new IdentifierExp(Loc(0), Id.p)); + ec.op = TOK.TOKblit; + e = Expression.combine(e, ec); + if (dtor) + { + /* Instead of running the destructor on s, run it + * on tmp. This avoids needing to copy tmp back in to s. + */ + Expression ecc = new DotVarExp(Loc(0), new VarExp(Loc(0), tmp), dtor, 0); + ecc = new CallExp(Loc(0), ecc); + e = Expression.combine(e, ecc); + } + } + else + { /* Do memberwise copy + */ + //printf("\tmemberwise copy\n"); + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol s = cast(Dsymbol)fields.data[i]; + VarDeclaration v = s.isVarDeclaration(); + assert(v && v.storage_class & STC.STCfield); + // this.v = s.v; + AssignExp ec = new AssignExp(Loc(0), new DotVarExp(Loc(0), new ThisExp(Loc(0)), v, 0), new DotVarExp(Loc(0), new IdentifierExp(Loc(0), Id.p), v, 0)); + ec.op = TOK.TOKblit; + e = Expression.combine(e, ec); + } + } + Statement s1 = new ExpStatement(Loc(0), e); + + /* Add: + * return this; + */ + e = new ThisExp(Loc(0)); + Statement s2 = new ReturnStatement(Loc(0), e); + + fop.fbody = new CompoundStatement(Loc(0), s1, s2); + + members.push(cast(void*)fop); + fop.addMember(sc, this, 1); + + sc = sc.push(); + sc.stc = STC.STCundefined; + sc.linkage = LINK.LINKd; + + fop.semantic(sc); + + sc.pop(); + + //printf("-StructDeclaration.buildOpAssign() %s\n", toChars()); + + return fop; + } + + /***************************************** + * Create inclusive postblit for struct by aggregating + * all the postblits in postblits[] with the postblits for + * all the members. + * Note the close similarity with AggregateDeclaration.buildDtor(), + * and the ordering changes (runs forward instead of backwards). + */ + +version (DMDV2) { + FuncDeclaration buildPostBlit(Scope sc) + { + //printf("StructDeclaration.buildPostBlit() %s\n", toChars()); + Expression e = null; + + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol s = cast(Dsymbol)fields.data[i]; + VarDeclaration v = s.isVarDeclaration(); + assert(v && v.storage_class & STC.STCfield); + if (v.storage_class & STC.STCref) + continue; + Type tv = v.type.toBasetype(); + size_t dim = 1; + while (tv.ty == TY.Tsarray) + { TypeSArray ta = cast(TypeSArray)tv; + dim *= (cast(TypeSArray)tv).dim.toInteger(); + tv = tv.nextOf().toBasetype(); + } + if (tv.ty == TY.Tstruct) + { TypeStruct ts = cast(TypeStruct)tv; + StructDeclaration sd = ts.sym; + if (sd.postblit) + { Expression ex; + + // this.v + ex = new ThisExp(Loc(0)); + ex = new DotVarExp(Loc(0), ex, v, 0); + + if (dim == 1) + { // this.v.postblit() + ex = new DotVarExp(Loc(0), ex, sd.postblit, 0); + ex = new CallExp(Loc(0), ex); + } + else + { + // Typeinfo.postblit(cast(void*)&this.v); + Expression ea = new AddrExp(Loc(0), ex); + ea = new CastExp(Loc(0), ea, Type.tvoid.pointerTo()); + + Expression et = v.type.getTypeInfo(sc); + et = new DotIdExp(Loc(0), et, Id.postblit); + + ex = new CallExp(Loc(0), et, ea); + } + e = Expression.combine(e, ex); // combine in forward order + } + } + } + + /* Build our own "postblit" which executes e + */ + if (e) + { //printf("Building __fieldPostBlit()\n"); + PostBlitDeclaration dd = new PostBlitDeclaration(Loc(0), Loc(0), Lexer.idPool("__fieldPostBlit")); + dd.fbody = new ExpStatement(Loc(0), e); + postblits.shift(cast(void*)dd); + members.push(cast(void*)dd); + dd.semantic(sc); + } + + switch (postblits.dim) + { + case 0: + return null; + + case 1: + return cast(FuncDeclaration)postblits.data[0]; + + default: + e = null; + for (size_t i = 0; i < postblits.dim; i++) + { FuncDeclaration fd = cast(FuncDeclaration)postblits.data[i]; + Expression ex = new ThisExp(Loc(0)); + ex = new DotVarExp(Loc(0), ex, fd, 0); + ex = new CallExp(Loc(0), ex); + e = Expression.combine(e, ex); + } + PostBlitDeclaration dd = new PostBlitDeclaration(Loc(0), Loc(0), Lexer.idPool("__aggrPostBlit")); + dd.fbody = new ExpStatement(Loc(0), e); + members.push(cast(void*)dd); + dd.semantic(sc); + return dd; + } + } +} + + /******************************************* + * Build copy constructor for struct. + * Copy constructors are compiler generated only, and are only + * callable from the compiler. They are not user accessible. + * A copy constructor is: + * void cpctpr(ref S s) + * { + * *this = s; + * this.postBlit(); + * } + * This is done so: + * - postBlit() never sees uninitialized data + * - memcpy can be much more efficient than memberwise copy + * - no fields are overlooked + */ + FuncDeclaration buildCpCtor(Scope sc) + { + //printf("StructDeclaration.buildCpCtor() %s\n", toChars()); + FuncDeclaration fcp = null; + + /* Copy constructor is only necessary if there is a postblit function, + * otherwise the code generator will just do a bit copy. + */ + if (postblit) + { + //printf("generating cpctor\n"); + + Argument param = new Argument(STC.STCref, type, Id.p, null); + Arguments fparams = new Arguments; + fparams.push(cast(void*)param); + Type ftype = new TypeFunction(fparams, Type.tvoid, false, LINK.LINKd); + + fcp = new FuncDeclaration(Loc(0), Loc(0), Id.cpctor, STC.STCundefined, ftype); + + // Build *this = p; + Expression e = new ThisExp(Loc(0)); +version (STRUCTTHISREF) { +} else { + e = new PtrExp(Loc(0), e); +} + AssignExp ea = new AssignExp(Loc(0), e, new IdentifierExp(Loc(0), Id.p)); + ea.op = TOK.TOKblit; + Statement s = new ExpStatement(Loc(0), ea); + + // Build postBlit(); + e = new VarExp(Loc(0), postblit, 0); + e = new CallExp(Loc(0), e); + + s = new CompoundStatement(Loc(0), s, new ExpStatement(Loc(0), e)); + fcp.fbody = s; + + members.push(cast(void*)fcp); + + sc = sc.push(); + sc.stc = STC.STCundefined; + sc.linkage = LINK.LINKd; + + fcp.semantic(sc); + + sc.pop(); + } + + return fcp; + } + + void toDocBuffer(OutBuffer buf) + { + assert(false); + } + + PROT getAccess(Dsymbol smember) // determine access to smember + { + assert(false); + } + + void toObjFile(int multiobj) // compile to .obj file + { + //printf("StructDeclaration.toObjFile('%s')\n", toChars()); + + if (multiobj) + { + obj_append(this); + return; + } + + // Anonymous structs/unions only exist as part of others, + // do not output forward referenced structs's + if (!isAnonymous() && members) + { + if (global.params.symdebug) { + toDebug(); + } + + type.getTypeInfo(null); // generate TypeInfo + + if (true) + { + // Generate static initializer + toInitializer(); + +static if (false) { + sinit.Sclass = SC.SCcomdat; +} else { + if (inTemplateInstance()) + { + sinit.Sclass = SC.SCcomdat; + } + else + { + sinit.Sclass = SC.SCglobal; + } +} + sinit.Sfl = FL.FLdata; + + toDt(&sinit.Sdt); + +version (OMFOBJ) { + /* For OMF, common blocks aren't pulled in from the library. + */ + /* ELF comdef's generate multiple + * definition errors for them from the gnu linker. + * Need to figure out how to generate proper comdef's for ELF. + */ + // See if we can convert a comdat to a comdef, + // which saves on exe file space. + if (sinit.Sclass == SCcomdat && + sinit.Sdt && + sinit.Sdt.dt == DT.DT_azeros && + sinit.Sdt.DTnext == null && + !global.params.multiobj) + { + sinit.Sclass = SC.SCglobal; + sinit.Sdt.dt = DT.DT_common; + } +} + +version (ELFOBJ) { + sinit.Sseg = Segment.CDATA; +} +version (MACHOBJ) { + sinit.Sseg = Segment.DATA; +} + outdata(sinit); + } + + // Put out the members + for (uint i = 0; i < members.dim; i++) + { + Dsymbol member = cast(Dsymbol)members.data[i]; + member.toObjFile(0); + } + } + } + + void toDt(dt_t** pdt) + { + uint offset; + uint i; + dt_t* dt; + + //printf("StructDeclaration.toDt(), this='%s'\n", toChars()); + offset = 0; + + // Note equivalence of this loop to class's + for (i = 0; i < fields.dim; i++) + { + VarDeclaration v = cast(VarDeclaration)fields.data[i]; + //printf("\tfield '%s' voffset %d, offset = %d\n", v.toChars(), v.offset, offset); + dt = null; + int sz; + + if (v.storage_class & STC.STCref) + { + sz = PTRSIZE; + if (v.offset >= offset) + dtnzeros(&dt, sz); + } + else + { + sz = cast(uint)v.type.size(); + Initializer init = v.init; + if (init) + { + //printf("\t\thas initializer %s\n", init.toChars()); + ExpInitializer ei = init.isExpInitializer(); + Type tb = v.type.toBasetype(); + if (ei && tb.ty == TY.Tsarray) + (cast(TypeSArray)tb).toDtElem(&dt, ei.exp); + else + dt = init.toDt(); + } + else if (v.offset >= offset) + v.type.toDt(&dt); + } + if (dt) + { + if (v.offset < offset) + error("overlapping initialization for struct %s.%s", toChars(), v.toChars()); + else + { + if (offset < v.offset) + dtnzeros(pdt, v.offset - offset); + dtcat(pdt, dt); + offset = v.offset + sz; + } + } + } + + if (offset < structsize) + dtnzeros(pdt, structsize - offset); + + dt_optimize(*pdt); + } + + void toDebug() // to symbolic debug info + { + assert(false); + } + + StructDeclaration isStructDeclaration() { return this; } +} \ No newline at end of file