int BaseClass::fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance) { ClassDeclaration *id = base; int result = 0; //printf("BaseClass::fillVtbl(this='%s', cd='%s')\n", base->toChars(), cd->toChars()); if (vtbl) vtbl->setDim(base->vtbl.dim); // first entry is ClassInfo reference for (size_t j = base->vtblOffset(); j < base->vtbl.dim; j++) { FuncDeclaration *ifd = base->vtbl[j]->isFuncDeclaration(); FuncDeclaration *fd; TypeFunction *tf; //printf(" vtbl[%d] is '%s'\n", j, ifd ? ifd->toChars() : "null"); assert(ifd); // Find corresponding function in this class tf = (ifd->type->ty == Tfunction) ? (TypeFunction *)(ifd->type) : NULL; assert(tf); // should always be non-null fd = cd->findFunc(ifd->ident, tf); if (fd && !fd->isAbstract()) { //printf(" found\n"); // Check that calling conventions match if (fd->linkage != ifd->linkage) fd->error("linkage doesn't match interface function"); // Check that it is current if (newinstance && fd->toParent() != cd && ifd->toParent() == base) cd->error("interface function %s.%s is not implemented", id->toChars(), ifd->ident->toChars()); if (fd->toParent() == cd) result = 1; } else { //printf(" not found\n"); // BUG: should mark this class as abstract? if (!cd->isAbstract()) cd->error("interface function %s.%s%s isn't implemented", id->toChars(), ifd->ident->toChars(), Parameter::argsTypesToChars(tf->parameters, tf->varargs)); fd = NULL; } if (vtbl) (*vtbl)[j] = fd; } return result; }
void Declaration::checkModify(Loc loc, Scope *sc, Type *t) { if (sc->incontract && isParameter()) error(loc, "cannot modify parameter '%s' in contract", toChars()); if (sc->incontract && isResult()) error(loc, "cannot modify result '%s' in contract", toChars()); if (isCtorinit() && !t->isMutable()) { // It's only modifiable if inside the right constructor Dsymbol *s = sc->func; while (1) { FuncDeclaration *fd = NULL; if (s) fd = s->isFuncDeclaration(); if (fd && ((fd->isCtorDeclaration() && storage_class & STCfield) || (fd->isStaticCtorDeclaration() && !(storage_class & STCfield))) && fd->toParent() == toParent() ) { VarDeclaration *v = isVarDeclaration(); assert(v); v->ctorinit = 1; //printf("setting ctorinit\n"); } else { if (s) { s = s->toParent2(); continue; } else { const char *p = isStatic() ? "static " : ""; error(loc, "can only initialize %sconst %s inside %sconstructor", p, toChars(), p); } } break; } } else { VarDeclaration *v = isVarDeclaration(); if (v && v->canassign == 0) { const char *p = NULL; if (isConst()) p = "const"; else if (isImmutable()) p = "immutable"; else if (storage_class & STCmanifest) p = "enum"; else if (!t->isAssignable()) p = "struct with immutable members"; if (p) { error(loc, "cannot modify %s", p); } } } }
void ReturnStatement::toIR(IRState *irs) { Blockx *blx = irs->blx; incUsage(irs, loc); if (exp) { elem *e; FuncDeclaration *func = irs->getFunc(); assert(func); assert(func->type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)(func->type); enum RET retmethod = tf->retStyle(); if (retmethod == RETstack) { elem *es; /* If returning struct literal, write result * directly into return value */ if (exp->op == TOKstructliteral) { StructLiteralExp *se = (StructLiteralExp *)exp; char save[sizeof(StructLiteralExp)]; memcpy(save, se, sizeof(StructLiteralExp)); se->sym = irs->shidden; se->soffset = 0; se->fillHoles = 1; e = exp->toElemDtor(irs); memcpy(se, save, sizeof(StructLiteralExp)); } else e = exp->toElemDtor(irs); assert(e); if (exp->op == TOKstructliteral || (func->nrvo_can && func->nrvo_var)) { // Return value via hidden pointer passed as parameter // Write exp; return shidden; es = e; } else { // Return value via hidden pointer passed as parameter // Write *shidden=exp; return shidden; int op; tym_t ety; ety = e->Ety; es = el_una(OPind,ety,el_var(irs->shidden)); op = (tybasic(ety) == TYstruct) ? OPstreq : OPeq; es = el_bin(op, ety, es, e); if (op == OPstreq) es->ET = exp->type->toCtype(); #if DMDV2 /* Call postBlit() on *shidden */ Type *tb = exp->type->toBasetype(); //if (tb->ty == Tstruct) exp->dump(0); if ((exp->op == TOKvar || exp->op == TOKdotvar || exp->op == TOKstar || exp->op == TOKthis) && tb->ty == Tstruct) { StructDeclaration *sd = ((TypeStruct *)tb)->sym; if (sd->postblit) { FuncDeclaration *fd = sd->postblit; if (fd->storage_class & STCdisable) { fd->toParent()->error(loc, "is not copyable because it is annotated with @disable"); } elem *ec = el_var(irs->shidden); ec = callfunc(loc, irs, 1, Type::tvoid, ec, tb->pointerTo(), fd, fd->type, NULL, NULL); es = el_bin(OPcomma, ec->Ety, es, ec); } #if 0 /* It has been moved, so disable destructor */ if (exp->op == TOKvar) { VarExp *ve = (VarExp *)exp; VarDeclaration *v = ve->var->isVarDeclaration(); if (v && v->rundtor) { elem *er = el_var(v->rundtor->toSymbol()); er = el_bin(OPeq, TYint, er, el_long(TYint, 0)); es = el_bin(OPcomma, TYint, es, er); } } #endif } #endif } e = el_var(irs->shidden); e = el_bin(OPcomma, e->Ety, es, e); } #if DMDV2 else if (tf->isref) { // Reference return, so convert to a pointer Expression *ae = exp->addressOf(NULL); e = ae->toElemDtor(irs); } #endif else { e = exp->toElemDtor(irs); assert(e); } elem_setLoc(e, loc); block_appendexp(blx->curblock, e); block_next(blx, BCretexp, NULL); } else block_next(blx, BCret, NULL); }
void FuncDeclaration::toObjFile(int multiobj) { FuncDeclaration *func = this; ClassDeclaration *cd = func->parent->isClassDeclaration(); int reverse; int has_arguments; //printf("FuncDeclaration::toObjFile(%p, %s.%s)\n", func, parent->toChars(), func->toChars()); //if (type) printf("type = %s\n", func->type->toChars()); #if 0 //printf("line = %d\n",func->getWhere() / LINEINC); EEcontext *ee = env->getEEcontext(); if (ee->EEcompile == 2) { if (ee->EElinnum < (func->getWhere() / LINEINC) || ee->EElinnum > (func->endwhere / LINEINC) ) return; // don't compile this function ee->EEfunc = func->toSymbol(); } #endif if (semanticRun >= PASSobj) // if toObjFile() already run return; // If errors occurred compiling it, such as bugzilla 6118 if (type && type->ty == Tfunction && ((TypeFunction *)type)->next->ty == Terror) return; if (global.errors) return; if (!func->fbody) { return; } if (func->isUnitTestDeclaration() && !global.params.useUnitTests) return; if (multiobj && !isStaticDtorDeclaration() && !isStaticCtorDeclaration()) { obj_append(this); return; } if (semanticRun == PASSsemanticdone) { /* What happened is this function failed semantic3() with errors, * but the errors were gagged. * Try to reproduce those errors, and then fail. */ error("errors compiling the function"); return; } assert(semanticRun == PASSsemantic3done); semanticRun = PASSobj; if (global.params.verbose) printf("function %s\n",func->toPrettyChars()); Symbol *s = func->toSymbol(); func_t *f = s->Sfunc; // tunnel type of "this" to debug info generation if (AggregateDeclaration* ad = func->parent->isAggregateDeclaration()) { ::type* t = ad->getType()->toCtype(); if(cd) t = t->Tnext; // skip reference f->Fclass = (Classsym *)t; } #if TARGET_WINDOS /* This is done so that the 'this' pointer on the stack is the same * distance away from the function parameters, so that an overriding * function can call the nested fdensure or fdrequire of its overridden function * and the stack offsets are the same. */ if (isVirtual() && (fensure || frequire)) f->Fflags3 |= Ffakeeh; #endif #if TARGET_OSX s->Sclass = SCcomdat; #else s->Sclass = SCglobal; #endif for (Dsymbol *p = parent; p; p = p->parent) { if (p->isTemplateInstance()) { s->Sclass = SCcomdat; break; } } /* Vector operations should be comdat's */ if (isArrayOp) s->Sclass = SCcomdat; if (isNested()) { // if (!(config.flags3 & CFG3pic)) // s->Sclass = SCstatic; f->Fflags3 |= Fnested; /* The enclosing function must have its code generated first, * so we know things like where its local symbols are stored. */ FuncDeclaration *fdp = toAliasFunc()->toParent2()->isFuncDeclaration(); // Bug 8016 - only include the function if it is a template instance Dsymbol * owner = NULL; if (fdp) { owner = fdp->toParent(); while (owner && !owner->isTemplateInstance()) owner = owner->toParent(); } if (owner && fdp && fdp->semanticRun == PASSsemantic3done && !fdp->isUnitTestDeclaration()) { /* Can't do unittest's out of order, they are order dependent in that their * execution is done in lexical order, and some modules (std.datetime *cough* * *cough*) rely on this. */ fdp->toObjFile(multiobj); } } else { const char *libname = (global.params.symdebug) ? global.params.debuglibname : global.params.defaultlibname; // Pull in RTL startup code (but only once) if (func->isMain() && onlyOneMain(loc)) { #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS objmod->external_def("_main"); objmod->ehsections(); // initialize exception handling sections #endif #if TARGET_WINDOS if (I64) { objmod->external_def("main"); objmod->ehsections(); // initialize exception handling sections } else { objmod->external_def("_main"); objmod->external_def("__acrtused_con"); } #endif objmod->includelib(libname); s->Sclass = SCglobal; } else if (strcmp(s->Sident, "main") == 0 && linkage == LINKc) { #if TARGET_WINDOS if (I64) { objmod->includelib("LIBCMT"); objmod->includelib("OLDNAMES"); } else { objmod->external_def("__acrtused_con"); // bring in C startup code objmod->includelib("snn.lib"); // bring in C runtime library } #endif s->Sclass = SCglobal; } #if TARGET_WINDOS else if (func->isWinMain() && onlyOneMain(loc)) { if (I64) { objmod->includelib("uuid"); objmod->includelib("LIBCMT"); objmod->includelib("OLDNAMES"); objmod->ehsections(); // initialize exception handling sections } else { objmod->external_def("__acrtused"); } objmod->includelib(libname); s->Sclass = SCglobal; } // Pull in RTL startup code else if (func->isDllMain() && onlyOneMain(loc)) { if (I64) { objmod->includelib("uuid"); objmod->includelib("LIBCMT"); objmod->includelib("OLDNAMES"); objmod->ehsections(); // initialize exception handling sections } else { objmod->external_def("__acrtused_dll"); } objmod->includelib(libname); s->Sclass = SCglobal; } #endif } cstate.CSpsymtab = &f->Flocsym; // Find module m for this function Module *m = NULL; for (Dsymbol *p = parent; p; p = p->parent) { m = p->isModule(); if (m) break; } IRState irs(m, func); Dsymbols deferToObj; // write these to OBJ file later irs.deferToObj = &deferToObj; TypeFunction *tf; RET retmethod; symbol *shidden = NULL; Symbol *sthis = NULL; tym_t tyf; tyf = tybasic(s->Stype->Tty); //printf("linkage = %d, tyf = x%x\n", linkage, tyf); reverse = tyrevfunc(s->Stype->Tty); assert(func->type->ty == Tfunction); tf = (TypeFunction *)(func->type); has_arguments = (tf->linkage == LINKd) && (tf->varargs == 1); retmethod = tf->retStyle(); if (retmethod == RETstack) { // If function returns a struct, put a pointer to that // as the first argument ::type *thidden = tf->next->pointerTo()->toCtype(); char hiddenparam[5+4+1]; static int hiddenparami; // how many we've generated so far sprintf(hiddenparam,"__HID%d",++hiddenparami); shidden = symbol_name(hiddenparam,SCparameter,thidden); shidden->Sflags |= SFLtrue | SFLfree; #if DMDV1 if (func->nrvo_can && func->nrvo_var && func->nrvo_var->nestedref) #else if (func->nrvo_can && func->nrvo_var && func->nrvo_var->nestedrefs.dim) #endif type_setcv(&shidden->Stype, shidden->Stype->Tty | mTYvolatile); irs.shidden = shidden; this->shidden = shidden; } else { // Register return style cannot make nrvo. // Auto functions keep the nrvo_can flag up to here, // so we should eliminate it before entering backend. nrvo_can = 0; } if (vthis) { assert(!vthis->csym); sthis = vthis->toSymbol(); irs.sthis = sthis; if (!(f->Fflags3 & Fnested)) f->Fflags3 |= Fmember; } // Estimate number of parameters, pi size_t pi = (v_arguments != NULL); if (parameters) pi += parameters->dim; // Create a temporary buffer, params[], to hold function parameters Symbol *paramsbuf[10]; Symbol **params = paramsbuf; // allocate on stack if possible if (pi + 2 > 10) // allow extra 2 for sthis and shidden { params = (Symbol **)malloc((pi + 2) * sizeof(Symbol *)); assert(params); } // Get the actual number of parameters, pi, and fill in the params[] pi = 0; if (v_arguments) { params[pi] = v_arguments->toSymbol(); pi += 1; } if (parameters) { for (size_t i = 0; i < parameters->dim; i++) { VarDeclaration *v = (*parameters)[i]; if (v->csym) { error("compiler error, parameter '%s', bugzilla 2962?", v->toChars()); assert(0); } params[pi + i] = v->toSymbol(); } pi += parameters->dim; } if (reverse) { // Reverse params[] entries for (size_t i = 0; i < pi/2; i++) { Symbol *sptmp = params[i]; params[i] = params[pi - 1 - i]; params[pi - 1 - i] = sptmp; } } if (shidden) { #if 0 // shidden becomes last parameter params[pi] = shidden; #else // shidden becomes first parameter memmove(params + 1, params, pi * sizeof(params[0])); params[0] = shidden; #endif pi++; } if (sthis) { #if 0 // sthis becomes last parameter params[pi] = sthis; #else // sthis becomes first parameter memmove(params + 1, params, pi * sizeof(params[0])); params[0] = sthis; #endif pi++; } if ((global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isSolaris) && linkage != LINKd && shidden && sthis) { /* swap shidden and sthis */ Symbol *sp = params[0]; params[0] = params[1]; params[1] = sp; } for (size_t i = 0; i < pi; i++) { Symbol *sp = params[i]; sp->Sclass = SCparameter; sp->Sflags &= ~SFLspill; sp->Sfl = FLpara; symbol_add(sp); } // Determine register assignments if (pi) { FuncParamRegs fpr(tyf); for (size_t i = 0; i < pi; i++) { Symbol *sp = params[i]; if (fpr.alloc(sp->Stype, sp->Stype->Tty, &sp->Spreg, &sp->Spreg2)) { sp->Sclass = (config.exe == EX_WIN64) ? SCshadowreg : SCfastpar; sp->Sfl = (sp->Sclass == SCshadowreg) ? FLpara : FLfast; } } } // Done with params if (params != paramsbuf) free(params); params = NULL; if (func->fbody) { localgot = NULL; Statement *sbody = func->fbody; Blockx bx; memset(&bx,0,sizeof(bx)); bx.startblock = block_calloc(); bx.curblock = bx.startblock; bx.funcsym = s; bx.scope_index = -1; bx.classdec = cd; bx.member = func; bx.module = getModule(); irs.blx = &bx; /* Doing this in semantic3() caused all kinds of problems: * 1. couldn't reliably get the final mangling of the function name due to fwd refs * 2. impact on function inlining * 3. what to do when writing out .di files, or other pretty printing */ if (global.params.trace) { /* Wrap the entire function body in: * trace_pro("funcname"); * try * body; * finally * _c_trace_epi(); */ StringExp *se = new StringExp(Loc(), s->Sident); se->type = new TypeDArray(Type::tchar->immutableOf()); se->type = se->type->semantic(Loc(), NULL); Expressions *exps = new Expressions(); exps->push(se); FuncDeclaration *fdpro = FuncDeclaration::genCfunc(Type::tvoid, "trace_pro"); Expression *ec = new VarExp(Loc(), fdpro); Expression *e = new CallExp(Loc(), ec, exps); e->type = Type::tvoid; Statement *sp = new ExpStatement(loc, e); FuncDeclaration *fdepi = FuncDeclaration::genCfunc(Type::tvoid, "_c_trace_epi"); ec = new VarExp(Loc(), fdepi); e = new CallExp(Loc(), ec); e->type = Type::tvoid; Statement *sf = new ExpStatement(loc, e); Statement *stf; if (sbody->blockExit(tf->isnothrow) == BEfallthru) stf = new CompoundStatement(Loc(), sbody, sf); else stf = new TryFinallyStatement(Loc(), sbody, sf); sbody = new CompoundStatement(Loc(), sp, stf); } #if DMDV2 buildClosure(&irs); #endif #if TARGET_WINDOS if (func->isSynchronized() && cd && config.flags2 & CFG2seh && !func->isStatic() && !sbody->usesEH() && !global.params.trace) { /* The "jmonitor" hack uses an optimized exception handling frame * which is a little shorter than the more general EH frame. */ s->Sfunc->Fflags3 |= Fjmonitor; } #endif sbody->toIR(&irs); bx.curblock->BC = BCret; f->Fstartblock = bx.startblock; // einit = el_combine(einit,bx.init); if (isCtorDeclaration()) { assert(sthis); for (block *b = f->Fstartblock; b; b = b->Bnext) { if (b->BC == BCret) { b->BC = BCretexp; b->Belem = el_combine(b->Belem, el_var(sthis)); } } } } // If static constructor #if DMDV2 if (isSharedStaticCtorDeclaration()) // must come first because it derives from StaticCtorDeclaration { ssharedctors.push(s); } else #endif if (isStaticCtorDeclaration()) { sctors.push(s); } // If static destructor #if DMDV2 if (isSharedStaticDtorDeclaration()) // must come first because it derives from StaticDtorDeclaration { SharedStaticDtorDeclaration *f = isSharedStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ esharedctorgates.push(f); } sshareddtors.shift(s); } else #endif if (isStaticDtorDeclaration()) { StaticDtorDeclaration *f = isStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ ectorgates.push(f); } sdtors.shift(s); } // If unit test if (isUnitTestDeclaration()) { stests.push(s); } if (global.errors) return; writefunc(s); if (isExport()) objmod->export_symbol(s, Para.offset); for (size_t i = 0; i < irs.deferToObj->dim; i++) { Dsymbol *s = (*irs.deferToObj)[i]; FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { FuncDeclaration *fdp = fd->toParent2()->isFuncDeclaration(); if (fdp && fdp->semanticRun < PASSobj) { /* Bugzilla 7595 * FuncDeclaration::buildClosure() relies on nested functions * being toObjFile'd after the outer function. Otherwise, the * v->offset's for the closure variables are wrong. * So, defer fd until after fdp is done. */ fdp->deferred.push(fd); continue; } } s->toObjFile(0); } for (size_t i = 0; i < deferred.dim; i++) { FuncDeclaration *fd = deferred[i]; fd->toObjFile(0); } #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS // A hack to get a pointer to this function put in the .dtors segment if (ident && memcmp(ident->toChars(), "_STD", 4) == 0) objmod->staticdtor(s); #endif #if DMDV2 if (irs.startaddress) { //printf("Setting start address\n"); objmod->startaddress(irs.startaddress); } #endif }
void ReturnStatement::toIR(IRState *irs) { Blockx *blx = irs->blx; enum BC bc; incUsage(irs, loc); if (exp) { elem *e; FuncDeclaration *func = irs->getFunc(); assert(func); assert(func->type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)(func->type); enum RET retmethod = tf->retStyle(); if (retmethod == RETstack) { elem *es; /* If returning struct literal, write result * directly into return value */ if (exp->op == TOKstructliteral) { StructLiteralExp *se = (StructLiteralExp *)exp; char save[sizeof(StructLiteralExp)]; memcpy(save, se, sizeof(StructLiteralExp)); se->sym = irs->shidden; se->soffset = 0; se->fillHoles = 1; e = exp->toElemDtor(irs); memcpy(se, save, sizeof(StructLiteralExp)); } else e = exp->toElemDtor(irs); assert(e); if (exp->op == TOKstructliteral || (func->nrvo_can && func->nrvo_var)) { // Return value via hidden pointer passed as parameter // Write exp; return shidden; es = e; } else { // Return value via hidden pointer passed as parameter // Write *shidden=exp; return shidden; int op; tym_t ety; ety = e->Ety; es = el_una(OPind,ety,el_var(irs->shidden)); op = (tybasic(ety) == TYstruct) ? OPstreq : OPeq; es = el_bin(op, ety, es, e); if (op == OPstreq) es->ET = exp->type->toCtype(); #if 0//DMDV2 /* Call postBlit() on *shidden */ Type *tb = exp->type->toBasetype(); //if (tb->ty == Tstruct) exp->dump(0); if (exp->isLvalue() && tb->ty == Tstruct) { StructDeclaration *sd = ((TypeStruct *)tb)->sym; if (sd->postblit) { FuncDeclaration *fd = sd->postblit; if (fd->storage_class & STCdisable) { fd->toParent()->error(loc, "is not copyable because it is annotated with @disable"); } elem *ec = el_var(irs->shidden); ec = callfunc(loc, irs, 1, Type::tvoid, ec, tb->pointerTo(), fd, fd->type, NULL, NULL); es = el_bin(OPcomma, ec->Ety, es, ec); } } #endif } e = el_var(irs->shidden); e = el_bin(OPcomma, e->Ety, es, e); } #if DMDV2 else if (tf->isref) { // Reference return, so convert to a pointer Expression *ae = exp->addressOf(NULL); e = ae->toElemDtor(irs); } #endif else { e = exp->toElemDtor(irs); assert(e); } elem_setLoc(e, loc); block_appendexp(blx->curblock, e); bc = BCretexp; } else bc = BCret; block *btry = blx->curblock->Btry; if (btry) { // A finally block is a successor to a return block inside a try-finally if (btry->numSucc() == 2) // try-finally { block *bfinally = btry->nthSucc(1); assert(bfinally->BC == BC_finally); blx->curblock->appendSucc(bfinally); } } block_next(blx, bc, NULL); }