/*************************************** * Return true if struct is POD (Plain Old Data). * This is defined as: * not nested * no postblits, constructors, destructors, or assignment operators * no fields with with any of those * The idea being these are compatible with C structs. * * Note that D struct constructors can mean POD, since there is always default * construction with no ctor, but that interferes with OPstrpar which wants it * on the stack in memory, not in registers. */ bool StructDeclaration::isPOD() { if (enclosing || cpctor || postblit || ctor || dtor) return false; /* Recursively check any fields have a constructor. * We should cache the results of this. */ for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->isField()); if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); while (tv->ty == Tsarray) { TypeSArray *ta = (TypeSArray *)tv; tv = tv->nextOf()->toBasetype(); } if (tv->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tv; StructDeclaration *sd = ts->sym; if (!sd->isPOD()) return false; } } return true; }
int StructDeclaration::needOpEquals() { #define X 0 if (X) printf("StructDeclaration::needOpEquals() %s\n", toChars()); if (hasIdentityEquals) goto Lneed; #if 0 if (isUnionDeclaration()) goto Ldontneed; #endif /* If any of the fields has an opEquals, then we * need it too. */ for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->isField()); if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); #if 0 if (tv->isfloating()) goto Lneed; if (tv->ty == Tarray) goto Lneed; if (tv->ty == Tclass) goto Lneed; #endif while (tv->ty == Tsarray) { TypeSArray *ta = (TypeSArray *)tv; tv = tv->nextOf()->toBasetype(); } if (tv->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tv; StructDeclaration *sd = ts->sym; if (sd->needOpEquals()) goto Lneed; } } Ldontneed: if (X) printf("\tdontneed\n"); return 0; Lneed: if (X) printf("\tneed\n"); return 1; #undef X }
/* Returns: * 0 this member doesn't need further processing to determine struct size * 1 this member does */ static int func(Dsymbol *s, void *param) { SV *psv = (SV *)param; VarDeclaration *v = s->isVarDeclaration(); if (v) { if (v->scope) v->semantic(NULL); if (v->storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCctfe | STCtemplateparameter)) return 0; if (v->isField() && v->sem >= SemanticDone) return 0; return 1; } return 0; }
int StructDeclaration::needOpAssign() { #define X 0 if (X) printf("StructDeclaration::needOpAssign() %s\n", toChars()); if (hasIdentityAssign) goto Lneed; // because has identity==elaborate opAssign 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 = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->isField()); if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); while (tv->ty == Tsarray) { TypeSArray *ta = (TypeSArray *)tv; tv = tv->nextOf()->toBasetype(); } if (tv->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tv; StructDeclaration *sd = ts->sym; if (sd->needOpAssign()) goto Lneed; } } Ldontneed: if (X) printf("\tdontneed\n"); return 0; Lneed: if (X) printf("\tneed\n"); return 1; #undef X }
/* Returns: * 0 this member doesn't need further processing to determine struct size * 1 this member does */ static int func(Dsymbol *s, void *param) { VarDeclaration *v = s->isVarDeclaration(); if (v) { /* Bugzilla 12799: enum a = ...; is a VarDeclaration and * STCmanifest is already set in parssing stage. So we can * check this before the semantic() call. */ if (v->storage_class & STCmanifest) return 0; if (v->scope) v->semantic(NULL); if (v->storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCctfe | STCtemplateparameter)) return 0; if (v->isField() && v->sem >= SemanticDone) return 0; return 1; } return 0; }
FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc) { //printf("AggregateDeclaration::buildDtor() %s\n", toChars()); Expression *e = NULL; #if DMDV2 for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->isField()); if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); dinteger_t dim = 1; while (tv->ty == Tsarray) { TypeSArray *ta = (TypeSArray *)tv; dim *= ((TypeSArray *)tv)->dim->toInteger(); tv = tv->nextOf()->toBasetype(); } if (tv->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tv; StructDeclaration *sd = ts->sym; if (sd->dtor && dim) { Expression *ex; // this.v ex = new ThisExp(0); ex = new DotVarExp(0, ex, v, 0); if (v->type->toBasetype()->ty == Tstruct) { // this.v.dtor() ex = new DotVarExp(0, ex, sd->dtor, 0); ex = new CallExp(0, ex); } else { // Typeinfo.destroy(cast(void*)&this.v); Expression *ea = new AddrExp(0, ex); ea = new CastExp(0, ea, Type::tvoid->pointerTo()); Expression *et = v->type->getTypeInfo(sc); et = new DotIdExp(0, et, Id::destroy); ex = new CallExp(0, et, ea); } e = Expression::combine(ex, e); // combine in reverse order } } } /* Build our own "destructor" which executes e */ if (e) { //printf("Building __fieldDtor()\n"); DtorDeclaration *dd = new DtorDeclaration(loc, 0, Lexer::idPool("__fieldDtor")); dd->fbody = new ExpStatement(0, e); dtors.shift(dd); members->push(dd); dd->semantic(sc); } #endif switch (dtors.dim) { case 0: return NULL; case 1: return dtors[0]; default: e = NULL; for (size_t i = 0; i < dtors.dim; i++) { FuncDeclaration *fd = dtors[i]; Expression *ex = new ThisExp(0); ex = new DotVarExp(0, ex, fd, 0); ex = new CallExp(0, ex); e = Expression::combine(ex, e); } DtorDeclaration *dd = new DtorDeclaration(loc, 0, Lexer::idPool("__aggrDtor")); dd->fbody = new ExpStatement(0, e); members->push(dd); dd->semantic(sc); return dd; } }
FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) { //printf("StructDeclaration::buildPostBlit() %s\n", toChars()); Expression *e = NULL; StorageClass stc = 0; for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->isField()); if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); dinteger_t dim = 1; while (tv->ty == Tsarray) { TypeSArray *ta = (TypeSArray *)tv; dim *= ((TypeSArray *)tv)->dim->toInteger(); tv = tv->nextOf()->toBasetype(); } if (tv->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tv; StructDeclaration *sd = ts->sym; if (sd->postblit && dim) { stc |= sd->postblit->storage_class & STCdisable; if (stc & STCdisable) { e = NULL; break; } // this.v Expression *ex = new ThisExp(0); ex = new DotVarExp(0, ex, v, 0); if (v->type->toBasetype()->ty == Tstruct) { // this.v.postblit() ex = new DotVarExp(0, ex, sd->postblit, 0); ex = new CallExp(0, ex); } else { // Typeinfo.postblit(cast(void*)&this.v); Expression *ea = new AddrExp(0, ex); ea = new CastExp(0, ea, Type::tvoid->pointerTo()); Expression *et = v->type->getTypeInfo(sc); et = new DotIdExp(0, et, Id::postblit); ex = new CallExp(0, et, ea); } e = Expression::combine(e, ex); // combine in forward order } } } /* Build our own "postblit" which executes e */ if (e || (stc & STCdisable)) { //printf("Building __fieldPostBlit()\n"); PostBlitDeclaration *dd = new PostBlitDeclaration(loc, 0, stc, Lexer::idPool("__fieldPostBlit")); dd->fbody = new ExpStatement(0, e); postblits.shift(dd); members->push(dd); dd->semantic(sc); } switch (postblits.dim) { case 0: return NULL; case 1: return postblits[0]; default: e = NULL; for (size_t i = 0; i < postblits.dim; i++) { FuncDeclaration *fd = postblits[i]; stc |= fd->storage_class & STCdisable; if (stc & STCdisable) { e = NULL; break; } Expression *ex = new ThisExp(0); ex = new DotVarExp(0, ex, fd, 0); ex = new CallExp(0, ex); e = Expression::combine(e, ex); } PostBlitDeclaration *dd = new PostBlitDeclaration(loc, 0, stc, Lexer::idPool("__aggrPostBlit")); dd->fbody = new ExpStatement(0, e); members->push(dd); dd->semantic(sc); return dd; } }
FuncDeclaration *StructDeclaration::buildOpEquals(Scope *sc) { Dsymbol *eq = search_function(this, Id::eq); if (eq) { /* check identity opEquals exists */ Type *tthis = type->constOf(); Expression *er = new NullExp(loc, tthis); // dummy rvalue Expression *el = new IdentifierExp(loc, Id::p); // dummy lvalue el->type = tthis; Expressions ar; ar.push(er); Expressions al; al.push(el); FuncDeclaration *f = NULL; unsigned errors = global.startGagging(); // Do not report errors, even if the unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it. global.speculativeGag = global.gag; sc = sc->push(); sc->speculative = true; f = resolveFuncCall(loc, sc, eq, NULL, er, &ar, 1); if (!f) f = resolveFuncCall(loc, sc, eq, NULL, er, &al, 1); sc = sc->pop(); global.speculativeGag = oldspec; global.endGagging(errors); if (f) return (f->storage_class & STCdisable) ? NULL : f; return NULL; } if (!needOpEquals()) return NULL; //printf("StructDeclaration::buildOpEquals() %s\n", toChars()); Parameters *parameters = new Parameters; parameters->push(new Parameter(STCin, type, Id::p, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); tf->mod = MODconst; tf = (TypeFunction *)tf->semantic(loc, sc); FuncDeclaration *fop = new FuncDeclaration(loc, 0, Id::eq, STCundefined, tf); Expression *e = NULL; /* Do memberwise compare */ //printf("\tmemberwise compare\n"); for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->isField()); if (v->storage_class & STCref) assert(0); // what should we do with this? // this.v == s.v; EqualExp *ec = new EqualExp(TOKequal, loc, new DotVarExp(loc, new ThisExp(loc), v, 0), new DotVarExp(loc, new IdentifierExp(loc, Id::p), v, 0)); if (e) e = new AndAndExp(loc, e, ec); else e = ec; } if (!e) e = new IntegerExp(loc, 1, Type::tbool); fop->fbody = new ReturnStatement(loc, e); members->push(fop); fop->addMember(sc, this, 1); sc = sc->push(); sc->stc = 0; sc->linkage = LINKd; fop->semantic(sc); sc->pop(); //printf("-StructDeclaration::buildOpEquals() %s\n", toChars()); return fop; }
FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) { Dsymbol *assign = search_function(this, Id::assign); if (assign) { if (FuncDeclaration *f = hasIdentityOpAssign(sc, assign)) return f; // Even if non-identity opAssign is defined, built-in identity opAssign // will be defined. (Is this an exception of operator overloading rule?) } if (!needOpAssign()) return NULL; //printf("StructDeclaration::buildOpAssign() %s\n", toChars()); Parameters *fparams = new Parameters; fparams->push(new Parameter(STCnodtor, type, Id::p, NULL)); Type *ftype = new TypeFunction(fparams, handle, FALSE, LINKd); ((TypeFunction *)ftype)->isref = 1; FuncDeclaration *fop = new FuncDeclaration(loc, 0, Id::assign, 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(0, type, idtmp, new VoidInitializer(0)); tmp->noscope = 1; tmp->storage_class |= STCctfe; e = new DeclarationExp(0, tmp); ec = new AssignExp(0, new VarExp(0, tmp), new ThisExp(0) ); ec->op = TOKblit; e = Expression::combine(e, ec); } ec = new AssignExp(0, new ThisExp(0), new IdentifierExp(0, Id::p)); ec->op = 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 *ec2 = new DotVarExp(0, new VarExp(0, tmp), dtor, 0); ec2 = new CallExp(0, ec2); e = Expression::combine(e, ec2); } } else { /* Do memberwise copy */ //printf("\tmemberwise copy\n"); for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->isField()); // this.v = s.v; AssignExp *ec = new AssignExp(0, new DotVarExp(0, new ThisExp(0), v, 0), new DotVarExp(0, new IdentifierExp(0, Id::p), v, 0)); e = Expression::combine(e, ec); } } Statement *s1 = new ExpStatement(0, e); /* Add: * return this; */ e = new ThisExp(0); Statement *s2 = new ReturnStatement(0, e); fop->fbody = new CompoundStatement(0, s1, s2); Dsymbol *s = fop; if (assign && assign->isTemplateDeclaration()) { // Wrap a template around the function declaration TemplateParameters *tpl = new TemplateParameters(); Dsymbols *decldefs = new Dsymbols(); decldefs->push(s); TemplateDeclaration *tempdecl = new TemplateDeclaration(assign->loc, fop->ident, tpl, NULL, decldefs, 0); s = tempdecl; } members->push(s); s->addMember(sc, this, 1); this->hasIdentityAssign = 1; // temporary mark identity assignable unsigned errors = global.startGagging(); // Do not report errors, even if the unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it. global.speculativeGag = global.gag; Scope *sc2 = sc->push(); sc2->stc = 0; sc2->linkage = LINKd; sc2->speculative = true; s->semantic(sc2); s->semantic2(sc2); s->semantic3(sc2); sc2->pop(); global.speculativeGag = oldspec; if (global.endGagging(errors)) // if errors happened { // Disable generated opAssign, because some members forbid identity assignment. fop->storage_class |= STCdisable; fop->fbody = NULL; // remove fbody which contains the error } //printf("-StructDeclaration::buildOpAssign() %s %s, errors = %d\n", toChars(), s->kind(), (fop->storage_class & STCdisable) != 0); return fop; }
FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) { if (FuncDeclaration *f = hasIdentityOpAssign(sc)) { hasIdentityAssign = 1; return f; } // Even if non-identity opAssign is defined, built-in identity opAssign // will be defined. if (!needOpAssign()) return NULL; //printf("StructDeclaration::buildOpAssign() %s\n", toChars()); StorageClass stc = STCsafe | STCnothrow | STCpure; Loc declLoc = this->loc; Loc loc = Loc(); // internal code should have no loc to prevent coverage if (dtor || postblit) { if (dtor) stc = mergeFuncAttrs(stc, dtor->storage_class); } else { for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->isField()); if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); while (tv->ty == Tsarray) { TypeSArray *ta = (TypeSArray *)tv; tv = tv->nextOf()->toBasetype(); } if (tv->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tv; StructDeclaration *sd = ts->sym; if (FuncDeclaration *f = sd->hasIdentityOpAssign(sc)) stc = mergeFuncAttrs(stc, f->storage_class); } } } Parameters *fparams = new Parameters; fparams->push(new Parameter(STCnodtor, type, Id::p, NULL)); Type *tf = new TypeFunction(fparams, handle, 0, LINKd, stc | STCref); FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), Id::assign, stc, tf); Expression *e = NULL; if (stc & STCdisable) { } else if (dtor || postblit) { /* Do swap this and rhs * 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, type, idtmp, new VoidInitializer(loc)); tmp->noscope = 1; tmp->storage_class |= STCctfe; e = new DeclarationExp(loc, tmp); ec = new AssignExp(loc, new VarExp(loc, tmp), new ThisExp(loc) ); ec->op = TOKblit; e = Expression::combine(e, ec); } ec = new AssignExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id::p)); ec->op = 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 *ec2 = new DotVarExp(loc, new VarExp(loc, tmp), dtor, 0); ec2 = new CallExp(loc, ec2); e = Expression::combine(e, ec2); } } else { /* Do memberwise copy */ //printf("\tmemberwise copy\n"); for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->isField()); // this.v = s.v; AssignExp *ec = new AssignExp(loc, new DotVarExp(loc, new ThisExp(loc), v, 0), new DotVarExp(loc, new IdentifierExp(loc, Id::p), v, 0)); e = Expression::combine(e, ec); } } if (e) { Statement *s1 = new ExpStatement(loc, e); /* Add: * return this; */ e = new ThisExp(loc); Statement *s2 = new ReturnStatement(loc, e); fop->fbody = new CompoundStatement(loc, s1, s2); } Dsymbol *s = fop; #if 1 // workaround until fixing issue 1528 Dsymbol *assign = search_function(this, Id::assign); if (assign && assign->isTemplateDeclaration()) { // Wrap a template around the function declaration TemplateParameters *tpl = new TemplateParameters(); Dsymbols *decldefs = new Dsymbols(); decldefs->push(s); TemplateDeclaration *tempdecl = new TemplateDeclaration(assign->loc, fop->ident, tpl, NULL, decldefs, 0); s = tempdecl; } #endif members->push(s); s->addMember(sc, this, 1); this->hasIdentityAssign = 1; // temporary mark identity assignable unsigned errors = global.startGagging(); // Do not report errors, even if the unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it. global.speculativeGag = global.gag; Scope *sc2 = sc->push(); sc2->stc = 0; sc2->linkage = LINKd; sc2->speculative = true; s->semantic(sc2); s->semantic2(sc2); s->semantic3(sc2); sc2->pop(); global.speculativeGag = oldspec; if (global.endGagging(errors)) // if errors happened { // Disable generated opAssign, because some members forbid identity assignment. fop->storage_class |= STCdisable; fop->fbody = NULL; // remove fbody which contains the error } //printf("-StructDeclaration::buildOpAssign() %s %s, errors = %d\n", toChars(), s->kind(), (fop->storage_class & STCdisable) != 0); return fop; }