FuncDeclaration *StructDeclaration::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"); StorageClass stc = postblit->storage_class & (STCdisable | STCsafe | STCtrusted | STCsystem | STCpure | STCnothrow); if (stc & (STCsafe | STCtrusted)) stc = stc & ~STCsafe | STCtrusted; Parameters *fparams = new Parameters; fparams->push(new Parameter(STCref, type->constOf(), Id::p, NULL)); Type *ftype = new TypeFunction(fparams, Type::tvoid, FALSE, LINKd, stc); ftype->mod = MODconst; fcp = new FuncDeclaration(loc, 0, Id::cpctor, stc, ftype); if (!(fcp->storage_class & STCdisable)) { // Build *this = p; Expression *e = new ThisExp(0); AssignExp *ea = new AssignExp(0, new PtrExp(0, new CastExp(0, new AddrExp(0, e), type->mutableOf()->pointerTo())), new PtrExp(0, new CastExp(0, new AddrExp(0, new IdentifierExp(0, Id::p)), type->mutableOf()->pointerTo())) ); ea->op = TOKblit; Statement *s = new ExpStatement(0, ea); // Build postBlit(); e = new ThisExp(0); e = new PtrExp(0, new CastExp(0, new AddrExp(0, e), type->mutableOf()->pointerTo())); e = new DotVarExp(0, e, postblit, 0); e = new CallExp(0, e); s = new CompoundStatement(0, s, new ExpStatement(0, e)); fcp->fbody = s; } else fcp->fbody = new ExpStatement(0, (Expression *)NULL); members->push(fcp); sc = sc->push(); sc->stc = 0; sc->linkage = LINKd; fcp->semantic(sc); sc->pop(); } return fcp; }
FuncDeclaration *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc) { Parameters *fparams = new Parameters(); Expression *loopbody = buildArrayLoop(exp, fparams); /* Construct the function body: * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++) * loopbody; * return p; */ Parameter *p = (*fparams)[0]; // foreach (i; 0 .. p.length) Statement *s1 = new ForeachRangeStatement(Loc(), TOKforeach, new Parameter(0, NULL, Id::p, NULL), new IntegerExp(Loc(), 0, Type::tsize_t), new ArrayLengthExp(Loc(), new IdentifierExp(Loc(), p->ident)), new ExpStatement(Loc(), loopbody), Loc()); //printf("%s\n", s1->toChars()); Statement *s2 = new ReturnStatement(Loc(), new IdentifierExp(Loc(), p->ident)); //printf("s2: %s\n", s2->toChars()); Statement *fbody = new CompoundStatement(Loc(), s1, s2); // Built-in array ops should be @trusted, pure, nothrow and nogc StorageClass stc = STCtrusted | STCpure | STCnothrow | STCnogc; /* Construct the function */ TypeFunction *ftype = new TypeFunction(fparams, exp->e1->type, 0, LINKc, stc); //printf("fd: %s %s\n", ident->toChars(), ftype->toChars()); FuncDeclaration *fd = new FuncDeclaration(Loc(), Loc(), ident, STCundefined, ftype); fd->fbody = fbody; fd->protection = Prot(PROTpublic); fd->linkage = LINKc; fd->isArrayOp = 1; sc->_module->importedFrom->members->push(fd); sc = sc->push(); sc->parent = sc->_module->importedFrom; sc->stc = 0; sc->linkage = LINKc; fd->semantic(sc); fd->semantic2(sc); unsigned errors = global.startGagging(); fd->semantic3(sc); if (global.endGagging(errors)) { fd->type = Type::terror; fd->errors = true; fd->fbody = NULL; } sc->pop(); return fd; }
FuncDeclaration *StructDeclaration::buildCpCtor(Scope *sc) { /* Copy constructor is only necessary if there is a postblit function, * otherwise the code generator will just do a bit copy. */ if (!postblit) return NULL; //printf("StructDeclaration::buildCpCtor() %s\n", toChars()); StorageClass stc = STCsafe | STCnothrow | STCpure; Loc declLoc = postblit->loc; Loc loc = Loc(); // internal code should have no loc to prevent coverage stc = mergeFuncAttrs(stc, postblit->storage_class); if (stc & STCsafe) // change to @trusted for unsafe casts stc = (stc & ~STCsafe) | STCtrusted; Parameters *fparams = new Parameters; fparams->push(new Parameter(STCref, type->constOf(), Id::p, NULL)); Type *tf = new TypeFunction(fparams, Type::tvoid, 0, LINKd, stc); tf->mod = MODconst; FuncDeclaration *fcp = new FuncDeclaration(declLoc, Loc(), Id::cpctor, stc, tf); if (!(stc & STCdisable)) { // Build *this = p; Expression *e = new ThisExp(loc); AssignExp *ea = new AssignExp(loc, new PtrExp(loc, new CastExp(loc, new AddrExp(loc, e), type->mutableOf()->pointerTo())), new PtrExp(loc, new CastExp(loc, new AddrExp(loc, new IdentifierExp(loc, Id::p)), type->mutableOf()->pointerTo())) ); ea->op = TOKblit; Statement *s = new ExpStatement(loc, ea); // Build postBlit(); e = new ThisExp(loc); e = new PtrExp(loc, new CastExp(loc, new AddrExp(loc, e), type->mutableOf()->pointerTo())); e = new DotVarExp(loc, e, postblit, 0); e = new CallExp(loc, e); s = new CompoundStatement(loc, s, new ExpStatement(loc, e)); fcp->fbody = s; } members->push(fcp); sc = sc->push(); sc->stc = 0; sc->linkage = LINKd; fcp->semantic(sc); sc->pop(); return fcp; }
FuncDeclaration *StructDeclaration::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"); Parameter *param = new Parameter(STCref, type, Id::p, NULL); Parameters *fparams = new Parameters; fparams->push(param); Type *ftype = new TypeFunction(fparams, Type::tvoid, FALSE, LINKd); fcp = new FuncDeclaration(loc, 0, Id::cpctor, STCundefined, ftype); fcp->storage_class |= postblit->storage_class & STCdisable; if (!(fcp->storage_class & STCdisable)) { // Build *this = p; Expression *e = new ThisExp(0); #if !STRUCTTHISREF e = new PtrExp(0, e); #endif AssignExp *ea = new AssignExp(0, e, new IdentifierExp(0, Id::p)); ea->op = TOKblit; Statement *s = new ExpStatement(0, ea); // Build postBlit(); e = new VarExp(0, postblit, 0); e = new CallExp(0, e); s = new CompoundStatement(0, s, new ExpStatement(0, e)); fcp->fbody = s; } else fcp->fbody = new ExpStatement(0, (Expression *)NULL); members->push(fcp); sc = sc->push(); sc->stc = 0; sc->linkage = LINKd; fcp->semantic(sc); sc->pop(); } return fcp; }
/****************************************** * Build __xtoHash for non-bitwise hashing * static hash_t xtoHash(ref const S p) nothrow @trusted; */ FuncDeclaration *buildXtoHash(StructDeclaration *sd, Scope *sc) { if (Dsymbol *s = search_function(sd, Id::tohash)) { static TypeFunction *tftohash; if (!tftohash) { tftohash = new TypeFunction(NULL, Type::thash_t, 0, LINKd); tftohash->mod = MODconst; tftohash = (TypeFunction *)tftohash->merge(); } if (FuncDeclaration *fd = s->isFuncDeclaration()) { fd = fd->overloadExactMatch(tftohash); if (fd) return fd; } } if (!needToHash(sd)) return NULL; //printf("StructDeclaration::buildXtoHash() %s\n", sd->toPrettyChars()); Loc declLoc = Loc(); // loc is unnecessary so __xtoHash is never called directly Loc loc = Loc(); // internal code should have no loc to prevent coverage Parameters *parameters = new Parameters(); parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::thash_t, 0, LINKd, STCnothrow | STCtrusted); Identifier *id = Id::xtoHash; FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); const char *code = "size_t h = 0;" "foreach (i, T; typeof(p.tupleof))" " h += typeid(T).getHash(cast(const void*)&p.tupleof[i]);" "return h;"; fop->fbody = new CompileStatement(loc, new StringExp(loc, (char *)code)); Scope *sc2 = sc->push(); sc2->stc = 0; sc2->linkage = LINKd; fop->semantic(sc2); fop->semantic2(sc2); sc2->pop(); //printf("%s fop = %s %s\n", sd->toChars(), fop->toChars(), fop->type->toChars()); return fop; }
Expression *BinExp::arrayOp(Scope *sc) { //printf("BinExp::arrayOp() %s\n", toChars()); Type *tb = type->toBasetype(); assert(tb->ty == Tarray || tb->ty == Tsarray); if (tb->nextOf()->toBasetype()->ty == Tvoid) { error("Cannot perform array operations on void[] arrays"); return new ErrorExp(); } if (!isArrayOpValid(e2)) { e2->error("invalid array operation %s (did you forget a [] ?)", toChars()); return new ErrorExp(); } Expressions *arguments = new Expressions(); /* The expression to generate an array operation for is mangled * into a name to use as the array operation function name. * Mangle in the operands and operators in RPN order, and type. */ OutBuffer buf; buf.writestring("_array"); buildArrayIdent(&buf, arguments); buf.writeByte('_'); /* Append deco of array element type */ #if DMDV2 buf.writestring(type->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco); #else buf.writestring(type->toBasetype()->nextOf()->toBasetype()->deco); #endif size_t namelen = buf.offset; buf.writeByte(0); char *name = buf.toChars(); Identifier *ident = Lexer::idPool(name); /* Look up name in hash table */ FuncDeclaration **pfd = (FuncDeclaration **)_aaGet(&arrayfuncs, ident); FuncDeclaration *fd = (FuncDeclaration *)*pfd; if (!fd) { /* Some of the array op functions are written as library functions, * presumably to optimize them with special CPU vector instructions. * List those library functions here, in alpha order. */ static const char *libArrayopFuncs[] = { "_arrayExpSliceAddass_a", "_arrayExpSliceAddass_d", // T[]+=T "_arrayExpSliceAddass_f", // T[]+=T "_arrayExpSliceAddass_g", "_arrayExpSliceAddass_h", "_arrayExpSliceAddass_i", "_arrayExpSliceAddass_k", "_arrayExpSliceAddass_s", "_arrayExpSliceAddass_t", "_arrayExpSliceAddass_u", "_arrayExpSliceAddass_w", "_arrayExpSliceDivass_d", // T[]/=T "_arrayExpSliceDivass_f", // T[]/=T "_arrayExpSliceMinSliceAssign_a", "_arrayExpSliceMinSliceAssign_d", // T[]=T-T[] "_arrayExpSliceMinSliceAssign_f", // T[]=T-T[] "_arrayExpSliceMinSliceAssign_g", "_arrayExpSliceMinSliceAssign_h", "_arrayExpSliceMinSliceAssign_i", "_arrayExpSliceMinSliceAssign_k", "_arrayExpSliceMinSliceAssign_s", "_arrayExpSliceMinSliceAssign_t", "_arrayExpSliceMinSliceAssign_u", "_arrayExpSliceMinSliceAssign_w", "_arrayExpSliceMinass_a", "_arrayExpSliceMinass_d", // T[]-=T "_arrayExpSliceMinass_f", // T[]-=T "_arrayExpSliceMinass_g", "_arrayExpSliceMinass_h", "_arrayExpSliceMinass_i", "_arrayExpSliceMinass_k", "_arrayExpSliceMinass_s", "_arrayExpSliceMinass_t", "_arrayExpSliceMinass_u", "_arrayExpSliceMinass_w", "_arrayExpSliceMulass_d", // T[]*=T "_arrayExpSliceMulass_f", // T[]*=T "_arrayExpSliceMulass_i", "_arrayExpSliceMulass_k", "_arrayExpSliceMulass_s", "_arrayExpSliceMulass_t", "_arrayExpSliceMulass_u", "_arrayExpSliceMulass_w", "_arraySliceExpAddSliceAssign_a", "_arraySliceExpAddSliceAssign_d", // T[]=T[]+T "_arraySliceExpAddSliceAssign_f", // T[]=T[]+T "_arraySliceExpAddSliceAssign_g", "_arraySliceExpAddSliceAssign_h", "_arraySliceExpAddSliceAssign_i", "_arraySliceExpAddSliceAssign_k", "_arraySliceExpAddSliceAssign_s", "_arraySliceExpAddSliceAssign_t", "_arraySliceExpAddSliceAssign_u", "_arraySliceExpAddSliceAssign_w", "_arraySliceExpDivSliceAssign_d", // T[]=T[]/T "_arraySliceExpDivSliceAssign_f", // T[]=T[]/T "_arraySliceExpMinSliceAssign_a", "_arraySliceExpMinSliceAssign_d", // T[]=T[]-T "_arraySliceExpMinSliceAssign_f", // T[]=T[]-T "_arraySliceExpMinSliceAssign_g", "_arraySliceExpMinSliceAssign_h", "_arraySliceExpMinSliceAssign_i", "_arraySliceExpMinSliceAssign_k", "_arraySliceExpMinSliceAssign_s", "_arraySliceExpMinSliceAssign_t", "_arraySliceExpMinSliceAssign_u", "_arraySliceExpMinSliceAssign_w", "_arraySliceExpMulSliceAddass_d", // T[] += T[]*T "_arraySliceExpMulSliceAddass_f", "_arraySliceExpMulSliceAddass_r", "_arraySliceExpMulSliceAssign_d", // T[]=T[]*T "_arraySliceExpMulSliceAssign_f", // T[]=T[]*T "_arraySliceExpMulSliceAssign_i", "_arraySliceExpMulSliceAssign_k", "_arraySliceExpMulSliceAssign_s", "_arraySliceExpMulSliceAssign_t", "_arraySliceExpMulSliceAssign_u", "_arraySliceExpMulSliceAssign_w", "_arraySliceExpMulSliceMinass_d", // T[] -= T[]*T "_arraySliceExpMulSliceMinass_f", "_arraySliceExpMulSliceMinass_r", "_arraySliceSliceAddSliceAssign_a", "_arraySliceSliceAddSliceAssign_d", // T[]=T[]+T[] "_arraySliceSliceAddSliceAssign_f", // T[]=T[]+T[] "_arraySliceSliceAddSliceAssign_g", "_arraySliceSliceAddSliceAssign_h", "_arraySliceSliceAddSliceAssign_i", "_arraySliceSliceAddSliceAssign_k", "_arraySliceSliceAddSliceAssign_r", // T[]=T[]+T[] "_arraySliceSliceAddSliceAssign_s", "_arraySliceSliceAddSliceAssign_t", "_arraySliceSliceAddSliceAssign_u", "_arraySliceSliceAddSliceAssign_w", "_arraySliceSliceAddass_a", "_arraySliceSliceAddass_d", // T[]+=T[] "_arraySliceSliceAddass_f", // T[]+=T[] "_arraySliceSliceAddass_g", "_arraySliceSliceAddass_h", "_arraySliceSliceAddass_i", "_arraySliceSliceAddass_k", "_arraySliceSliceAddass_s", "_arraySliceSliceAddass_t", "_arraySliceSliceAddass_u", "_arraySliceSliceAddass_w", "_arraySliceSliceMinSliceAssign_a", "_arraySliceSliceMinSliceAssign_d", // T[]=T[]-T[] "_arraySliceSliceMinSliceAssign_f", // T[]=T[]-T[] "_arraySliceSliceMinSliceAssign_g", "_arraySliceSliceMinSliceAssign_h", "_arraySliceSliceMinSliceAssign_i", "_arraySliceSliceMinSliceAssign_k", "_arraySliceSliceMinSliceAssign_r", // T[]=T[]-T[] "_arraySliceSliceMinSliceAssign_s", "_arraySliceSliceMinSliceAssign_t", "_arraySliceSliceMinSliceAssign_u", "_arraySliceSliceMinSliceAssign_w", "_arraySliceSliceMinass_a", "_arraySliceSliceMinass_d", // T[]-=T[] "_arraySliceSliceMinass_f", // T[]-=T[] "_arraySliceSliceMinass_g", "_arraySliceSliceMinass_h", "_arraySliceSliceMinass_i", "_arraySliceSliceMinass_k", "_arraySliceSliceMinass_s", "_arraySliceSliceMinass_t", "_arraySliceSliceMinass_u", "_arraySliceSliceMinass_w", "_arraySliceSliceMulSliceAssign_d", // T[]=T[]*T[] "_arraySliceSliceMulSliceAssign_f", // T[]=T[]*T[] "_arraySliceSliceMulSliceAssign_i", "_arraySliceSliceMulSliceAssign_k", "_arraySliceSliceMulSliceAssign_s", "_arraySliceSliceMulSliceAssign_t", "_arraySliceSliceMulSliceAssign_u", "_arraySliceSliceMulSliceAssign_w", "_arraySliceSliceMulass_d", // T[]*=T[] "_arraySliceSliceMulass_f", // T[]*=T[] "_arraySliceSliceMulass_i", "_arraySliceSliceMulass_k", "_arraySliceSliceMulass_s", "_arraySliceSliceMulass_t", "_arraySliceSliceMulass_u", "_arraySliceSliceMulass_w", }; int i = binary(name, libArrayopFuncs, sizeof(libArrayopFuncs) / sizeof(char *)); if (i == -1) { #ifdef DEBUG // Make sure our array is alphabetized for (i = 0; i < sizeof(libArrayopFuncs) / sizeof(char *); i++) { if (strcmp(name, libArrayopFuncs[i]) == 0) assert(0); } #endif /* Not in library, so generate it. * Construct the function body: * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++) * loopbody; * return p; */ Parameters *fparams = new Parameters(); Expression *loopbody = buildArrayLoop(fparams); Parameter *p = (*fparams)[0 /*fparams->dim - 1*/]; #if DMDV1 // for (size_t i = 0; i < p.length; i++) Initializer *init = new ExpInitializer(0, new IntegerExp(0, 0, Type::tsize_t)); Dsymbol *d = new VarDeclaration(0, Type::tsize_t, Id::p, init); Statement *s1 = new ForStatement(0, new DeclarationStatement(0, d), new CmpExp(TOKlt, 0, new IdentifierExp(0, Id::p), new ArrayLengthExp(0, new IdentifierExp(0, p->ident))), new PostExp(TOKplusplus, 0, new IdentifierExp(0, Id::p)), new ExpStatement(0, loopbody)); #else // foreach (i; 0 .. p.length) Statement *s1 = new ForeachRangeStatement(0, TOKforeach, new Parameter(0, NULL, Id::p, NULL), new IntegerExp(0, 0, Type::tsize_t), new ArrayLengthExp(0, new IdentifierExp(0, p->ident)), new ExpStatement(0, loopbody)); #endif Statement *s2 = new ReturnStatement(0, new IdentifierExp(0, p->ident)); //printf("s2: %s\n", s2->toChars()); Statement *fbody = new CompoundStatement(0, s1, s2); /* Construct the function */ TypeFunction *ftype = new TypeFunction(fparams, type, 0, LINKc); //printf("ftype: %s\n", ftype->toChars()); fd = new FuncDeclaration(loc, 0, ident, STCundefined, ftype); fd->fbody = fbody; fd->protection = PROTpublic; fd->linkage = LINKc; fd->isArrayOp = 1; sc->module->importedFrom->members->push(fd); sc = sc->push(); sc->parent = sc->module->importedFrom; sc->stc = 0; sc->linkage = LINKc; fd->semantic(sc); fd->semantic2(sc); fd->semantic3(sc); sc->pop(); } else { /* In library, refer to it. */ fd = FuncDeclaration::genCfunc(type, ident); } *pfd = fd; // cache symbol in hash table } /* Call the function fd(arguments) */ Expression *ec = new VarExp(0, fd); Expression *e = new CallExp(loc, ec, arguments); e->type = type; return e; }
/****************************************** * Build __xopCmp for TypeInfo_Struct * static bool __xopCmp(ref const S p, ref const S q) * { * return p.opCmp(q); * } * * This is called by TypeInfo.compare(p1, p2). If the struct does not support * const objects comparison, it will throw "not implemented" Error in runtime. */ FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc) { //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); if (Dsymbol *cmp = search_function(sd, Id::cmp)) { if (FuncDeclaration *fd = cmp->isFuncDeclaration()) { TypeFunction *tfcmpptr; { Scope scx; /* const int opCmp(ref const S s); */ Parameters *parameters = new Parameters; parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL)); tfcmpptr = new TypeFunction(parameters, Type::tint32, 0, LINKd); tfcmpptr->mod = MODconst; tfcmpptr = (TypeFunction *)tfcmpptr->semantic(Loc(), &scx); } fd = fd->overloadExactMatch(tfcmpptr); if (fd) return fd; } } else { #if 0 // FIXME: doesn't work for recursive alias this /* Check opCmp member exists. * Consider 'alias this', but except opDispatch. */ Expression *e = new DsymbolExp(sd->loc, sd); e = new DotIdExp(sd->loc, e, Id::cmp); Scope *sc2 = sc->push(); e = e->trySemantic(sc2); sc2->pop(); if (e) { Dsymbol *s = NULL; switch (e->op) { case TOKoverloadset: s = ((OverExp *)e)->vars; break; case TOKimport: s = ((ScopeExp *)e)->sds; break; case TOKvar: s = ((VarExp *)e)->var; break; default: break; } if (!s || s->ident != Id::cmp) e = NULL; // there's no valid member 'opCmp' } if (!e) return NULL; // bitwise comparison would work /* Essentially, a struct which does not define opCmp is not comparable. * At this time, typeid(S).compare might be correct that throwing "not implement" Error. * But implementing it would break existing code, such as: * * struct S { int value; } // no opCmp * int[S] aa; // Currently AA key uses bitwise comparison * // (It's default behavior of TypeInfo_Strust.compare). * * Not sure we should fix this inconsistency, so just keep current behavior. */ #else return NULL; #endif } if (!sd->xerrcmp) { // object._xopCmp Identifier *id = Identifier::idPool("_xopCmp"); Expression *e = new IdentifierExp(sd->loc, Id::empty); e = new DotIdExp(sd->loc, e, Id::object); e = new DotIdExp(sd->loc, e, id); e = e->semantic(sc); Dsymbol *s = getDsymbol(e); assert(s); sd->xerrcmp = s->isFuncDeclaration(); } Loc declLoc = Loc(); // loc is unnecessary so __xopCmp is never called directly Loc loc = Loc(); // loc is unnecessary so errors are gagged Parameters *parameters = new Parameters; parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL)); parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::tint32, 0, LINKd); Identifier *id = Id::xopCmp; FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); Expression *e1 = new IdentifierExp(loc, Id::p); Expression *e2 = new IdentifierExp(loc, Id::q); Expression *e = new CallExp(loc, new DotIdExp(loc, e2, Id::cmp), e1); fop->fbody = new ReturnStatement(loc, e); unsigned errors = global.startGagging(); // Do not report errors Scope *sc2 = sc->push(); sc2->stc = 0; sc2->linkage = LINKd; fop->semantic(sc2); fop->semantic2(sc2); sc2->pop(); if (global.endGagging(errors)) // if errors happened fop = sd->xerrcmp; return fop; }
/****************************************** * Build __xopEquals for TypeInfo_Struct * static bool __xopEquals(ref const S p, ref const S q) * { * return p == q; * } * * This is called by TypeInfo.equals(p1, p2). If the struct does not support * const objects comparison, it will throw "not implemented" Error in runtime. */ FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc) { if (!needOpEquals(sd)) return NULL; // bitwise comparison would work //printf("StructDeclaration::buildXopEquals() %s\n", sd->toChars()); if (Dsymbol *eq = search_function(sd, Id::eq)) { if (FuncDeclaration *fd = eq->isFuncDeclaration()) { TypeFunction *tfeqptr; { Scope scx; /* const bool opEquals(ref const S s); */ Parameters *parameters = new Parameters; parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL)); tfeqptr = new TypeFunction(parameters, Type::tbool, 0, LINKd); tfeqptr->mod = MODconst; tfeqptr = (TypeFunction *)tfeqptr->semantic(Loc(), &scx); } fd = fd->overloadExactMatch(tfeqptr); if (fd) return fd; } } if (!sd->xerreq) { // object._xopEquals Identifier *id = Identifier::idPool("_xopEquals"); Expression *e = new IdentifierExp(sd->loc, Id::empty); e = new DotIdExp(sd->loc, e, Id::object); e = new DotIdExp(sd->loc, e, id); e = e->semantic(sc); Dsymbol *s = getDsymbol(e); assert(s); sd->xerreq = s->isFuncDeclaration(); } Loc declLoc = Loc(); // loc is unnecessary so __xopEquals is never called directly Loc loc = Loc(); // loc is unnecessary so errors are gagged Parameters *parameters = new Parameters; parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL)); parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); Identifier *id = Id::xopEquals; FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); Expression *e1 = new IdentifierExp(loc, Id::p); Expression *e2 = new IdentifierExp(loc, Id::q); Expression *e = new EqualExp(TOKequal, loc, e1, e2); fop->fbody = new ReturnStatement(loc, e); unsigned errors = global.startGagging(); // Do not report errors Scope *sc2 = sc->push(); sc2->stc = 0; sc2->linkage = LINKd; fop->semantic(sc2); fop->semantic2(sc2); sc2->pop(); if (global.endGagging(errors)) // if errors happened fop = sd->xerreq; return fop; }
void StructDeclaration::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; handle = type->pointerTo(); structalign = sc->structalign; protection = sc->protection; storage_class |= sc->stc; assert(!isAnonymous()); if (sc->stc & STCabstract) error("structs, unions cannot be abstract"); if (storage_class & STCinvariant) type = type->invariantOf(); else if (storage_class & STCconst) type = type->constOf(); if (sizeok == 0) // if not already done the addMember step { for (i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; //printf("adding member '%s' to '%s'\n", s->toChars(), this->toChars()); s->addMember(sc, this, 1); } } sizeok = 0; sc2 = sc->push(this); sc2->stc &= storage_class & (STCconst | STCinvariant); sc2->parent = this; if (isUnionDeclaration()) sc2->inunion = 1; sc2->protection = PROTpublic; sc2->explicitProtection = 0; int members_dim = members->dim; for (i = 0; i < members_dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->semantic(sc2); if (isUnionDeclaration()) sc2->offset = 0; #if 0 if (sizeok == 2) { //printf("forward reference\n"); break; } #endif } /* 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(STCin, handle, Id::p, NULL); arguments->push(arg); tfeqptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeqptr = (TypeFunction *)tfeqptr->semantic(0, sc); } TypeFunction *tfeq; { Arguments *arguments = new Arguments; Argument *arg = new Argument(STCin, type, NULL, NULL); arguments->push(arg); tfeq = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeq = (TypeFunction *)tfeq->semantic(0, sc); } Identifier *id = Id::eq; for (int i = 0; i < 2; i++) { 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, STCundefined, tfeqptr); Expression *e = new IdentifierExp(loc, Id::p); e = new PtrExp(loc, e); Expressions *args = new Expressions(); args->push(e); e = new IdentifierExp(loc, id); e = new CallExp(loc, e, args); fdptr->fbody = new ReturnStatement(loc, e); ScopeDsymbol *s = fdx->parent->isScopeDsymbol(); assert(s); s->members->push(fdptr); fdptr->addMember(sc, s, 1); fdptr->semantic(sc2); } } } id = Id::cmp; } 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 = 1; for (i = 0; i < fields.dim; i++) { Dsymbol *s = (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 = 0; break; } else { if (!vd->type->isZeroInit()) { zeroInit = 0; break; } } } } /* Look for special member functions. */ inv = (InvariantDeclaration *)search(0, Id::classInvariant, 0); aggNew = (NewDeclaration *)search(0, Id::classNew, 0); aggDelete = (DeleteDeclaration *)search(0, Id::classDelete, 0); if (sc->func) { semantic2(sc); semantic3(sc); } }
ArrayOp *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc, Loc loc) { Parameters *fparams = new Parameters(); Expression *loopbody = exp->buildArrayLoop(fparams); ArrayOp *op = new ArrayOp; if (isDruntimeArrayOp(ident)) op->cFunc = FuncDeclaration::genCfunc(fparams, exp->type, ident); else op->cFunc = NULL; /* Construct the function body: * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++) * loopbody; * return p; */ Parameter *p = (*fparams)[0 /*fparams->dim - 1*/]; #if DMDV1 // for (size_t i = 0; i < p.length; i++) Initializer *init = new ExpInitializer(0, new IntegerExp(0, 0, Type::tsize_t)); Dsymbol *d = new VarDeclaration(0, Type::tsize_t, Id::p, init); Statement *s1 = new ForStatement(0, new ExpStatement(0, d), new CmpExp(TOKlt, 0, new IdentifierExp(0, Id::p), new ArrayLengthExp(0, new IdentifierExp(0, p->ident))), new PostExp(TOKplusplus, 0, new IdentifierExp(0, Id::p)), new ExpStatement(0, loopbody)); #else // foreach (i; 0 .. p.length) Statement *s1 = new ForeachRangeStatement(Loc(), TOKforeach, new Parameter(0, NULL, Id::p, NULL), new IntegerExp(Loc(), 0, Type::tsize_t), new ArrayLengthExp(Loc(), new IdentifierExp(Loc(), p->ident)), new ExpStatement(Loc(), loopbody)); #endif //printf("%s\n", s1->toChars()); Statement *s2 = new ReturnStatement(Loc(), new IdentifierExp(Loc(), p->ident)); //printf("s2: %s\n", s2->toChars()); Statement *fbody = new CompoundStatement(Loc(), s1, s2); // Built-in array ops should be @trusted, pure and nothrow StorageClass stc = STCtrusted | STCpure | STCnothrow; /* Construct the function */ TypeFunction *ftype = new TypeFunction(fparams, exp->type, 0, LINKc, stc); //printf("ftype: %s\n", ftype->toChars()); FuncDeclaration *fd = new FuncDeclaration(Loc(), Loc(), ident, STCundefined, ftype); fd->fbody = fbody; fd->protection = PROTpublic; fd->linkage = LINKc; fd->isArrayOp = 1; if (!op->cFunc) sc->module->importedFrom->members->push(fd); sc = sc->push(); sc->parent = sc->module->importedFrom; sc->stc = 0; sc->linkage = LINKc; fd->semantic(sc); fd->semantic2(sc); fd->semantic3(sc); sc->pop(); if (op->cFunc) { op->cFunc->dArrayOp = fd; op->cFunc->type = fd->type; } op->dFunc = fd; return op; }
FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) { if (!needOpAssign()) return NULL; //printf("StructDeclaration::buildOpAssign() %s\n", toChars()); FuncDeclaration *fop = NULL; Parameters *fparams = new Parameters; fparams->push(new Parameter(STCnodtor, type, Id::p, NULL)); Type *ftype = new TypeFunction(fparams, handle, FALSE, LINKd); #if STRUCTTHISREF ((TypeFunction *)ftype)->isref = 1; #endif 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), #if STRUCTTHISREF new ThisExp(0) #else new PtrExp(0, new ThisExp(0)) #endif ); ec->op = TOKblit; e = Expression::combine(e, ec); } ec = new AssignExp(0, #if STRUCTTHISREF new ThisExp(0), #else new PtrExp(0, new ThisExp(0)), #endif 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->storage_class & STCfield); // 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)); ec->op = TOKblit; 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); members->push(fop); fop->addMember(sc, this, 1); sc = sc->push(); sc->stc = 0; sc->linkage = LINKd; fop->semantic(sc); sc->pop(); //printf("-StructDeclaration::buildOpAssign() %s\n", toChars()); return fop; }
FuncDeclaration *StructDeclaration::buildOpEquals(Scope *sc) { Dsymbol *eq = search_function(this, Id::eq); if (eq) { for (size_t i = 0; i <= 1; i++) { Expression *e = i == 0 ? new NullExp(loc, type->constOf()) // dummy rvalue : type->constOf()->defaultInit(); // dummy lvalue Expressions *arguments = new Expressions(); arguments->push(e); // check identity opEquals exists FuncDeclaration *fd = eq->isFuncDeclaration(); if (fd) { fd = fd->overloadResolve(loc, e, arguments, 1); if (fd && !(fd->storage_class & STCdisable)) return fd; } TemplateDeclaration *td = eq->isTemplateDeclaration(); if (td) { fd = td->deduceFunctionTemplate(sc, loc, NULL, e, arguments, 1); if (fd && !(fd->storage_class & STCdisable)) return fd; } } 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->storage_class & STCfield); 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::buildOpEquals(Scope *sc) { if (!needOpEquals()) return NULL; //printf("StructDeclaration::buildOpEquals() %s\n", toChars()); Loc loc = this->loc; Parameters *parameters = new Parameters; #if STRUCTTHISREF // bool opEquals(ref const T) const; Parameter *param = new Parameter(STCref, type->constOf(), Id::p, NULL); #else // bool opEquals(const T*) const; Parameter *param = new Parameter(STCin, type->pointerTo(), Id::p, NULL); #endif parameters->push(param); TypeFunction *ftype = new TypeFunction(parameters, Type::tbool, 0, LINKd); ftype->mod = MODconst; ftype = (TypeFunction *)ftype->semantic(loc, sc); FuncDeclaration *fop = new FuncDeclaration(loc, 0, Id::eq, STCundefined, ftype); Expression *e = NULL; /* Do memberwise compare */ //printf("\tmemberwise compare\n"); for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = (Dsymbol *)fields.data[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->storage_class & STCfield); 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::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; }
void StructDeclaration::semantic(Scope *sc) { Scope *sc2; //printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", this, toChars(), sizeok); //static int count; if (++count == 20) halt(); assert(type); if (!members) // if forward reference return; if (symtab) { if (sizeok == 1 || !scope) { //printf("already completed\n"); scope = NULL; 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; } unsigned dprogress_save = Module::dprogress; #ifdef IN_GCC methods.setDim(0); #endif parent = sc->parent; type = type->semantic(loc, sc); #if STRUCTTHISREF handle = type; #else handle = type->pointerTo(); #endif structalign = sc->structalign; protection = sc->protection; if (sc->stc & STCdeprecated) isdeprecated = 1; assert(!isAnonymous()); if (sc->stc & STCabstract) error("structs, unions cannot be abstract"); #if DMDV2 if (storage_class & STCimmutable) type = type->invariantOf(); else if (storage_class & STCconst) type = type->constOf(); #endif #if IN_GCC if (attributes) attributes->append(sc->attributes); else attributes = sc->attributes; #endif if (sizeok == 0) // if not already done the addMember step { for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; //printf("adding member '%s' to '%s'\n", s->toChars(), this->toChars()); s->addMember(sc, this, 1); } } sizeok = 0; sc2 = sc->push(this); sc2->stc = 0; #if IN_GCC sc2->attributes = NULL; #endif sc2->parent = this; if (isUnionDeclaration()) sc2->inunion = 1; sc2->protection = PROTpublic; sc2->explicitProtection = 0; size_t members_dim = members->dim; /* Set scope so if there are forward references, we still might be able to * resolve individual members like enums. */ for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; /* There are problems doing this in the general case because * Scope keeps track of things like 'offset' */ if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident)) { //printf("setScope %s %s\n", s->kind(), s->toChars()); s->setScope(sc2); } } for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->semantic(sc2); #if 0 if (sizeok == 2) { //printf("forward reference\n"); break; } #endif } #if DMDV1 /* This doesn't work for DMDV2 because (ref S) and (S) parameter * lists will overload the same. */ /* 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; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, handle, Id::p, NULL); arguments->push(arg); tfeqptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeqptr = (TypeFunction *)tfeqptr->semantic(0, sc); } TypeFunction *tfeq; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, type, NULL, NULL); arguments->push(arg); tfeq = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeq = (TypeFunction *)tfeq->semantic(0, sc); } Identifier *id = Id::eq; for (int i = 0; i < 2; i++) { 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, STCundefined, tfeqptr); Expression *e = new IdentifierExp(loc, Id::p); e = new PtrExp(loc, e); Expressions *args = new Expressions(); args->push(e); e = new IdentifierExp(loc, id); e = new CallExp(loc, e, args); fdptr->fbody = new ReturnStatement(loc, e); ScopeDsymbol *s = fdx->parent->isScopeDsymbol(); assert(s); s->members->push(fdptr); fdptr->addMember(sc, s, 1); fdptr->semantic(sc2); } } } id = Id::cmp; } #endif #if DMDV2 /* Try to find the opEquals function. Build it if necessary. */ TypeFunction *tfeqptr; { // bool opEquals(const T*) const; Parameters *parameters = new Parameters; #if STRUCTTHISREF // bool opEquals(ref const T) const; Parameter *param = new Parameter(STCref, type->constOf(), NULL, NULL); #else // bool opEquals(const T*) const; Parameter *param = new Parameter(STCin, type->pointerTo(), NULL, NULL); #endif parameters->push(param); tfeqptr = new TypeFunction(parameters, Type::tbool, 0, LINKd); tfeqptr->mod = MODconst; tfeqptr = (TypeFunction *)tfeqptr->semantic(0, sc2); Dsymbol *s = search_function(this, Id::eq); FuncDeclaration *fdx = s ? s->isFuncDeclaration() : NULL; if (fdx) { eq = fdx->overloadExactMatch(tfeqptr); if (!eq) fdx->error("type signature should be %s not %s", tfeqptr->toChars(), fdx->type->toChars()); } TemplateDeclaration *td = s ? s->isTemplateDeclaration() : NULL; // BUG: should also check that td is a function template, not just a template if (!eq && !td) eq = buildOpEquals(sc2); } dtor = buildDtor(sc2); postblit = buildPostBlit(sc2); cpctor = buildCpCtor(sc2); buildOpAssign(sc2); #endif 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); Module::dprogress = dprogress_save; //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 = 1; for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = (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 = 0; break; } else { if (!vd->type->isZeroInit(loc)) { zeroInit = 0; break; } } } } /* Look for special member functions. */ #if DMDV2 ctor = search(0, Id::ctor, 0); #endif inv = (InvariantDeclaration *)search(0, Id::classInvariant, 0); aggNew = (NewDeclaration *)search(0, Id::classNew, 0); aggDelete = (DeleteDeclaration *)search(0, Id::classDelete, 0); if (sc->func) { semantic2(sc); semantic3(sc); } }
FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) { if (!search_function(this, Id::eq)) return NULL; /* static bool__xopEquals(in void* p, in void* q) { * return ( *cast(const S*)(p) ).opEquals( *cast(const S*)(q) ); * } */ Parameters *parameters = new Parameters; parameters->push(new Parameter(STCin, Type::tvoidptr, Id::p, NULL)); parameters->push(new Parameter(STCin, Type::tvoidptr, Id::q, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); tf = (TypeFunction *)tf->semantic(0, sc); Identifier *id = Lexer::idPool("__xopEquals"); FuncDeclaration *fop = new FuncDeclaration(0, 0, id, STCstatic, tf); Expression *e = new CallExp(0, new DotIdExp(0, new PtrExp(0, new CastExp(0, new IdentifierExp(0, Id::p), type->pointerTo()->constOf())), Id::eq), new PtrExp(0, new CastExp(0, new IdentifierExp(0, Id::q), type->pointerTo()->constOf()))); fop->fbody = new ReturnStatement(0, e); size_t index = members->dim; members->push(fop); 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; fop->semantic(sc2); fop->semantic2(sc2); fop->semantic3(sc2); sc2->pop(); global.speculativeGag = oldspec; if (global.endGagging(errors)) // if errors happened { members->remove(index); if (!xerreq) { Expression *e = new IdentifierExp(0, Id::empty); e = new DotIdExp(0, e, Id::object); e = new DotIdExp(0, e, Lexer::idPool("_xopEquals")); e = e->semantic(sc); Dsymbol *s = getDsymbol(e); FuncDeclaration *fd = s->isFuncDeclaration(); xerreq = fd; } fop = xerreq; } else fop->addMember(sc, this, 1); return fop; }
/****************************************** * Build opAssign for struct. * ref S opAssign(S s) { ... } * * Note that s will be constructed onto the stack, and probably * copy-constructed in caller site. * * If S has copy copy construction and/or destructor, * the body will make bit-wise object swap: * S __swap = this; // bit copy * this = s; // bit copy * __swap.dtor(); * Instead of running the destructor on s, run it on tmp instead. * * Otherwise, the body will make member-wise assignments: * Then, the body is: * this.field1 = s.field1; * this.field2 = s.field2; * ...; */ FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc) { if (FuncDeclaration *f = hasIdentityOpAssign(sd, sc)) { sd->hasIdentityAssign = true; return f; } // Even if non-identity opAssign is defined, built-in identity opAssign // will be defined. if (!needOpAssign(sd)) return NULL; //printf("StructDeclaration::buildOpAssign() %s\n", sd->toChars()); StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; Loc declLoc = sd->loc; Loc loc = Loc(); // internal code should have no loc to prevent coverage if (sd->dtor || sd->postblit) { if (!sd->type->isAssignable()) // Bugzilla 13044 return NULL; stc = mergeFuncAttrs(stc, sd->dtor); if (stc & STCsafe) stc = (stc & ~STCsafe) | STCtrusted; } else { for (size_t i = 0; i < sd->fields.dim; i++) { VarDeclaration *v = sd->fields[i]; if (v->storage_class & STCref) continue; Type *tv = v->type->baseElemOf(); if (tv->ty != Tstruct) continue; StructDeclaration *sdv = ((TypeStruct *)tv)->sym; stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc)); } } Parameters *fparams = new Parameters; fparams->push(new Parameter(STCnodtor, sd->type, Id::p, NULL)); TypeFunction *tf = new TypeFunction(fparams, sd->handleType(), 0, LINKd, stc | STCref); FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), Id::assign, stc, tf); fop->storage_class |= STCinference; Expression *e = NULL; if (stc & STCdisable) { } else if (sd->dtor || sd->postblit) { /* Do swap this and rhs * __swap = this; this = s; __swap.dtor(); */ //printf("\tswap copy\n"); Identifier *idtmp = Identifier::generateId("__swap"); VarDeclaration *tmp = NULL; AssignExp *ec = NULL; if (sd->dtor) { tmp = new VarDeclaration(loc, sd->type, idtmp, new VoidInitializer(loc)); tmp->noscope = 1; tmp->storage_class |= STCtemp | STCctfe; e = new DeclarationExp(loc, tmp); ec = new BlitExp(loc, new VarExp(loc, tmp), new ThisExp(loc)); e = Expression::combine(e, ec); } ec = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id::p)); e = Expression::combine(e, ec); if (sd->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), sd->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 < sd->fields.dim; i++) { VarDeclaration *v = sd->fields[i]; // 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); tf->isreturn = true; } sd->members->push(fop); fop->addMember(sc, sd); sd->hasIdentityAssign = true; // temporary mark identity assignable unsigned errors = global.startGagging(); // Do not report errors, even if the Scope *sc2 = sc->push(); sc2->stc = 0; sc2->linkage = LINKd; fop->semantic(sc2); fop->semantic2(sc2); // Bugzilla 15044: fop->semantic3 isn't run here for lazy forward reference resolution. sc2->pop(); 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, errors = %d\n", sd->toChars(), (fop->storage_class & STCdisable) != 0); return fop; }
void StructDeclaration::semantic(Scope *sc) { Scope *sc2; //printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", this, parent->toChars(), toChars(), sizeok); //static int count; if (++count == 20) halt(); assert(type); if (!members) // if opaque declaration { return; } if (symtab) { if (sizeok == SIZEOKdone || !scope) { //printf("already completed\n"); scope = NULL; 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; } int errors = global.errors; unsigned dprogress_save = Module::dprogress; parent = sc->parent; type = type->semantic(loc, sc); handle = type; protection = sc->protection; alignment = sc->structalign; storage_class |= sc->stc; if (sc->stc & STCdeprecated) isdeprecated = true; assert(!isAnonymous()); if (sc->stc & STCabstract) error("structs, unions cannot be abstract"); userAttributes = sc->userAttributes; if (sizeok == SIZEOKnone) // if not already done the addMember step { for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; //printf("adding member '%s' to '%s'\n", s->toChars(), this->toChars()); s->addMember(sc, this, 1); } } sizeok = SIZEOKnone; sc2 = sc->push(this); sc2->stc &= STCsafe | STCtrusted | STCsystem; sc2->parent = this; if (isUnionDeclaration()) sc2->inunion = 1; sc2->protection = PROTpublic; sc2->explicitProtection = 0; sc2->structalign = STRUCTALIGN_DEFAULT; sc2->userAttributes = NULL; /* Set scope so if there are forward references, we still might be able to * resolve individual members like enums. */ for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; /* There are problems doing this in the general case because * Scope keeps track of things like 'offset' */ //if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident)) { //printf("struct: setScope %s %s\n", s->kind(), s->toChars()); s->setScope(sc2); } } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; /* If this is the last member, see if we can finish setting the size. * This could be much better - finish setting the size after the last * field was processed. The problem is the chicken-and-egg determination * of when that is. See Bugzilla 7426 for more info. */ if (i + 1 == members->dim) { if (sizeok == SIZEOKnone && s->isAliasDeclaration()) finalizeSize(sc2); } // Ungag errors when not speculative unsigned oldgag = global.gag; if (global.isSpeculativeGagging() && !isSpeculative()) { global.gag = 0; } s->semantic(sc2); global.gag = oldgag; } finalizeSize(sc2); if (sizeok == SIZEOKfwd) { // semantic() failed because of forward references. // Unwind what we did, and defer it for later for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *vd = s->isVarDeclaration(); if (vd) vd->offset = 0; } fields.setDim(0); structsize = 0; alignsize = 0; // structalign = 0; scope = scx ? scx : new Scope(*sc); scope->setNoFree(); scope->module->addDeferredSemantic(this); Module::dprogress = dprogress_save; //printf("\tdeferring %s\n", toChars()); return; } Module::dprogress++; //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); // Determine if struct is all zeros or not zeroInit = 1; for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[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 = 0; break; } else { if (!vd->type->isZeroInit(loc)) { zeroInit = 0; break; } } } } #if DMDV1 /* This doesn't work for DMDV2 because (ref S) and (S) parameter * lists will overload the same. */ /* 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; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, handle, Id::p, NULL); arguments->push(arg); tfeqptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeqptr = (TypeFunction *)tfeqptr->semantic(Loc(), sc); } TypeFunction *tfeq; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, type, NULL, NULL); arguments->push(arg); tfeq = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeq = (TypeFunction *)tfeq->semantic(Loc(), sc); } Identifier *id = Id::eq; for (int i = 0; i < 2; i++) { 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, STCundefined, tfeqptr); Expression *e = new IdentifierExp(loc, Id::p); e = new PtrExp(loc, e); Expressions *args = new Expressions(); args->push(e); e = new IdentifierExp(loc, id); e = new CallExp(loc, e, args); fdptr->fbody = new ReturnStatement(loc, e); ScopeDsymbol *s = fdx->parent->isScopeDsymbol(); assert(s); s->members->push(fdptr); fdptr->addMember(sc, s, 1); fdptr->semantic(sc2); } } } id = Id::cmp; } #endif #if DMDV2 dtor = buildDtor(sc2); postblit = buildPostBlit(sc2); cpctor = buildCpCtor(sc2); buildOpAssign(sc2); buildOpEquals(sc2); #endif inv = buildInv(sc2); sc2->pop(); /* Look for special member functions. */ #if DMDV2 ctor = search(Loc(), Id::ctor, 0); #endif aggNew = (NewDeclaration *)search(Loc(), Id::classNew, 0); aggDelete = (DeleteDeclaration *)search(Loc(), Id::classDelete, 0); TypeTuple *tup = type->toArgTypes(); size_t dim = tup->arguments->dim; if (dim >= 1) { assert(dim <= 2); arg1type = (*tup->arguments)[0]->type; if (dim == 2) arg2type = (*tup->arguments)[1]->type; } if (sc->func) { semantic2(sc); semantic3(sc); } if (global.errors != errors) { // The type is no good. type = Type::terror; } if (deferred && !global.gag) { deferred->semantic2(sc); deferred->semantic3(sc); } #if 0 if (type->ty == Tstruct && ((TypeStruct *)type)->sym != this) { printf("this = %p %s\n", this, this->toChars()); printf("type = %d sym = %p\n", type->ty, ((TypeStruct *)type)->sym); } #endif assert(type->ty != Tstruct || ((TypeStruct *)type)->sym == this); }
void StructDeclaration::semantic(Scope *sc) { Scope *sc2; //printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", this, parent->toChars(), toChars(), sizeok); //static int count; if (++count == 20) halt(); assert(type); if (!members) // if forward reference return; if (symtab) { if (sizeok == 1 || !scope) { //printf("already completed\n"); scope = NULL; 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; } int errors = global.gaggedErrors; unsigned dprogress_save = Module::dprogress; parent = sc->parent; type = type->semantic(loc, sc); #if STRUCTTHISREF handle = type; #else handle = type->pointerTo(); #endif structalign = sc->structalign; protection = sc->protection; storage_class |= sc->stc; if (sc->stc & STCdeprecated) isdeprecated = true; assert(!isAnonymous()); if (sc->stc & STCabstract) error("structs, unions cannot be abstract"); #if DMDV2 if (storage_class & STCimmutable) type = type->addMod(MODimmutable); if (storage_class & STCconst) type = type->addMod(MODconst); if (storage_class & STCshared) type = type->addMod(MODshared); #endif if (sizeok == 0) // if not already done the addMember step { int hasfunctions = 0; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = members->tdata()[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 & STCstatic)) { Dsymbol *s = toParent2(); if (s) { AggregateDeclaration *ad = s->isAggregateDeclaration(); FuncDeclaration *fd = s->isFuncDeclaration(); TemplateInstance *ti; if (ad && (ti = ad->parent->isTemplateInstance()) != NULL && ti->isnested || fd) { isnested = 1; Type *t; if (ad) t = ad->handle; else if (fd) { AggregateDeclaration *ad = fd->isMember2(); if (ad) t = ad->handle; else t = Type::tvoidptr; } else assert(0); if (t->ty == Tstruct) t = Type::tvoidptr; // t should not be a ref type assert(!vthis); vthis = new ThisDeclaration(loc, t); //vthis->storage_class |= STCref; members->push(vthis); } } } } sizeok = 0; sc2 = sc->push(this); sc2->stc &= STCsafe | STCtrusted | STCsystem; sc2->parent = this; if (isUnionDeclaration()) sc2->inunion = 1; sc2->protection = PROTpublic; sc2->explicitProtection = 0; size_t members_dim = members->dim; /* Set scope so if there are forward references, we still might be able to * resolve individual members like enums. */ for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (*members)[i]; /* There are problems doing this in the general case because * Scope keeps track of things like 'offset' */ if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident)) { //printf("setScope %s %s\n", s->kind(), s->toChars()); s->setScope(sc2); } } for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (*members)[i]; /* If this is the last member, see if we can finish setting the size. * This could be much better - finish setting the size after the last * field was processed. The problem is the chicken-and-egg determination * of when that is. See Bugzilla 7426 for more info. */ if (i + 1 == members_dim) { if (sizeok == 0 && s->isAliasDeclaration()) finalizeSize(); } // Ungag errors when not speculative unsigned oldgag = global.gag; if (global.isSpeculativeGagging() && !isSpeculative()) global.gag = 0; s->semantic(sc2); global.gag = oldgag; } 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); Module::dprogress = dprogress_save; //printf("\tdeferring %s\n", toChars()); return; } finalizeSize(); Module::dprogress++; //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); // Determine if struct is all zeros or not zeroInit = 1; for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields.tdata()[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 = 0; break; } else { if (!vd->type->isZeroInit(loc)) { zeroInit = 0; break; } } } } #if DMDV1 /* This doesn't work for DMDV2 because (ref S) and (S) parameter * lists will overload the same. */ /* 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; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, handle, Id::p, NULL); arguments->push(arg); tfeqptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeqptr = (TypeFunction *)tfeqptr->semantic(0, sc); } TypeFunction *tfeq; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, type, NULL, NULL); arguments->push(arg); tfeq = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeq = (TypeFunction *)tfeq->semantic(0, sc); } Identifier *id = Id::eq; for (int i = 0; i < 2; i++) { 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, STCundefined, tfeqptr); Expression *e = new IdentifierExp(loc, Id::p); e = new PtrExp(loc, e); Expressions *args = new Expressions(); args->push(e); e = new IdentifierExp(loc, id); e = new CallExp(loc, e, args); fdptr->fbody = new ReturnStatement(loc, e); ScopeDsymbol *s = fdx->parent->isScopeDsymbol(); assert(s); s->members->push(fdptr); fdptr->addMember(sc, s, 1); fdptr->semantic(sc2); } } } id = Id::cmp; } #endif #if DMDV2 dtor = buildDtor(sc2); postblit = buildPostBlit(sc2); cpctor = buildCpCtor(sc2); buildOpAssign(sc2); hasIdentityEquals = (buildOpEquals(sc2) != NULL); xeq = buildXopEquals(sc2); #endif sc2->pop(); /* Look for special member functions. */ #if DMDV2 ctor = search(0, Id::ctor, 0); #endif inv = (InvariantDeclaration *)search(0, Id::classInvariant, 0); aggNew = (NewDeclaration *)search(0, Id::classNew, 0); aggDelete = (DeleteDeclaration *)search(0, Id::classDelete, 0); if (sc->func) { semantic2(sc); semantic3(sc); } if (global.gag && global.gaggedErrors != errors) { // The type is no good, yet the error messages were gagged. type = Type::terror; } if (deferred && !global.gag) { deferred->semantic2(sc); deferred->semantic3(sc); } }