Mercurial > projects > ddmd
view dmd/StructDeclaration.d @ 98:5c859d5fbe27
and more
author | Trass3r |
---|---|
date | Tue, 31 Aug 2010 03:53:49 +0200 |
parents | ae5b11064a9a |
children | e6090d1aea7c |
line wrap: on
line source
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(); /// } override Dsymbol syntaxCopy(Dsymbol s) { StructDeclaration sd; if (s) sd = cast(StructDeclaration)s; else sd = new StructDeclaration(loc, ident); ScopeDsymbol.syntaxCopy(sd); return sd; } override void semantic(Scope sc) { 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; foreach(Dsymbol s; members) { //printf("adding member '%s' to '%s'\n", s.toChars(), this.toChars()); s.addMember(sc, this, true); 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(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; foreach(Dsymbol s; members) { 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); auto args = new Expressions(); args.push(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(fdptr); fdptr.addMember(sc, ss, true); 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 : sc.clone(); 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; foreach (VarDeclaration vd; fields) { 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); } } override void toCBuffer(OutBuffer buf, HdrGenState* hgs) { assert(false); } override string mangle() { //printf("StructDeclaration.mangle() '%s'\n", toChars()); return Dsymbol.mangle(); } override 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. */ foreach (VarDeclaration v; fields) { 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"); foreach (VarDeclaration v; fields) { 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(fop); fop.addMember(sc, this, true); 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; foreach (VarDeclaration v; fields) { 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"); auto dd = new PostBlitDeclaration(Loc(0), Loc(0), Lexer.idPool("__fieldPostBlit")); dd.fbody = new ExpStatement(Loc(0), e); postblits.shift(dd); members.push(dd); dd.semantic(sc); } switch (postblits.dim) { case 0: return null; case 1: return cast(FuncDeclaration)postblits[0]; default: e = null; foreach(FuncDeclaration fd; postblits) { 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); } auto dd = new PostBlitDeclaration(Loc(0), Loc(0), Lexer.idPool("__aggrPostBlit")); dd.fbody = new ExpStatement(Loc(0), e); members.push(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(fcp); sc = sc.push(); sc.stc = STC.STCundefined; sc.linkage = LINK.LINKd; fcp.semantic(sc); sc.pop(); } return fcp; } override void toDocBuffer(OutBuffer buf) { assert(false); } override PROT getAccess(Dsymbol smember) // determine access to smember { assert(false); } override 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 (0 && // causes multiple def problems with COMMON in one file and COMDAT in library 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 foreach(Dsymbol member; members) member.toObjFile(0); } } void toDt(dt_t** pdt) { uint offset; dt_t* dt; //printf("StructDeclaration.toDt(), this='%s'\n", toChars()); offset = 0; // Note equivalence of this loop to class's for (uint i = 0; i < fields.dim; i++) { VarDeclaration v = cast(VarDeclaration)fields[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); } override StructDeclaration isStructDeclaration() { return this; } }