void Module::semantic() { if (semanticstarted) return; //printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent); semanticstarted = 1; // Note that modules get their own scope, from scratch. // This is so regardless of where in the syntax a module // gets imported, it is unaffected by context. Scope *sc = scope; // see if already got one from importAll() if (!sc) { printf("test2\n"); Scope::createGlobal(this); // create root scope } //printf("Module = %p, linkage = %d\n", sc->scopesym, sc->linkage); #if 0 // Add import of "object" if this module isn't "object" if (ident != Id::object) { Import *im = new Import(0, NULL, Id::object, NULL, 0); members->shift(im); } // Add all symbols into module's symbol table symtab = new DsymbolTable(); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->addMember(NULL, sc->scopesym, 1); } /* Set scope for the symbols so that if we forward reference * a symbol, it can possibly be resolved on the spot. * If this works out well, it can be extended to all modules * before any semantic() on any of them. */ for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->setScope(sc); } #endif // Do semantic() on members that don't depend on others for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = members->tdata()[i]; //printf("\tModule('%s'): '%s'.semantic0()\n", toChars(), s->toChars()); s->semantic0(sc); } // Pass 1 semantic routines: do public side of the definition for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = members->tdata()[i]; //printf("\tModule('%s'): '%s'.semantic()\n", toChars(), s->toChars()); s->semantic(sc); runDeferredSemantic(); } if (!scope) { sc = sc->pop(); sc->pop(); // 2 pops because Scope::createGlobal() created 2 } semanticRun = semanticstarted; //printf("-Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent); }
Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) { //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident->toChars(), flags); if (ident == Id::length || ident == Id::dollar) { VarDeclaration **pvar; Expression *ce; if (ident == Id::length && !global.params.useDeprecated) error("using 'length' inside [ ] is deprecated, use '$' instead"); L1: if (td) { /* $ gives the number of elements in the tuple */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); Expression *e = new IntegerExp(0, td->objects->dim, Type::tsize_t); v->init = new ExpInitializer(0, e); v->storage_class |= STCstatic | STCconst; v->semantic(sc); return v; } if (type) { /* $ gives the number of type entries in the type tuple */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); Expression *e = new IntegerExp(0, type->arguments->dim, Type::tsize_t); v->init = new ExpInitializer(0, e); v->storage_class |= STCstatic | STCconst; v->semantic(sc); return v; } if (exp->op == TOKindex) { /* array[index] where index is some function of $ */ IndexExp *ie = (IndexExp *)exp; pvar = &ie->lengthVar; ce = ie->e1; } else if (exp->op == TOKslice) { /* array[lwr .. upr] where lwr or upr is some function of $ */ SliceExp *se = (SliceExp *)exp; pvar = &se->lengthVar; ce = se->e1; } else if (exp->op == TOKarray) { /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $ * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...) */ ArrayExp *ae = (ArrayExp *)exp; AggregateDeclaration *ad = NULL; Type *t = ae->e1->type->toBasetype(); if (t->ty == Tclass) { ad = ((TypeClass *)t)->sym; } else if (t->ty == Tstruct) { ad = ((TypeStruct *)t)->sym; } assert(ad); Dsymbol *dsym = search_function(ad, Id::opDollar); if (!dsym) // no dollar exists -- search in higher scope return NULL; VarDeclaration *v = ae->lengthVar; if (!v) { // $ is lazily initialized. Create it now. TemplateDeclaration *td = dsym->isTemplateDeclaration(); if (td) { // Instantiate opDollar!(dim) with the index as a template argument Objects *tdargs = new Objects(); tdargs->setDim(1); Expression *x = new IntegerExp(0, ae->currentDimension, Type::tsize_t); x = x->semantic(sc); tdargs->data[0] = x; //TemplateInstance *ti = new TemplateInstance(loc, td, tdargs); //ti->semantic(sc); DotTemplateInstanceExp *dte = new DotTemplateInstanceExp(loc, ae->e1, td->ident, tdargs); v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(0, dte)); } else { /* opDollar exists, but it's a function, not a template. * This is acceptable ONLY for single-dimension indexing. * Note that it's impossible to have both template & function opDollar, * because both take no arguments. */ if (ae->arguments->dim != 1) { ae->error("%s only defines opDollar for one dimension", ad->toChars()); return NULL; } FuncDeclaration *fd = dsym->isFuncDeclaration(); assert(fd); Expression * x = new DotVarExp(loc, ae->e1, fd); v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(0, x)); } v->semantic(sc); ae->lengthVar = v; } return v; } else /* Didn't find $, look in enclosing scope(s). */ return NULL; /* If we are indexing into an array that is really a type * tuple, rewrite this as an index into a type tuple and * try again. */ if (ce->op == TOKtype) { Type *t = ((TypeExp *)ce)->type; if (t->ty == Ttuple) { type = (TypeTuple *)t; goto L1; } } /* *pvar is lazily initialized, so if we refer to $ * multiple times, it gets set only once. */ if (!*pvar) // if not already initialized { /* Create variable v and set it to the value of $ */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); if (ce->op == TOKtuple) { /* It is for an expression tuple, so the * length will be a const. */ Expression *e = new IntegerExp(0, ((TupleExp *)ce)->exps->dim, Type::tsize_t); v->init = new ExpInitializer(0, e); v->storage_class |= STCstatic | STCconst; } else { /* For arrays, $ will either be a compile-time constant * (in which case its value in set during constant-folding), * or a variable (in which case an expression is created in * toir.c). */ VoidInitializer *e = new VoidInitializer(0); e->type = Type::tsize_t; v->init = e; } *pvar = v; } (*pvar)->semantic(sc); return (*pvar); } return NULL; }
Dsymbol *ScopeDsymbol::search(Loc loc, Identifier *ident, int flags) { //printf("%s->ScopeDsymbol::search(ident='%s', flags=x%x)\n", toChars(), ident->toChars(), flags); //if (strcmp(ident->toChars(),"c") == 0) *(char*)0=0; // Look in symbols declared in this module Dsymbol *s = symtab ? symtab->lookup(ident) : NULL; //printf("\ts = %p, imports = %p, %d\n", s, imports, imports ? imports->dim : 0); if (s) { //printf("\ts = '%s.%s'\n",toChars(),s->toChars()); } else if (imports) { OverloadSet *a = NULL; // Look in imported modules for (size_t i = 0; i < imports->dim; i++) { ScopeDsymbol *ss = (*imports)[i]; Dsymbol *s2; // If private import, don't search it if (flags & 1 && prots[i] == PROTprivate) continue; //printf("\tscanning import '%s', prots = %d, isModule = %p, isImport = %p\n", ss->toChars(), prots[i], ss->isModule(), ss->isImport()); /* Don't find private members if ss is a module */ s2 = ss->search(loc, ident, ss->isModule() ? 1 : 0); if (!s) s = s2; else if (s2 && s != s2) { if (s->toAlias() == s2->toAlias()) { /* After following aliases, we found the same symbol, * so it's not an ambiguity. * But if one alias is deprecated, prefer the other. */ if (s->isDeprecated()) s = s2; } else { /* Two imports of the same module should be regarded as * the same. */ Import *i1 = s->isImport(); Import *i2 = s2->isImport(); if (!(i1 && i2 && (i1->mod == i2->mod || (!i1->parent->isImport() && !i2->parent->isImport() && i1->ident->equals(i2->ident)) ) ) ) { /* If both s2 and s are overloadable (though we only * need to check s once) */ if (s2->isOverloadable() && (a || s->isOverloadable())) { if (!a) a = new OverloadSet(); /* Don't add to a[] if s2 is alias of previous sym */ for (size_t j = 0; j < a->a.dim; j++) { Dsymbol *s3 = a->a[j]; if (s2->toAlias() == s3->toAlias()) { if (s3->isDeprecated()) a->a[j] = s2; goto Lcontinue; } } a->push(s2); Lcontinue: continue; } if (flags & 4) // if return NULL on ambiguity return NULL; if (!(flags & 2)) ss->multiplyDefined(loc, s, s2); break; } } } } /* Build special symbol if we had multiple finds */ if (a) { assert(s); a->push(s); s = a; } if (s) { Declaration *d = s->isDeclaration(); if (d && d->protection == PROTprivate && !d->parent->isTemplateMixin() && !(flags & 2)) error(loc, "%s is private", d->toPrettyChars()); } } return s; }
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(loc, sc); Identifier *id = Lexer::idPool("__xopEquals"); FuncDeclaration *fop = new FuncDeclaration(loc, 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(loc, e); size_t index = members->dim; members->push(fop); sc = sc->push(); sc->stc = 0; sc->linkage = LINKd; unsigned errors = global.startGagging(); fop->semantic(sc); if (errors == global.gaggedErrors) { fop->semantic2(sc); if (errors == global.gaggedErrors) { fop->semantic3(sc); if (errors == global.gaggedErrors) fop->addMember(sc, this, 1); } } if (global.endGagging(errors)) // if errors happened { members->remove(index); if (!xerreq) { Expression *e = new IdentifierExp(loc, Id::empty); e = new DotIdExp(loc, e, Id::object); e = new DotIdExp(loc, e, Lexer::idPool("_xopEquals")); e = e->semantic(sc); Dsymbol *s = getDsymbol(e); FuncDeclaration *fd = s->isFuncDeclaration(); xerreq = fd; } fop = xerreq; } sc->pop(); return fop; }
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.tdata()[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->storage_class & STCfield); if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); dinteger_t dim = (tv->ty == Tsarray ? 1 : 0); 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) { Expression *ex; // this.v ex = new ThisExp(0); ex = new DotVarExp(0, ex, v, 0); if (dim == 0) { // 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.tdata()[0]; default: e = NULL; for (size_t i = 0; i < dtors.dim; i++) { FuncDeclaration *fd = dtors.tdata()[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; } }
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 (!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; #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; enum 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; /* If profiling, insert call to the profiler here. * _c_trace_pro(char* funcname); */ if (global.params.trace) { dt_t *dt = NULL; char *id = s->Sident; size_t len = strlen(id); dtnbytes(&dt, len + 1, id); Symbol *sfuncname = symbol_generate(SCstatic,type_fake(TYchar)); sfuncname->Sdt = dt; sfuncname->Sfl = FLdata; out_readonly(sfuncname); outdata(sfuncname); elem *efuncname = el_ptr(sfuncname); elem *eparam = el_params(efuncname, el_long(TYsize_t, len), NULL); elem *e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_TRACE_CPRO]), eparam); block_appendexp(bx.curblock, e); } #if DMDV2 buildClosure(&irs); #endif #if TARGET_WINDOS if (func->isSynchronized() && cd && config.flags2 & CFG2seh && !func->isStatic() && !sbody->usesEH()) { /* 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 }
/******************************* * Do access check for member of this class, this class being the * type of the 'this' pointer used to access smember. * Returns true if the member is not accessible. */ bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember) { FuncDeclaration *f = sc->func; AggregateDeclaration *cdscope = sc->getStructClassScope(); #if LOG printf("AggregateDeclaration::checkAccess() for %s.%s in function %s() in scope %s\n", ad->toChars(), smember->toChars(), f ? f->toChars() : NULL, cdscope ? cdscope->toChars() : NULL); #endif Dsymbol *smemberparent = smember->toParent(); if (!smemberparent || !smemberparent->isAggregateDeclaration()) { #if LOG printf("not an aggregate member\n"); #endif return false; // then it is accessible } // BUG: should enable this check //assert(smember->parent->isBaseOf(this, NULL)); bool result; Prot access; if (smemberparent == ad) { Prot access2 = smember->prot(); result = access2.kind >= PROTpublic || hasPrivateAccess(ad, f) || isFriendOf(ad, cdscope) || (access2.kind == PROTpackage && hasPackageAccess(sc, ad)) || ad->getAccessModule() == sc->module; #if LOG printf("result1 = %d\n", result); #endif } else if ((access = getAccess(ad, smember)).kind >= PROTpublic) { result = true; #if LOG printf("result2 = %d\n", result); #endif } else if (access.kind == PROTpackage && hasPackageAccess(sc, ad)) { result = true; #if LOG printf("result3 = %d\n", result); #endif } else { result = isAccessible(smember, f, ad, cdscope); #if LOG printf("result4 = %d\n", result); #endif } if (!result) { ad->error(loc, "member %s is not accessible", smember->toChars()); //printf("smember = %s %s, prot = %d, semanticRun = %d\n", // smember->kind(), smember->toPrettyChars(), smember->prot(), smember->semanticRun); return true; } return false; }
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 = Lexer::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); if (!s) { ::error(Loc(), "ICE: %s not found in object module. You must update druntime", id->toChars()); fatal(); } 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); tf = (TypeFunction *)tf->semantic(loc, sc); 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, e1, Id::cmp), 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->xerrcmp; return fop; }
void AnonDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion) { //printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", this); if (decl) { /* This works by treating an AnonDeclaration as an aggregate 'member', * so in order to place that member we need to compute the member's * size and alignment. */ size_t fieldstart = ad->fields.dim; /* Hackishly hijack ad's structsize and alignsize fields * for use in our fake anon aggregate member. */ unsigned savestructsize = ad->structsize; unsigned savealignsize = ad->alignsize; ad->structsize = 0; ad->alignsize = 0; unsigned offset = 0; for (size_t i = 0; i < decl->dim; i++) { Dsymbol *s = (*decl)[i]; s->setFieldOffset(ad, &offset, this->isunion); if (this->isunion) offset = 0; } unsigned anonstructsize = ad->structsize; unsigned anonalignsize = ad->alignsize; ad->structsize = savestructsize; ad->alignsize = savealignsize; // 0 sized structs are set to 1 byte if (anonstructsize == 0) { anonstructsize = 1; anonalignsize = 1; } /* Given the anon 'member's size and alignment, * go ahead and place it. */ unsigned anonoffset = AggregateDeclaration::placeField( poffset, anonstructsize, anonalignsize, alignment, &ad->structsize, &ad->alignsize, isunion); // Add to the anon fields the base offset of this anonymous aggregate //printf("anon fields, anonoffset = %d\n", anonoffset); for (size_t i = fieldstart; i < ad->fields.dim; i++) { VarDeclaration *v = ad->fields[i]; //printf("\t[%d] %s %d\n", i, v->toChars(), v->offset); v->offset += anonoffset; } } }
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", toChars()); StorageClass stc = STCsafe | STCnothrow | STCpure; Loc declLoc = sd->loc; Loc loc = Loc(); // internal code should have no loc to prevent coverage if (sd->dtor || sd->postblit) { if (sd->dtor) { 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) { TypeStruct *ts = (TypeStruct *)tv; if (FuncDeclaration *f = hasIdentityOpAssign(ts->sym, sc)) stc = mergeFuncAttrs(stc, f); } } } Parameters *fparams = new Parameters; fparams->push(new Parameter(STCnodtor, sd->type, Id::p, NULL)); Type *tf = new TypeFunction(fparams, sd->handleType(), 0, LINKd, stc | STCref); FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), Id::assign, stc, tf); Expression *e = NULL; if (stc & STCdisable) { } else if (sd->dtor || sd->postblit) { /* Do swap this and rhs * tmp = this; this = s; tmp.dtor(); */ //printf("\tswap copy\n"); Identifier *idtmp = Lexer::uniqueId("__tmp"); 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); } Dsymbol *s = fop; #if 1 // workaround until fixing issue 1528 Dsymbol *assign = search_function(sd, 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); s = tempdecl; } #endif sd->members->push(s); s->addMember(sc, sd, 1); sd->hasIdentityAssign = true; // 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 *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 = Lexer::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); if (!s) { ::error(Loc(), "ICE: %s not found in object module. You must update druntime", id->toChars()); fatal(); } 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); tf = (TypeFunction *)tf->semantic(loc, sc); 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; }
// Put out instance of ModuleInfo for this Module void Module::genmoduleinfo() { // resolve ModuleInfo if (!moduleinfo) { error("object.d is missing the ModuleInfo struct"); fatal(); } // check for patch else { // The base struct should consist only of _flags/_index. if (moduleinfo->structsize != 4 + 4) { error("object.d ModuleInfo class is incorrect"); fatal(); } } // use the RTTIBuilder RTTIBuilder b(moduleinfo); // some types LLType* moduleinfoTy = moduleinfo->type->irtype->getLLType(); LLType* classinfoTy = Type::typeinfoclass->type->irtype->getLLType(); // importedModules[] std::vector<LLConstant*> importInits; LLConstant* importedModules = 0; llvm::ArrayType* importedModulesTy = 0; for (size_t i = 0; i < aimports.dim; i++) { Module *m = static_cast<Module *>(aimports.data[i]); if (!m->needModuleInfo() || m == this) continue; // declare the imported module info std::string m_name("_D"); m_name.append(m->mangle()); m_name.append("12__ModuleInfoZ"); llvm::GlobalVariable* m_gvar = gIR->module->getGlobalVariable(m_name); if (!m_gvar) m_gvar = new llvm::GlobalVariable(*gIR->module, moduleinfoTy, false, llvm::GlobalValue::ExternalLinkage, NULL, m_name); importInits.push_back(m_gvar); } // has import array? if (!importInits.empty()) { importedModulesTy = llvm::ArrayType::get(getPtrToType(moduleinfoTy), importInits.size()); importedModules = LLConstantArray::get(importedModulesTy, importInits); } // localClasses[] LLConstant* localClasses = 0; llvm::ArrayType* localClassesTy = 0; ClassDeclarations aclasses; //printf("members->dim = %d\n", members->dim); for (size_t i = 0; i < members->dim; i++) { Dsymbol *member; member = static_cast<Dsymbol *>(members->data[i]); //printf("\tmember '%s'\n", member->toChars()); member->addLocalClass(&aclasses); } // fill inits std::vector<LLConstant*> classInits; for (size_t i = 0; i < aclasses.dim; i++) { ClassDeclaration* cd = aclasses[i]; DtoResolveClass(cd); if (cd->isInterfaceDeclaration()) { IF_LOG Logger::println("skipping interface '%s' in moduleinfo", cd->toPrettyChars()); continue; } else if (cd->sizeok != SIZEOKdone) { IF_LOG Logger::println("skipping opaque class declaration '%s' in moduleinfo", cd->toPrettyChars()); continue; } IF_LOG Logger::println("class: %s", cd->toPrettyChars()); LLConstant *c = DtoBitCast(cd->ir.irAggr->getClassInfoSymbol(), classinfoTy); classInits.push_back(c); } // has class array? if (!classInits.empty()) { localClassesTy = llvm::ArrayType::get(classinfoTy, classInits.size()); localClasses = LLConstantArray::get(localClassesTy, classInits); } // These must match the values in druntime/src/object_.d #define MIstandalone 4 #define MItlsctor 8 #define MItlsdtor 0x10 #define MIctor 0x20 #define MIdtor 0x40 #define MIxgetMembers 0x80 #define MIictor 0x100 #define MIunitTest 0x200 #define MIimportedModules 0x400 #define MIlocalClasses 0x800 #define MInew 0x80000000 // it's the "new" layout llvm::Function* fsharedctor = build_module_shared_ctor(); llvm::Function* fshareddtor = build_module_shared_dtor(); llvm::Function* funittest = build_module_unittest(); llvm::Function* fctor = build_module_ctor(); llvm::Function* fdtor = build_module_dtor(); unsigned flags = MInew; if (fctor) flags |= MItlsctor; if (fdtor) flags |= MItlsdtor; if (fsharedctor) flags |= MIctor; if (fshareddtor) flags |= MIdtor; #if 0 if (fgetmembers) flags |= MIxgetMembers; if (fictor) flags |= MIictor; #endif if (funittest) flags |= MIunitTest; if (importedModules) flags |= MIimportedModules; if (localClasses) flags |= MIlocalClasses; if (!needmoduleinfo) flags |= MIstandalone; b.push_uint(flags); // flags b.push_uint(0); // index if (fctor) b.push(fctor); if (fdtor) b.push(fdtor); if (fsharedctor) b.push(fsharedctor); if (fshareddtor) b.push(fshareddtor); #if 0 if (fgetmembers) b.push(fgetmembers); if (fictor) b.push(fictor); #endif if (funittest) b.push(funittest); if (importedModules) { b.push_size(importInits.size()); b.push(importedModules); } if (localClasses) { b.push_size(classInits.size()); b.push(localClasses); } // Put out module name as a 0-terminated string. const char *name = toPrettyChars(); const size_t len = strlen(name) + 1; llvm::IntegerType *it = llvm::IntegerType::getInt8Ty(gIR->context()); llvm::ArrayType *at = llvm::ArrayType::get(it, len); b.push(toConstantArray(it, at, name, len, false)); // create and set initializer LLGlobalVariable *moduleInfoSym = moduleInfoSymbol(); b.finalize(moduleInfoSym->getType()->getPointerElementType(), moduleInfoSym); moduleInfoSym->setLinkage(llvm::GlobalValue::ExternalLinkage); if (global.params.isLinux) { build_dso_registry_calls(moduleInfoSym); } else { // build the modulereference and ctor for registering it LLFunction* mictor = build_module_reference_and_ctor(moduleInfoSym); AppendFunctionToLLVMGlobalCtorsDtors(mictor, 65535, true); } }
Initializer *StructInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInterpret) { //printf("StructInitializer::semantic(t = %s) %s\n", t->toChars(), toChars()); t = t->toBasetype(); if (t->ty == Tsarray && t->nextOf()->toBasetype()->ty == Tstruct) t = t->nextOf()->toBasetype(); if (t->ty == Tstruct) { StructDeclaration *sd = ((TypeStruct *)t)->sym; if (sd->ctor) { error(loc, "%s %s has constructors, cannot use { initializers }, use %s( initializers ) instead", sd->kind(), sd->toChars(), sd->toChars()); return new ErrorInitializer(); } sd->size(loc); if (sd->sizeok != SIZEOKdone) return new ErrorInitializer(); size_t nfields = sd->fields.dim - sd->isNested(); //expandTuples for non-identity arguments? Expressions *elements = new Expressions(); elements->setDim(nfields); for (size_t i = 0; i < elements->dim; i++) (*elements)[i] = NULL; // Run semantic for explicitly given initializers // TODO: this part is slightly different from StructLiteralExp::semantic. bool errors = false; for (size_t fieldi = 0, i = 0; i < field.dim; i++) { if (Identifier *id = field[i]) { Dsymbol *s = sd->search(loc, id); if (!s) { s = sd->search_correct(id); if (s) error(loc, "'%s' is not a member of '%s', did you mean '%s %s'?", id->toChars(), sd->toChars(), s->kind(), s->toChars()); else error(loc, "'%s' is not a member of '%s'", id->toChars(), sd->toChars()); return new ErrorInitializer(); } s = s->toAlias(); // Find out which field index it is for (fieldi = 0; 1; fieldi++) { if (fieldi >= nfields) { error(loc, "%s.%s is not a per-instance initializable field", sd->toChars(), s->toChars()); return new ErrorInitializer(); } if (s == sd->fields[fieldi]) break; } } else if (fieldi >= nfields) { error(loc, "too many initializers for %s", sd->toChars()); return new ErrorInitializer(); } VarDeclaration *vd = sd->fields[fieldi]; if ((*elements)[fieldi]) { error(loc, "duplicate initializer for field '%s'", vd->toChars()); errors = true; continue; } for (size_t j = 0; j < nfields; j++) { VarDeclaration *v2 = sd->fields[j]; bool overlap = (vd->offset < v2->offset + v2->type->size() && v2->offset < vd->offset + vd->type->size()); if (overlap && (*elements)[j]) { error(loc, "overlapping initialization for field %s and %s", v2->toChars(), vd->toChars()); errors = true; continue; } } assert(sc); Initializer *iz = value[i]; iz = iz->semantic(sc, vd->type->addMod(t->mod), needInterpret); Expression *ex = iz->toExpression(); if (ex->op == TOKerror) { errors = true; continue; } value[i] = iz; (*elements)[fieldi] = ex; ++fieldi; } if (errors) return new ErrorInitializer(); StructLiteralExp *sle = new StructLiteralExp(loc, sd, elements, t); if (!sd->fill(loc, elements, false)) return new ErrorInitializer(); sle->type = t; ExpInitializer *ie = new ExpInitializer(loc, sle); return ie->semantic(sc, t, needInterpret); } else if ((t->ty == Tdelegate || t->ty == Tpointer && t->nextOf()->ty == Tfunction) && value.dim == 0) { TOK tok = (t->ty == Tdelegate) ? TOKdelegate : TOKfunction; /* Rewrite as empty delegate literal { } */ Parameters *parameters = new Parameters; Type *tf = new TypeFunction(parameters, NULL, 0, LINKd); FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, Loc(), tf, tok, NULL); fd->fbody = new CompoundStatement(loc, new Statements()); fd->endloc = loc; Expression *e = new FuncExp(loc, fd); ExpInitializer *ie = new ExpInitializer(loc, e); return ie->semantic(sc, t, needInterpret); } error(loc, "a struct is not a valid initializer for a %s", t->toChars()); return new ErrorInitializer(); }
void StructDeclaration::semantic(Scope *sc) { //printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", this, parent->toChars(), toChars(), sizeok); //static int count; if (++count == 20) halt(); if (semanticRun >= PASSsemanticdone) return; unsigned dprogress_save = Module::dprogress; int errors = global.errors; Scope *scx = NULL; if (scope) { sc = scope; scx = scope; // save so we don't make redundant copies scope = NULL; } if (!parent) { assert(sc->parent && sc->func); parent = sc->parent; } assert(parent && !isAnonymous()); type = type->semantic(loc, sc); if (type->ty == Tstruct && ((TypeStruct *)type)->sym != this) { TemplateInstance *ti = ((TypeStruct *)type)->sym->isInstantiated(); if (ti && isError(ti)) ((TypeStruct *)type)->sym = this; } // Ungag errors when not speculative Ungag ungag = ungagSpeculative(); if (semanticRun == PASSinit) { protection = sc->protection; alignment = sc->structalign; storage_class |= sc->stc; if (storage_class & STCdeprecated) isdeprecated = true; if (storage_class & STCabstract) error("structs, unions cannot be abstract"); userAttribDecl = sc->userAttribDecl; } else if (symtab) { if (sizeok == SIZEOKdone || !scx) { semanticRun = PASSsemanticdone; return; } } semanticRun = PASSsemantic; if (!members) // if opaque declaration { semanticRun = PASSsemanticdone; return; } if (!symtab) symtab = new DsymbolTable(); 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; Scope *sc2 = sc->push(this); sc2->stc &= STCsafe | STCtrusted | STCsystem; sc2->parent = this; if (isUnionDeclaration()) sc2->inunion = 1; sc2->protection = Prot(PROTpublic); sc2->explicitProtection = 0; sc2->structalign = STRUCTALIGN_DEFAULT; sc2->userAttribDecl = 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]; //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]; s->importAll(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); } s->semantic(sc2); } 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++) { VarDeclaration *vd = fields[i]; vd->offset = 0; } fields.setDim(0); structsize = 0; alignsize = 0; scope = scx ? scx : sc->copy(); scope->setNoFree(); scope->module->addDeferredSemantic(this); Module::dprogress = dprogress_save; //printf("\tdeferring %s\n", toChars()); return; } Module::dprogress++; semanticRun = PASSsemanticdone; //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++) { VarDeclaration *vd = fields[i]; if (!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; } } } } dtor = buildDtor(this, sc2); postblit = buildPostBlit(this, sc2); cpctor = buildCpCtor(this, sc2); buildOpAssign(this, sc2); buildOpEquals(this, sc2); xeq = buildXopEquals(this, sc2); xcmp = buildXopCmp(this, sc2); xhash = buildXtoHash(this, sc2); /* Even if the struct is merely imported and its semantic3 is not run, * the TypeInfo object would be speculatively stored in each object * files. To set correct function pointer, run semantic3 for xeq and xcmp. */ //if ((xeq && xeq != xerreq || xcmp && xcmp != xerrcmp) && isImportedSym(this)) // Module::addDeferredSemantic3(this); /* Defer requesting semantic3 until TypeInfo generation is actually invoked. * See semanticTypeInfo(). */ inv = buildInv(this, sc2); sc2->pop(); /* Look for special member functions. */ ctor = searchCtor(); aggNew = (NewDeclaration *)search(Loc(), Id::classNew); aggDelete = (DeleteDeclaration *)search(Loc(), Id::classDelete); if (ctor) { Dsymbol *scall = search(Loc(), Id::call); if (scall) { unsigned xerrors = global.startGagging(); sc = sc->push(); sc->speculative = true; FuncDeclaration *fcall = resolveFuncCall(loc, sc, scall, NULL, NULL, NULL, 1); sc = sc->pop(); global.endGagging(xerrors); if (fcall && fcall->isStatic()) { error(fcall->loc, "static opCall is hidden by constructors and can never be called"); errorSupplemental(fcall->loc, "Please use a factory method instead, or replace all constructors with static opCall."); } } } TypeTuple *tup = toArgTypes(type); 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); if (global.errors != errors) { // The type is no good. type = Type::terror; this->errors = true; if (deferred) deferred->errors = true; } 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); }
/********************************* * Operator overloading for op= */ Expression *BinAssignExp::op_overload(Scope *sc) { //printf("BinAssignExp::op_overload() (%s)\n", toChars()); #if DMDV2 if (e1->op == TOKarray) { ArrayExp *ae = (ArrayExp *)e1; ae->e1 = ae->e1->semantic(sc); ae->e1 = resolveProperties(sc, ae->e1); AggregateDeclaration *ad = isAggregate(ae->e1->type); if (ad) { /* Rewrite a[args]+=e2 as: * a.opIndexOpAssign!("+")(e2, args); */ Dsymbol *fd = search_function(ad, Id::opIndexOpAssign); if (fd) { Expression *e0 = resolveOpDollar(sc, ae); Expressions *a = (Expressions *)ae->arguments->copy(); a->insert(0, e2); Objects *tiargs = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, ae->e1, fd->ident, tiargs); e = new CallExp(loc, e, a); e = combine(e0, e); e = e->semantic(sc); return e; } // Didn't find it. Forward to aliasthis if (ad->aliasthis && ae->e1->type != att1) { /* Rewrite a[arguments] op= e2 as: * a.aliasthis[arguments] op= e2 */ Expression *e1 = ae->copy(); ((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att1 && ae->e1->type->checkAliasThisRec()) be->att1 = ae->e1->type; be->e1 = e1; if (Expression *e = be->trySemantic(sc)) return e; } att1 = NULL; } } else if (e1->op == TOKslice) { SliceExp *se = (SliceExp *)e1; se->e1 = se->e1->semantic(sc); se->e1 = resolveProperties(sc, se->e1); AggregateDeclaration *ad = isAggregate(se->e1->type); if (ad) { /* Rewrite a[lwr..upr]+=e2 as: * a.opSliceOpAssign!("+")(e2, lwr, upr); */ Dsymbol *fd = search_function(ad, Id::opSliceOpAssign); if (fd) { Expression *e0 = resolveOpDollar(sc, se); Expressions *a = new Expressions(); a->push(e2); assert(!se->lwr || se->upr); if (se->lwr) { a->push(se->lwr); a->push(se->upr); } Objects *tiargs = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, se->e1, fd->ident, tiargs); e = new CallExp(loc, e, a); e = combine(e0, e); e = e->semantic(sc); return e; } // Didn't find it. Forward to aliasthis if (ad->aliasthis && se->e1->type != att1) { /* Rewrite a[lwr..upr] op= e2 as: * a.aliasthis[lwr..upr] op= e2 */ Expression *e1 = se->copy(); ((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att1 && se->e1->type->checkAliasThisRec()) be->att1 = se->e1->type; be->e1 = e1; if (Expression *e = be->trySemantic(sc)) return e; } att1 = NULL; } } #endif BinExp::semantic(sc); e1 = resolveProperties(sc, e1); e2 = resolveProperties(sc, e2); // Don't attempt 'alias this' if an error occured if (e1->type->ty == Terror || e2->type->ty == Terror) return new ErrorExp(); Identifier *id = opId(); Expressions args2; AggregateDeclaration *ad1 = isAggregate(e1->type); Dsymbol *s = NULL; #if 1 // the old D1 scheme if (ad1 && id) { s = search_function(ad1, id); } #endif Objects *tiargs = NULL; #if DMDV2 if (!s) { /* Try the new D2 scheme, opOpAssign */ if (ad1) { s = search_function(ad1, Id::opOpAssign); if (s && !s->isTemplateDeclaration()) { error("%s.opOpAssign isn't a template", e1->toChars()); return new ErrorExp(); } } // Set tiargs, the template argument list, which will be the operator string if (s) { id = Id::opOpAssign; tiargs = opToArg(sc, op); } } #endif if (s) { /* Try: * a.opOpAssign(b) */ args2.setDim(1); args2[0] = e2; Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; if (s) overloadResolveX(&m, s, loc, sc, tiargs, e1, &args2); if (m.count > 1) { // Error, ambiguous error("overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), m.nextf->type->toChars(), m.lastf->toChars()); } else if (m.last == MATCHnomatch) { m.lastf = m.anyf; if (tiargs) goto L1; } // Rewrite (e1 op e2) as e1.opOpAssign(e2) return build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); } L1: #if DMDV2 // Try alias this on first operand if (ad1 && ad1->aliasthis) { /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ if (att1 && this->e1->type == att1) return NULL; //printf("att %s e1 = %s\n", Token::toChars(op), this->e1->type->toChars()); Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att1 && this->e1->type->checkAliasThisRec()) be->att1 = this->e1->type; be->e1 = e1; return be->trySemantic(sc); } // Try alias this on second operand AggregateDeclaration *ad2 = isAggregate(e2->type); if (ad2 && ad2->aliasthis) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ if (att2 && this->e2->type == att2) return NULL; //printf("att %s e2 = %s\n", Token::toChars(op), this->e2->type->toChars()); Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att2 && this->e2->type->checkAliasThisRec()) be->att2 = this->e2->type; be->e2 = e2; return be->trySemantic(sc); } #endif return NULL; }
void PragmaDeclaration::semantic(Scope *sc) { // Should be merged with PragmaStatement //printf("\tPragmaDeclaration::semantic '%s'\n",toChars()); if (ident == Id::msg) { if (args) { for (size_t i = 0; i < args->dim; i++) { Expression *e = (*args)[i]; e = e->semantic(sc); e = resolveProperties(sc, e); if (e->op != TOKerror && e->op != TOKtype) e = e->ctfeInterpret(); if (e->op == TOKerror) { errorSupplemental(loc, "while evaluating pragma(msg, %s)", (*args)[i]->toChars()); return; } StringExp *se = e->toString(); if (se) { fprintf(stdmsg, "%.*s", (int)se->len, (char *)se->string); } else fprintf(stdmsg, "%s", e->toChars()); } fprintf(stdmsg, "\n"); } goto Lnodecl; } else if (ident == Id::lib) { if (!args || args->dim != 1) error("string expected for library name"); else { Expression *e = (*args)[0]; e = e->semantic(sc); e = resolveProperties(sc, e); e = e->ctfeInterpret(); (*args)[0] = e; if (e->op == TOKerror) goto Lnodecl; StringExp *se = e->toString(); if (!se) error("string expected for library name, not '%s'", e->toChars()); else if (global.params.verbose) { char *name = (char *)mem.malloc(se->len + 1); memcpy(name, se->string, se->len); name[se->len] = 0; printf("library %s\n", name); mem.free(name); } } goto Lnodecl; } #ifdef IN_GCC else if (ident == Id::GNU_asm) { if (! args || args->dim != 2) error("identifier and string expected for asm name"); else { Expression *e; Declaration *d = NULL; StringExp *s = NULL; e = (*args)[0]; e = e->semantic(sc); if (e->op == TOKvar) { d = ((VarExp *)e)->var; if (! d->isFuncDeclaration() && ! d->isVarDeclaration()) d = NULL; } if (!d) error("first argument of GNU_asm must be a function or variable declaration"); e = (*args)[1]; e = e->semantic(sc); e = resolveProperties(sc, e); e = e->ctfeInterpret(); e = e->toString(); if (e && ((StringExp *)e)->sz == 1) s = ((StringExp *)e); else error("second argument of GNU_asm must be a character string"); if (d && s) d->c_ident = Lexer::idPool((char*) s->string); } goto Lnodecl; } #endif #if DMDV2 else if (ident == Id::startaddress) { if (!args || args->dim != 1) error("function name expected for start address"); else { Expression *e = (*args)[0]; e = e->semantic(sc); e = resolveProperties(sc, e); e = e->ctfeInterpret(); (*args)[0] = e; Dsymbol *sa = getDsymbol(e); if (!sa || !sa->isFuncDeclaration()) error("function name expected for start address, not '%s'", e->toChars()); } goto Lnodecl; } #endif #if TARGET_NET else if (ident == Lexer::idPool("assembly")) { } #endif // TARGET_NET else if (global.params.ignoreUnsupportedPragmas) { if (global.params.verbose) { /* Print unrecognized pragmas */ printf("pragma %s", ident->toChars()); if (args) { for (size_t i = 0; i < args->dim; i++) { Expression *e = (*args)[i]; e = e->semantic(sc); e = resolveProperties(sc, e); e = e->ctfeInterpret(); if (i == 0) printf(" ("); else printf(","); printf("%s", e->toChars()); } if (args->dim) printf(")"); } printf("\n"); } goto Lnodecl; } else error("unrecognized pragma(%s)", ident->toChars()); Ldecl: if (decl) { for (size_t i = 0; i < decl->dim; i++) { Dsymbol *s = (*decl)[i]; s->semantic(sc); } } return; Lnodecl: if (decl) { error("pragma is missing closing ';'"); goto Ldecl; // do them anyway, to avoid segfaults. } }
void Module::genobjfile(int multiobj) { //EEcontext *ee = env->getEEcontext(); //printf("Module::genobjfile(multiobj = %d) %s\n", multiobj, toChars()); lastmname = srcfile->toChars(); objmod->initfile(lastmname, NULL, toPrettyChars()); eictor = NULL; ictorlocalgot = NULL; sctors.setDim(0); ectorgates.setDim(0); sdtors.setDim(0); ssharedctors.setDim(0); esharedctorgates.setDim(0); sshareddtors.setDim(0); stests.setDim(0); dtorcount = 0; shareddtorcount = 0; if (doppelganger) { /* Generate a reference to the moduleinfo, so the module constructors * and destructors get linked in. */ Module *m = aimports[0]; assert(m); if (m->sictor || m->sctor || m->sdtor || m->ssharedctor || m->sshareddtor) { Symbol *s = m->toSymbol(); //objextern(s); //if (!s->Sxtrnnum) objextdef(s->Sident); if (!s->Sxtrnnum) { //printf("%s\n", s->Sident); #if 0 /* This should work, but causes optlink to fail in common/newlib.asm */ objextdef(s->Sident); #else Symbol *sref = symbol_generate(SCstatic, type_fake(TYnptr)); sref->Sfl = FLdata; dtxoff(&sref->Sdt, s, 0, TYnptr); outdata(sref); #endif } } } if (global.params.cov) { /* Create coverage identifier: * private uint[numlines] __coverage; */ cov = symbol_calloc("__coverage"); cov->Stype = type_fake(TYint); cov->Stype->Tmangle = mTYman_c; cov->Stype->Tcount++; cov->Sclass = SCstatic; cov->Sfl = FLdata; dtnzeros(&cov->Sdt, 4 * numlines); outdata(cov); slist_add(cov); covb = (unsigned *)calloc((numlines + 32) / 32, sizeof(*covb)); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *member = (*members)[i]; //printf("toObjFile %s %s\n", member->kind(), member->toChars()); member->toObjFile(multiobj); } if (global.params.cov) { /* Generate * bit[numlines] __bcoverage; */ Symbol *bcov = symbol_calloc("__bcoverage"); bcov->Stype = type_fake(TYuint); bcov->Stype->Tcount++; bcov->Sclass = SCstatic; bcov->Sfl = FLdata; dtnbytes(&bcov->Sdt, (numlines + 32) / 32 * sizeof(*covb), (char *)covb); outdata(bcov); free(covb); covb = NULL; /* Generate: * _d_cover_register(uint[] __coverage, BitArray __bcoverage, string filename); * and prepend it to the static constructor. */ /* t will be the type of the functions generated: * extern (C) void func(); */ type *t = type_function(TYnfunc, NULL, 0, false, tsvoid); t->Tmangle = mTYman_c; sictor = toSymbolX("__modictor", SCglobal, t, "FZv"); cstate.CSpsymtab = &sictor->Sfunc->Flocsym; localgot = ictorlocalgot; elem *ecov = el_pair(TYdarray, el_long(TYsize_t, numlines), el_ptr(cov)); elem *ebcov = el_pair(TYdarray, el_long(TYsize_t, numlines), el_ptr(bcov)); if (config.exe == EX_WIN64) { ecov = addressElem(ecov, Type::tvoid->arrayOf(), false); ebcov = addressElem(ebcov, Type::tvoid->arrayOf(), false); } elem *e = el_params(ecov, ebcov, toEfilename(), NULL); e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_DCOVER]), e); eictor = el_combine(e, eictor); ictorlocalgot = localgot; } // If coverage / static constructor / destructor / unittest calls if (eictor || sctors.dim || ectorgates.dim || sdtors.dim || ssharedctors.dim || esharedctorgates.dim || sshareddtors.dim || stests.dim) { if (eictor) { localgot = ictorlocalgot; block *b = block_calloc(); b->BC = BCret; b->Belem = eictor; sictor->Sfunc->Fstartline.Sfilename = arg; sictor->Sfunc->Fstartblock = b; writefunc(sictor); } sctor = callFuncsAndGates(this, &sctors, &ectorgates, "__modctor"); sdtor = callFuncsAndGates(this, &sdtors, NULL, "__moddtor"); #if DMDV2 ssharedctor = callFuncsAndGates(this, &ssharedctors, (StaticDtorDeclarations *)&esharedctorgates, "__modsharedctor"); sshareddtor = callFuncsAndGates(this, &sshareddtors, NULL, "__modshareddtor"); #endif stest = callFuncsAndGates(this, &stests, NULL, "__modtest"); if (doppelganger) genmoduleinfo(); } if (doppelganger) { objmod->termfile(); return; } if (global.params.multiobj) { /* This is necessary because the main .obj for this module is written * first, but determining whether marray or massert or munittest are needed is done * possibly later in the doppelganger modules. * Another way to fix it is do the main one last. */ toModuleAssert(); toModuleUnittest(); toModuleArray(); } /* Always generate module info, because of templates and -cov. * But module info needs the runtime library, so disable it for betterC. */ if (!global.params.betterC /*|| needModuleInfo()*/) genmoduleinfo(); // If module assert for (int i = 0; i < 3; i++) { Symbol *ma; unsigned rt; unsigned bc; switch (i) { case 0: ma = marray; rt = RTLSYM_DARRAY; bc = BCexit; break; case 1: ma = massert; rt = RTLSYM_DASSERTM; bc = BCexit; break; case 2: ma = munittest; rt = RTLSYM_DUNITTESTM; bc = BCret; break; default: assert(0); } if (ma) { elem *elinnum; localgot = NULL; // Call dassert(filename, line) // Get sole parameter, linnum { Symbol *sp = symbol_calloc("linnum"); sp->Stype = type_fake(TYint); sp->Stype->Tcount++; sp->Sclass = (config.exe == EX_WIN64) ? SCshadowreg : SCfastpar; FuncParamRegs fpr(TYjfunc); fpr.alloc(sp->Stype, sp->Stype->Tty, &sp->Spreg, &sp->Spreg2); sp->Sflags &= ~SFLspill; sp->Sfl = (sp->Sclass == SCshadowreg) ? FLpara : FLfast; cstate.CSpsymtab = &ma->Sfunc->Flocsym; symbol_add(sp); elinnum = el_var(sp); } elem *efilename = el_ptr(toSymbol()); elem *e = el_var(rtlsym[rt]); e = el_bin(OPcall, TYvoid, e, el_param(elinnum, efilename)); block *b = block_calloc(); b->BC = bc; b->Belem = e; ma->Sfunc->Fstartline.Sfilename = arg; ma->Sfunc->Fstartblock = b; ma->Sclass = SCglobal; ma->Sfl = 0; ma->Sflags |= rtlsym[rt]->Sflags & SFLexit; writefunc(ma); } } objmod->termfile(); }
Expression *StructInitializer::fill(Scope *sc, Type *t, NeedInterpret needInterpret) { //printf("StructInitializer::fill(sc = %p, '%s')\n", sc, toChars()); assert(t->ty == Tstruct); StructDeclaration *sd = ((TypeStruct *)t)->sym; sd->size(loc); if (sd->sizeok != SIZEOKdone) return new ErrorExp(); size_t nfields = sd->fields.dim - sd->isNested(); Expressions *elements = new Expressions(); elements->setDim(nfields); for (size_t i = 0; i < elements->dim; i++) (*elements)[i] = NULL; // Run semantic for explicitly given initializers bool errors = false; for (size_t fieldi = 0, i = 0; i < field.dim; i++) { if (Identifier *id = field[i]) { Dsymbol *s = sd->search(loc, id, 0); if (!s) { s = sd->search_correct(id); if (s) error(loc, "'%s' is not a member of '%s', did you mean '%s %s'?", id->toChars(), sd->toChars(), s->kind(), s->toChars()); else error(loc, "'%s' is not a member of '%s'", id->toChars(), sd->toChars()); return new ErrorExp(); } s = s->toAlias(); // Find out which field index it is for (fieldi = 0; 1; fieldi++) { if (fieldi >= nfields) { error(loc, "%s.%s is not a per-instance initializable field", sd->toChars(), s->toChars()); return new ErrorExp(); } if (s == sd->fields[fieldi]) break; } } else if (fieldi >= nfields) { error(loc, "too many initializers for %s", sd->toChars()); return new ErrorExp(); } VarDeclaration *vd = sd->fields[fieldi]; if ((*elements)[fieldi]) { error(loc, "duplicate initializer for field '%s'", vd->toChars()); errors = true; continue; } for (size_t j = 0; j < nfields; j++) { VarDeclaration *v2 = sd->fields[j]; bool overlap = (vd->offset < v2->offset + v2->type->size() && v2->offset < vd->offset + vd->type->size()); if (overlap && (*elements)[j]) { error(loc, "overlapping initialization for field %s and %s", v2->toChars(), vd->toChars()); errors = true; continue; } } assert(sc); Initializer *iz = value[i]; iz = iz->semantic(sc, vd->type->addMod(t->mod), needInterpret); Expression *ex = iz->toExpression(); if (ex->op == TOKerror) { errors = true; continue; } value[i] = iz; (*elements)[fieldi] = ex; ++fieldi; } if (errors) return new ErrorExp(); // Fill in missing any elements with default initializers for (size_t i = 0; i < elements->dim; i++) { if ((*elements)[i]) continue; VarDeclaration *vd = sd->fields[i]; VarDeclaration *vx = vd; if (vd->init && vd->init->isVoidInitializer()) vx = NULL; // Find overlapped fields with the hole [vd->offset .. vd->offset->size()]. size_t fieldi = i; for (size_t j = 0; j < nfields; j++) { if (i == j) continue; VarDeclaration *v2 = sd->fields[j]; if (v2->init && v2->init->isVoidInitializer()) continue; bool overlap = (vd->offset < v2->offset + v2->type->size() && v2->offset < vd->offset + vd->type->size()); if (!overlap) continue; if ((*elements)[j]) { vx = NULL; break; } #if 1 /* Prefer first found non-void-initialized field * union U { int a; int b = 2; } * U u; // Error: overlapping initialization for field a and b */ if (!vx) vx = v2, fieldi = j; else if (v2->init) { error(loc, "overlapping initialization for field %s and %s", v2->toChars(), vd->toChars()); } #else // fix Bugzilla 1432 /* Prefer explicitly initialized field * union U { int a; int b = 2; } * U u; // OK (u.b == 2) */ if (!vx || !vx->init && v2->init) vx = v2, fieldi = j; else if (vx->init && v2->init) { error(loc, "overlapping default initialization for field %s and %s", v2->toChars(), vd->toChars()); } else assert(vx->init || !vx->init && !v2->init); #endif } if (vx) { if (vx->init) { assert(!vx->init->isVoidInitializer()); if (vx->scope) { // Do deferred semantic analysis Initializer *i2 = vx->init->syntaxCopy(); i2 = i2->semantic(vx->scope, vx->type, INITinterpret); (*elements)[fieldi] = i2->toExpression(); if (!global.gag) { vx->scope = NULL; vx->init = i2; // save result } } else (*elements)[fieldi] = vx->init->toExpression(); } else (*elements)[fieldi] = vx->type->defaultInit(); } } for (size_t i = 0; i < elements->dim; i++) { Expression *e = (*elements)[i]; if (e && e->op == TOKerror) return e; } Expression *e = new StructLiteralExp(loc, sd, elements, t); if (sc) e = e->semantic(sc); else e->type = sd->type; // from glue layer return e; }
void obj_write_deferred(Library *library) { for (size_t i = 0; i < obj_symbols_towrite.dim; i++) { Dsymbol *s = obj_symbols_towrite[i]; Module *m = s->getModule(); char *mname; if (m) { mname = m->srcfile->toChars(); lastmname = mname; } else { //mname = s->ident->toChars(); mname = lastmname; assert(mname); } obj_start(mname); static int count; count++; // sequence for generating names /* Create a module that's a doppelganger of m, with just * enough to be able to create the moduleinfo. */ OutBuffer idbuf; idbuf.printf("%s.%d", m ? m->ident->toChars() : mname, count); char *idstr = idbuf.toChars(); idbuf.data = NULL; Identifier *id = new Identifier(idstr, TOKidentifier); Module *md = new Module(mname, id, 0, 0); md->members = new Dsymbols(); md->members->push(s); // its only 'member' is s if (m) { md->doppelganger = 1; // identify this module as doppelganger md->md = m->md; md->aimports.push(m); // it only 'imports' m md->massert = m->massert; md->munittest = m->munittest; md->marray = m->marray; } md->genobjfile(0); /* Set object file name to be source name with sequence number, * as mangled symbol names get way too long. */ const char *fname = FileName::removeExt(mname); OutBuffer namebuf; unsigned hash = 0; for (char *p = s->toChars(); *p; p++) hash += *p; namebuf.printf("%s_%x_%x.%s", fname, count, hash, global.obj_ext); namebuf.writeByte(0); FileName::free((char *)fname); fname = (char *)namebuf.extractData(); //printf("writing '%s'\n", fname); File *objfile = new File(fname); obj_end(library, objfile); } obj_symbols_towrite.dim = 0; }
void FuncDeclaration_toObjFile(FuncDeclaration *fd, bool multiobj) { ClassDeclaration *cd = fd->parent->isClassDeclaration(); //printf("FuncDeclaration::toObjFile(%p, %s.%s)\n", fd, fd->parent->toChars(), fd->toChars()); //if (type) printf("type = %s\n", type->toChars()); #if 0 //printf("line = %d\n", getWhere() / LINEINC); EEcontext *ee = env->getEEcontext(); if (ee->EEcompile == 2) { if (ee->EElinnum < (getWhere() / LINEINC) || ee->EElinnum > (endwhere / LINEINC) ) return; // don't compile this function ee->EEfunc = toSymbol(this); } #endif if (fd->semanticRun >= PASSobj) // if toObjFile() already run return; if (fd->type && fd->type->ty == Tfunction && ((TypeFunction *)fd->type)->next == NULL) return; // If errors occurred compiling it, such as bugzilla 6118 if (fd->type && fd->type->ty == Tfunction && ((TypeFunction *)fd->type)->next->ty == Terror) return; if (fd->semantic3Errors) return; if (global.errors) return; if (!fd->fbody) return; UnitTestDeclaration *ud = fd->isUnitTestDeclaration(); if (ud && !global.params.useUnitTests) return; if (multiobj && !fd->isStaticDtorDeclaration() && !fd->isStaticCtorDeclaration()) { obj_append(fd); return; } if (fd->semanticRun == PASSsemanticdone) { /* What happened is this function failed semantic3() with errors, * but the errors were gagged. * Try to reproduce those errors, and then fail. */ fd->error("errors compiling the function"); return; } assert(fd->semanticRun == PASSsemantic3done); assert(fd->ident != Id::empty); for (FuncDeclaration *fd2 = fd; fd2; ) { if (fd2->inNonRoot()) return; if (fd2->isNested()) fd2 = fd2->toParent2()->isFuncDeclaration(); else break; } if (UnitTestDeclaration *udp = needsDeferredNested(fd)) { /* Can't do unittest's out of order, they are order dependent in that their * execution is done in lexical order. */ udp->deferredNested.push(fd); //printf("%s @[%s]\n\t--> pushed to unittest @[%s]\n", // fd->toPrettyChars(), fd->loc.toChars(), udp->loc.toChars()); return; } if (fd->isArrayOp && isDruntimeArrayOp(fd->ident)) { // Implementation is in druntime return; } // start code generation fd->semanticRun = PASSobj; if (global.params.verbose) fprintf(global.stdmsg, "function %s\n", fd->toPrettyChars()); Symbol *s = toSymbol(fd); func_t *f = s->Sfunc; // tunnel type of "this" to debug info generation if (AggregateDeclaration* ad = fd->parent->isAggregateDeclaration()) { ::type* t = Type_toCtype(ad->getType()); if (cd) t = t->Tnext; // skip reference f->Fclass = (Classsym *)t; } /* 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 (fd->isVirtual() && (fd->fensure || fd->frequire)) f->Fflags3 |= Ffakeeh; #if TARGET_OSX s->Sclass = SCcomdat; #else s->Sclass = SCglobal; #endif for (Dsymbol *p = fd->parent; p; p = p->parent) { if (p->isTemplateInstance()) { s->Sclass = SCcomdat; break; } } /* Vector operations should be comdat's */ if (fd->isArrayOp) s->Sclass = SCcomdat; if (fd->inlinedNestedCallees) { /* Bugzilla 15333: If fd contains inlined expressions that come from * nested function bodies, the enclosing of the functions must be * generated first, in order to calculate correct frame pointer offset. */ for (size_t i = 0; i < fd->inlinedNestedCallees->dim; i++) { FuncDeclaration *f = (*fd->inlinedNestedCallees)[i]; FuncDeclaration *fp = f->toParent2()->isFuncDeclaration();; if (fp && fp->semanticRun < PASSobj) { toObjFile(fp, multiobj); } } } if (fd->isNested()) { //if (!(config.flags3 & CFG3pic)) // s->Sclass = SCstatic; f->Fflags3 |= Fnested; /* The enclosing function must have its code generated first, * in order to calculate correct frame pointer offset. */ FuncDeclaration *fdp = fd->toParent2()->isFuncDeclaration(); if (fdp && fdp->semanticRun < PASSobj) { toObjFile(fdp, multiobj); } } else { const char *libname = (global.params.symdebug) ? global.params.debuglibname : global.params.defaultlibname; // Pull in RTL startup code (but only once) if (fd->isMain() && onlyOneMain(fd->loc)) { #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS objmod->external_def("_main"); objmod->ehsections(); // initialize exception handling sections #endif if (global.params.mscoff) { objmod->external_def("main"); objmod->ehsections(); // initialize exception handling sections } else if (config.exe == EX_WIN32) { objmod->external_def("_main"); objmod->external_def("__acrtused_con"); } objmod->includelib(libname); s->Sclass = SCglobal; } else if (strcmp(s->Sident, "main") == 0 && fd->linkage == LINKc) { if (global.params.mscoff) { objmod->includelib("LIBCMT"); objmod->includelib("OLDNAMES"); } else if (config.exe == EX_WIN32) { objmod->external_def("__acrtused_con"); // bring in C startup code objmod->includelib("snn.lib"); // bring in C runtime library } s->Sclass = SCglobal; } #if TARGET_WINDOS else if (fd->isWinMain() && onlyOneMain(fd->loc)) { if (global.params.mscoff) { 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 (fd->isDllMain() && onlyOneMain(fd->loc)) { if (global.params.mscoff) { 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 } symtab_t *symtabsave = cstate.CSpsymtab; cstate.CSpsymtab = &f->Flocsym; // Find module m for this function Module *m = NULL; for (Dsymbol *p = fd->parent; p; p = p->parent) { m = p->isModule(); if (m) break; } IRState irs(m, fd); Dsymbols deferToObj; // write these to OBJ file later irs.deferToObj = &deferToObj; void *labels = NULL; irs.labels = &labels; symbol *shidden = NULL; Symbol *sthis = NULL; tym_t tyf = tybasic(s->Stype->Tty); //printf("linkage = %d, tyf = x%x\n", linkage, tyf); int reverse = tyrevfunc(s->Stype->Tty); assert(fd->type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)fd->type; RET retmethod = retStyle(tf); if (retmethod == RETstack) { // If function returns a struct, put a pointer to that // as the first argument ::type *thidden = Type_toCtype(tf->next->pointerTo()); 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 (fd->nrvo_can && fd->nrvo_var && fd->nrvo_var->nestedrefs.dim) type_setcv(&shidden->Stype, shidden->Stype->Tty | mTYvolatile); irs.shidden = shidden; fd->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. fd->nrvo_can = 0; } if (fd->vthis) { assert(!fd->vthis->csym); sthis = toSymbol(fd->vthis); irs.sthis = sthis; if (!(f->Fflags3 & Fnested)) f->Fflags3 |= Fmember; } // Estimate number of parameters, pi size_t pi = (fd->v_arguments != NULL); if (fd->parameters) pi += fd->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 (fd->v_arguments) { params[pi] = toSymbol(fd->v_arguments); pi += 1; } if (fd->parameters) { for (size_t i = 0; i < fd->parameters->dim; i++) { VarDeclaration *v = (*fd->parameters)[i]; //printf("param[%d] = %p, %s\n", i, v, v->toChars()); assert(!v->csym); params[pi + i] = toSymbol(v); } pi += fd->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) && fd->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 (fd->fbody) { localgot = NULL; Statement *sbody = fd->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 = fd; bx.module = fd->getModule(); irs.blx = &bx; // Initialize argptr if (fd->v_argptr) { // Declare va_argsave if (global.params.is64bit && !global.params.isWindows) { type *t = type_struct_class("__va_argsave_t", 16, 8 * 6 + 8 * 16 + 8 * 3, NULL, NULL, false, false, true); // The backend will pick this up by name Symbol *s = symbol_name("__va_argsave", SCauto, t); s->Stype->Tty |= mTYvolatile; symbol_add(s); } Symbol *s = toSymbol(fd->v_argptr); symbol_add(s); elem *e = el_una(OPva_start, TYnptr, el_ptr(s)); block_appendexp(irs.blx->curblock, e); } /* 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 && !fd->isCMain()) { /* The profiler requires TLS, and TLS may not be set up yet when C main() * gets control (i.e. OSX), leading to a crash. */ /* Wrap the entire function body in: * trace_pro("funcname"); * try * body; * finally * _c_trace_epi(); */ StringExp *se = StringExp::create(Loc(), s->Sident); se->type = Type::tstring; se->type = se->type->semantic(Loc(), NULL); Expressions *exps = Expressions_create(); exps->push(se); FuncDeclaration *fdpro = FuncDeclaration::genCfunc(NULL, Type::tvoid, "trace_pro"); Expression *ec = VarExp::create(Loc(), fdpro); Expression *e = CallExp::create(Loc(), ec, exps); e->type = Type::tvoid; Statement *sp = ExpStatement::create(fd->loc, e); FuncDeclaration *fdepi = FuncDeclaration::genCfunc(NULL, Type::tvoid, "_c_trace_epi"); ec = VarExp::create(Loc(), fdepi); e = CallExp::create(Loc(), ec); e->type = Type::tvoid; Statement *sf = ExpStatement::create(fd->loc, e); Statement *stf; if (sbody->blockExit(fd, false) == BEfallthru) stf = CompoundStatement::create(Loc(), sbody, sf); else stf = TryFinallyStatement::create(Loc(), sbody, sf); sbody = CompoundStatement::create(Loc(), sp, stf); } if (fd->interfaceVirtual) { // Adjust the 'this' pointer instead of using a thunk assert(irs.sthis); elem *ethis = el_var(irs.sthis); elem *e = el_bin(OPminass, TYnptr, ethis, el_long(TYsize_t, fd->interfaceVirtual->offset)); block_appendexp(irs.blx->curblock, e); } buildClosure(fd, &irs); if (config.ehmethod == EH_WIN32 && fd->isSynchronized() && cd && !fd->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; } Statement_toIR(sbody, &irs); bx.curblock->BC = BCret; f->Fstartblock = bx.startblock; // einit = el_combine(einit,bx.init); if (fd->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)); } } } insertFinallyBlockCalls(f->Fstartblock); } // If static constructor if (fd->isSharedStaticCtorDeclaration()) // must come first because it derives from StaticCtorDeclaration { ssharedctors.push(s); } else if (fd->isStaticCtorDeclaration()) { sctors.push(s); } // If static destructor if (fd->isSharedStaticDtorDeclaration()) // must come first because it derives from StaticDtorDeclaration { SharedStaticDtorDeclaration *f = fd->isSharedStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ esharedctorgates.push(f); } sshareddtors.shift(s); } else if (fd->isStaticDtorDeclaration()) { StaticDtorDeclaration *f = fd->isStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ ectorgates.push(f); } sdtors.shift(s); } // If unit test if (ud) { stests.push(s); } if (global.errors) { // Restore symbol table cstate.CSpsymtab = symtabsave; return; } writefunc(s); // Restore symbol table cstate.CSpsymtab = symtabsave; if (fd->isExport()) objmod->export_symbol(s, Para.offset); for (size_t i = 0; i < irs.deferToObj->dim; i++) { Dsymbol *s = (*irs.deferToObj)[i]; toObjFile(s, false); } if (ud) { for (size_t i = 0; i < ud->deferredNested.dim; i++) { FuncDeclaration *fd = ud->deferredNested[i]; toObjFile(fd, false); } } #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 (fd->ident && memcmp(fd->ident->toChars(), "_STD", 4) == 0) objmod->staticdtor(s); #endif if (irs.startaddress) { //printf("Setting start address\n"); objmod->startaddress(irs.startaddress); } }
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.tdata()[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; }
void obj_write_deferred(Library *library) { for (size_t i = 0; i < obj_symbols_towrite.dim; i++) { Dsymbol *s = obj_symbols_towrite[i]; Module *m = s->getModule(); char *mname; if (m) { mname = (char*)m->srcfile->toChars(); lastmname = mname; } else { //mname = s->ident->toChars(); mname = lastmname; assert(mname); } obj_start(mname); static int count; count++; // sequence for generating names /* Create a module that's a doppelganger of m, with just * enough to be able to create the moduleinfo. */ OutBuffer idbuf; idbuf.printf("%s.%d", m ? m->ident->toChars() : mname, count); char *idstr = idbuf.peekString(); if (!m) { // it doesn't make sense to make up a module if we don't know where to put the symbol // so output it into it's own object file without ModuleInfo objmod->initfile(idstr, NULL, mname); toObjFile(s, false); objmod->termfile(); } else { idbuf.data = NULL; Identifier *id = Identifier::create(idstr); Module *md = Module::create(mname, id, 0, 0); md->members = Dsymbols_create(); md->members->push(s); // its only 'member' is s md->doppelganger = 1; // identify this module as doppelganger md->md = m->md; md->aimports.push(m); // it only 'imports' m md->massert = m->massert; md->munittest = m->munittest; md->marray = m->marray; genObjFile(md, false); } /* Set object file name to be source name with sequence number, * as mangled symbol names get way too long. */ const char *fname = FileName::removeExt(mname); OutBuffer namebuf; unsigned hash = 0; for (char *p = s->toChars(); *p; p++) hash += *p; namebuf.printf("%s_%x_%x.%s", fname, count, hash, global.obj_ext); FileName::free((char *)fname); fname = namebuf.extractString(); //printf("writing '%s'\n", fname); File *objfile = File::create(fname); obj_end(library, objfile); } obj_symbols_towrite.dim = 0; }
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.tdata()[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->storage_class & STCfield); if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); dinteger_t dim = (tv->ty == Tsarray ? 1 : 0); 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) { 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 (dim == 0) { // 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, Lexer::idPool("__fieldPostBlit")); dd->storage_class |= stc; 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.tdata()[0]; default: e = NULL; for (size_t i = 0; i < postblits.dim; i++) { FuncDeclaration *fd = postblits.tdata()[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, Lexer::idPool("__aggrPostBlit")); dd->storage_class |= stc; dd->fbody = new ExpStatement(0, e); members->push(dd); dd->semantic(sc); return dd; } }
int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) { if (!arguments || !arguments->dim) return 0; if (sapply) // prefer opApply { for (size_t u = 0; u < arguments->dim; u++) { Parameter *arg = (*arguments)[u]; if (arg->type) { arg->type = arg->type->semantic(loc, sc); arg->type = arg->type->addStorageClass(arg->storageClass); } } Expression *ethis; Type *tab = aggr->type->toBasetype(); if (tab->ty == Tclass || tab->ty == Tstruct) ethis = aggr; else { assert(tab->ty == Tdelegate && aggr->op == TOKdelegate); ethis = ((DelegateExp *)aggr)->e1; } /* Look for like an * int opApply(int delegate(ref Type [, ...]) dg); * overload */ FuncDeclaration *fd = sapply->isFuncDeclaration(); if (fd) { sapply = inferApplyArgTypesX(ethis, fd, arguments); } #if 0 TemplateDeclaration *td = sapply->isTemplateDeclaration(); if (td) { inferApplyArgTypesZ(td, arguments); } #endif return sapply ? 1 : 0; } /* Return if no arguments need types. */ for (size_t u = 0; u < arguments->dim; u++) { Parameter *arg = (*arguments)[u]; if (!arg->type) break; } AggregateDeclaration *ad; Parameter *arg = (*arguments)[0]; Type *taggr = aggr->type; assert(taggr); Type *tab = taggr->toBasetype(); switch (tab->ty) { case Tarray: case Tsarray: case Ttuple: if (arguments->dim == 2) { if (!arg->type) { arg->type = Type::tsize_t; // key type arg->type = arg->type->addStorageClass(arg->storageClass); } arg = (*arguments)[1]; } if (!arg->type && tab->ty != Ttuple) { arg->type = tab->nextOf(); // value type arg->type = arg->type->addStorageClass(arg->storageClass); } break; case Taarray: { TypeAArray *taa = (TypeAArray *)tab; if (arguments->dim == 2) { if (!arg->type) { arg->type = taa->index; // key type arg->type = arg->type->addStorageClass(arg->storageClass); } arg = (*arguments)[1]; } if (!arg->type) { arg->type = taa->next; // value type arg->type = arg->type->addStorageClass(arg->storageClass); } break; } case Tclass: ad = ((TypeClass *)tab)->sym; goto Laggr; case Tstruct: ad = ((TypeStruct *)tab)->sym; goto Laggr; Laggr: if (arguments->dim == 1) { if (!arg->type) { /* Look for a front() or back() overload */ Identifier *id = (op == TOKforeach) ? Id::Ffront : Id::Fback; Dsymbol *s = ad->search(Loc(), id, 0); FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL; if (fd) { // Resolve inout qualifier of front type arg->type = fd->type->nextOf(); if (arg->type) { arg->type = arg->type->substWildTo(tab->mod); arg->type = arg->type->addStorageClass(arg->storageClass); } } else if (s && s->isTemplateDeclaration()) ; else if (s && s->isDeclaration()) arg->type = ((Declaration *)s)->type; else break; } break; } break; case Tdelegate: { if (!inferApplyArgTypesY((TypeFunction *)tab->nextOf(), arguments)) return 0; break; } default: break; // ignore error, caught later } return 1; }
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.tdata()[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; }
Expression *BinExp::op_overload(Scope *sc) { //printf("BinExp::op_overload() (%s)\n", toChars()); Identifier *id = opId(); Identifier *id_r = opId_r(); Expressions args1; Expressions args2; int argsset = 0; AggregateDeclaration *ad1 = isAggregate(e1->type); AggregateDeclaration *ad2 = isAggregate(e2->type); if (op == TOKassign && ad1 == ad2) { StructDeclaration *sd = ad1->isStructDeclaration(); if (sd && !sd->hasIdentityAssign) { /* This is bitwise struct assignment. */ return NULL; } } Dsymbol *s = NULL; Dsymbol *s_r = NULL; #if 1 // the old D1 scheme if (ad1 && id) { s = search_function(ad1, id); } if (ad2 && id_r) { s_r = search_function(ad2, id_r); } #endif Objects *tiargs = NULL; #if DMDV2 if (op == TOKplusplus || op == TOKminusminus) { // Bug4099 fix if (ad1 && search_function(ad1, Id::opUnary)) return NULL; } if (!s && !s_r && op != TOKequal && op != TOKnotequal && op != TOKassign && op != TOKplusplus && op != TOKminusminus) { /* Try the new D2 scheme, opBinary and opBinaryRight */ if (ad1) { s = search_function(ad1, Id::opBinary); if (s && !s->isTemplateDeclaration()) { e1->error("%s.opBinary isn't a template", e1->toChars()); return new ErrorExp(); } } if (ad2) { s_r = search_function(ad2, Id::opBinaryRight); if (s_r && !s_r->isTemplateDeclaration()) { e2->error("%s.opBinaryRight isn't a template", e2->toChars()); return new ErrorExp(); } } // Set tiargs, the template argument list, which will be the operator string if (s || s_r) { id = Id::opBinary; id_r = Id::opBinaryRight; tiargs = opToArg(sc, op); } } #endif if (s || s_r) { /* Try: * a.opfunc(b) * b.opfunc_r(a) * and see which is better. */ args1.setDim(1); args1[0] = e1; args2.setDim(1); args2[0] = e2; argsset = 1; Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; if (s) overloadResolveX(&m, s, loc, sc, tiargs, e1, &args2); FuncDeclaration *lastf = m.lastf; if (s_r) overloadResolveX(&m, s_r, loc, sc, tiargs, e2, &args1); if (m.count > 1) { // Error, ambiguous error("overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), m.nextf->type->toChars(), m.lastf->toChars()); } else if (m.last == MATCHnomatch) { m.lastf = m.anyf; if (tiargs) goto L1; } Expression *e; if (op == TOKplusplus || op == TOKminusminus) // Kludge because operator overloading regards e++ and e-- // as unary, but it's implemented as a binary. // Rewrite (e1 ++ e2) as e1.postinc() // Rewrite (e1 -- e2) as e1.postdec() e = build_overload(loc, sc, e1, NULL, m.lastf ? m.lastf : s); else if (lastf && m.lastf == lastf || !s_r && m.last == MATCHnomatch) // Rewrite (e1 op e2) as e1.opfunc(e2) e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); else // Rewrite (e1 op e2) as e2.opfunc_r(e1) e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s_r); return e; } L1: #if 1 // Retained for D1 compatibility if (isCommutative() && !tiargs) { s = NULL; s_r = NULL; if (ad1 && id_r) { s_r = search_function(ad1, id_r); } if (ad2 && id) { s = search_function(ad2, id); } if (s || s_r) { /* Try: * a.opfunc_r(b) * b.opfunc(a) * and see which is better. */ if (!argsset) { args1.setDim(1); args1[0] = e1; args2.setDim(1); args2[0] = e2; } Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; if (s_r) overloadResolveX(&m, s_r, loc, sc, tiargs, e1, &args2); FuncDeclaration *lastf = m.lastf; if (s) overloadResolveX(&m, s, loc, sc, tiargs, e2, &args1); if (m.count > 1) { // Error, ambiguous error("overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), m.nextf->type->toChars(), m.lastf->toChars()); } else if (m.last == MATCHnomatch) { m.lastf = m.anyf; } Expression *e; if (lastf && m.lastf == lastf || !s && m.last == MATCHnomatch) // Rewrite (e1 op e2) as e1.opfunc_r(e2) e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s_r); else // Rewrite (e1 op e2) as e2.opfunc(e1) e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s); // When reversing operands of comparison operators, // need to reverse the sense of the op switch (op) { case TOKlt: op = TOKgt; break; case TOKgt: op = TOKlt; break; case TOKle: op = TOKge; break; case TOKge: op = TOKle; break; // Floating point compares case TOKule: op = TOKuge; break; case TOKul: op = TOKug; break; case TOKuge: op = TOKule; break; case TOKug: op = TOKul; break; // These are symmetric case TOKunord: case TOKlg: case TOKleg: case TOKue: break; } return e; } } #endif #if DMDV2 // Try alias this on first operand if (ad1 && ad1->aliasthis && !(op == TOKassign && ad2 && ad1 == ad2)) // See Bugzilla 2943 { /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ if (att1 && this->e1->type == att1) return NULL; //printf("att bin e1 = %s\n", this->e1->type->toChars()); Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att1 && this->e1->type->checkAliasThisRec()) be->att1 = this->e1->type; be->e1 = e1; return be->trySemantic(sc); } // Try alias this on second operand if (ad2 && ad2->aliasthis && /* Bugzilla 2943: make sure that when we're copying the struct, we don't * just copy the alias this member */ !(op == TOKassign && ad1 && ad1 == ad2)) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ if (att2 && this->e2->type == att2) return NULL; //printf("att bin e2 = %s\n", this->e2->type->toChars()); Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att2 && this->e2->type->checkAliasThisRec()) be->att2 = this->e2->type; be->e2 = e2; return be->trySemantic(sc); } #endif return NULL; }
Dsymbol *Dsymbol::searchX(Loc loc, Scope *sc, Identifier *id) { //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident->toChars()); Dsymbol *s = toAlias(); Dsymbol *sm; switch (id->dyncast()) { case DYNCAST_IDENTIFIER: sm = s->search(loc, id, 0); break; case DYNCAST_DSYMBOL: { // It's a template instance //printf("\ttemplate instance id\n"); Dsymbol *st = (Dsymbol *)id; TemplateInstance *ti = st->isTemplateInstance(); id = ti->name; sm = s->search(loc, id, 0); if (!sm) { error("template identifier %s is not a member of %s %s", id->toChars(), s->kind(), s->toChars()); return NULL; } sm = sm->toAlias(); TemplateDeclaration *td = sm->isTemplateDeclaration(); if (!td) { error("%s is not a template, it is a %s", id->toChars(), sm->kind()); return NULL; } ti->tempdecl = td; if (!ti->semanticRun) ti->semantic(sc); sm = ti->toAlias(); break; } default: assert(0); } return sm; }
/****************************************** * Common code for overloading of EqualExp and CmpExp */ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) { //printf("BinExp::compare_overload(id = %s) %s\n", id->toChars(), toChars()); AggregateDeclaration *ad1 = isAggregate(e1->type); AggregateDeclaration *ad2 = isAggregate(e2->type); Dsymbol *s = NULL; Dsymbol *s_r = NULL; if (ad1) { s = search_function(ad1, id); } if (ad2) { s_r = search_function(ad2, id); if (s == s_r) s_r = NULL; } Objects *tiargs = NULL; if (s || s_r) { /* Try: * a.opEquals(b) * b.opEquals(a) * and see which is better. */ Expressions args1; Expressions args2; args1.setDim(1); args1[0] = e1; args2.setDim(1); args2[0] = e2; Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; if (0 && s && s_r) { printf("s : %s\n", s->toPrettyChars()); printf("s_r: %s\n", s_r->toPrettyChars()); } if (s) overloadResolveX(&m, s, loc, sc, tiargs, e1, &args2); FuncDeclaration *lastf = m.lastf; int count = m.count; if (s_r) overloadResolveX(&m, s_r, loc, sc, tiargs, e2, &args1); if (m.count > 1) { /* The following if says "not ambiguous" if there's one match * from s and one from s_r, in which case we pick s. * This doesn't follow the spec, but is a workaround for the case * where opEquals was generated from templates and we cannot figure * out if both s and s_r came from the same declaration or not. * The test case is: * import std.typecons; * void main() { * assert(tuple("has a", 2u) == tuple("has a", 1)); * } */ if (!(m.lastf == lastf && m.count == 2 && count == 1)) { // Error, ambiguous error("overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), m.nextf->type->toChars(), m.lastf->toChars()); } } else if (m.last == MATCHnomatch) { m.lastf = m.anyf; } Expression *e; if (lastf && m.lastf == lastf || !s_r && m.last == MATCHnomatch) // Rewrite (e1 op e2) as e1.opfunc(e2) e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); else { // Rewrite (e1 op e2) as e2.opfunc_r(e1) e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s_r); // When reversing operands of comparison operators, // need to reverse the sense of the op switch (op) { case TOKlt: op = TOKgt; break; case TOKgt: op = TOKlt; break; case TOKle: op = TOKge; break; case TOKge: op = TOKle; break; // Floating point compares case TOKule: op = TOKuge; break; case TOKul: op = TOKug; break; case TOKuge: op = TOKule; break; case TOKug: op = TOKul; break; // The rest are symmetric default: break; } } return e; } // Try alias this on first operand if (ad1 && ad1->aliasthis) { /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ if (att1 && this->e1->type == att1) return NULL; //printf("att cmp_bin e1 = %s\n", this->e1->type->toChars()); Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att1 && this->e1->type->checkAliasThisRec()) be->att1 = this->e1->type; be->e1 = e1; return be->trySemantic(sc); } // Try alias this on second operand if (ad2 && ad2->aliasthis) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ if (att2 && this->e2->type == att2) return NULL; //printf("att cmp_bin e2 = %s\n", this->e2->type->toChars()); Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att2 && this->e2->type->checkAliasThisRec()) be->att2 = this->e2->type; be->e2 = e2; return be->trySemantic(sc); } return NULL; }
Expression *TraitsExp::semantic(Scope *sc) { #if LOGSEMANTIC printf("TraitsExp::semantic() %s\n", toChars()); #endif if (ident != Id::compiles && ident != Id::isSame && ident != Id::identifier) { TemplateInstance::semanticTiargs(loc, sc, args, 1); } size_t dim = args ? args->dim : 0; Object *o; Declaration *d; FuncDeclaration *f; #define ISTYPE(cond) \ for (size_t i = 0; i < dim; i++) \ { Type *t = getType((Object *)args->data[i]); \ if (!t) \ goto Lfalse; \ if (!(cond)) \ goto Lfalse; \ } \ if (!dim) \ goto Lfalse; \ goto Ltrue; #define ISDSYMBOL(cond) \ for (size_t i = 0; i < dim; i++) \ { Dsymbol *s = getDsymbol((Object *)args->data[i]); \ if (!s) \ goto Lfalse; \ if (!(cond)) \ goto Lfalse; \ } \ if (!dim) \ goto Lfalse; \ goto Ltrue; if (ident == Id::isArithmetic) { ISTYPE(t->isintegral() || t->isfloating()) } else if (ident == Id::isFloating) { ISTYPE(t->isfloating()) } else if (ident == Id::isIntegral) { ISTYPE(t->isintegral()) } else if (ident == Id::isScalar) { ISTYPE(t->isscalar()) } else if (ident == Id::isUnsigned) { ISTYPE(t->isunsigned()) } else if (ident == Id::isAssociativeArray) { ISTYPE(t->toBasetype()->ty == Taarray) } else if (ident == Id::isStaticArray) { ISTYPE(t->toBasetype()->ty == Tsarray) } else if (ident == Id::isAbstractClass) { ISTYPE(t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->isAbstract()) } else if (ident == Id::isFinalClass) { ISTYPE(t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal) } else if (ident == Id::isAbstractFunction) { ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isAbstract()) } else if (ident == Id::isVirtualFunction) { ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isVirtual()) } else if (ident == Id::isFinalFunction) { ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isFinal()) } #if DMDV2 else if (ident == Id::isStaticFunction) { ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && !f->needThis()) } else if (ident == Id::isRef) { ISDSYMBOL((d = s->isDeclaration()) != NULL && d->isRef()) } else if (ident == Id::isOut) { ISDSYMBOL((d = s->isDeclaration()) != NULL && d->isOut()) } else if (ident == Id::isLazy) { ISDSYMBOL((d = s->isDeclaration()) != NULL && d->storage_class & STClazy) } else if (ident == Id::identifier) { // Get identifier for symbol as a string literal // Specify 0 for the flags argument to semanticTiargs() so that // a symbol should not be folded to a constant. TemplateInstance::semanticTiargs(loc, sc, args, 0); if (dim != 1) goto Ldimerror; Object *o = (Object *)args->data[0]; Dsymbol *s = getDsymbol(o); if (!s || !s->ident) { error("argument %s has no identifier", o->toChars()); goto Lfalse; } StringExp *se = new StringExp(loc, s->ident->toChars()); return se->semantic(sc); } else if (ident == Id::parent) { if (dim != 1) goto Ldimerror; Object *o = (Object *)args->data[0]; Dsymbol *s = getDsymbol(o); if (s) s = s->toParent(); if (!s) { error("argument %s has no parent", o->toChars()); goto Lfalse; } return (new DsymbolExp(loc, s))->semantic(sc); } #endif else if (ident == Id::hasMember || ident == Id::getMember || ident == Id::getOverloads || ident == Id::getVirtualFunctions) { if (dim != 2) goto Ldimerror; Object *o = (Object *)args->data[0]; Expression *e = isExpression((Object *)args->data[1]); if (!e) { error("expression expected as second argument of __traits %s", ident->toChars()); goto Lfalse; } e = e->optimize(WANTvalue | WANTinterpret); if (e->op != TOKstring) { error("string expected as second argument of __traits %s instead of %s", ident->toChars(), e->toChars()); goto Lfalse; } StringExp *se = (StringExp *)e; se = se->toUTF8(sc); if (se->sz != 1) { error("string must be chars"); goto Lfalse; } Identifier *id = Lexer::idPool((char *)se->string); Type *t = isType(o); e = isExpression(o); Dsymbol *s = isDsymbol(o); if (t) e = typeDotIdExp(loc, t, id); else if (e) e = new DotIdExp(loc, e, id); else if (s) { e = new DsymbolExp(loc, s); e = new DotIdExp(loc, e, id); } else { error("invalid first argument"); goto Lfalse; } if (ident == Id::hasMember) { /* Take any errors as meaning it wasn't found */ e = e->trySemantic(sc); if (!e) { if (global.gag) global.errors++; goto Lfalse; } else goto Ltrue; } else if (ident == Id::getMember) { e = e->semantic(sc); return e; } else if (ident == Id::getVirtualFunctions || ident == Id::getOverloads) { unsigned errors = global.errors; Expression *ex = e; e = e->semantic(sc); if (errors < global.errors) error("%s cannot be resolved", ex->toChars()); /* Create tuple of functions of e */ //e->dump(0); Expressions *exps = new Expressions(); FuncDeclaration *f; if (e->op == TOKvar) { VarExp *ve = (VarExp *)e; f = ve->var->isFuncDeclaration(); } else if (e->op == TOKdotvar) { DotVarExp *dve = (DotVarExp *)e; f = dve->var->isFuncDeclaration(); } else f = NULL; Ptrait p; p.exps = exps; p.e1 = e; p.ident = ident; overloadApply(f, fptraits, &p); TupleExp *tup = new TupleExp(loc, exps); return tup->semantic(sc); } else assert(0); } else if (ident == Id::classInstanceSize) { if (dim != 1) goto Ldimerror; Object *o = (Object *)args->data[0]; Dsymbol *s = getDsymbol(o); ClassDeclaration *cd; if (!s || (cd = s->isClassDeclaration()) == NULL) { error("first argument is not a class"); goto Lfalse; } return new IntegerExp(loc, cd->structsize, Type::tsize_t); } else if (ident == Id::allMembers || ident == Id::derivedMembers) { if (dim != 1) goto Ldimerror; Object *o = (Object *)args->data[0]; Dsymbol *s = getDsymbol(o); ScopeDsymbol *sd; if (!s) { error("argument has no members"); goto Lfalse; } if ((sd = s->isScopeDsymbol()) == NULL) { error("%s %s has no members", s->kind(), s->toChars()); goto Lfalse; } Expressions *exps = new Expressions; while (1) { size_t dim = ScopeDsymbol::dim(sd->members); for (size_t i = 0; i < dim; i++) { Dsymbol *sm = ScopeDsymbol::getNth(sd->members, i); //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars()); if (sm->ident) { //printf("\t%s\n", sm->ident->toChars()); char *str = sm->ident->toChars(); /* Skip if already present in exps[] */ for (size_t j = 0; j < exps->dim; j++) { StringExp *se2 = (StringExp *)exps->data[j]; if (strcmp(str, (char *)se2->string) == 0) goto Lnext; } StringExp *se = new StringExp(loc, str); exps->push(se); } Lnext: ; } ClassDeclaration *cd = sd->isClassDeclaration(); if (cd && cd->baseClass && ident == Id::allMembers) sd = cd->baseClass; // do again with base class else break; } #if DMDV1 Expression *e = new ArrayLiteralExp(loc, exps); #endif #if DMDV2 /* Making this a tuple is more flexible, as it can be statically unrolled. * To make an array literal, enclose __traits in [ ]: * [ __traits(allMembers, ...) ] */ Expression *e = new TupleExp(loc, exps); #endif e = e->semantic(sc); return e; } else if (ident == Id::compiles) { /* Determine if all the objects - types, expressions, or symbols - * compile without error */ if (!dim) goto Lfalse; for (size_t i = 0; i < dim; i++) { Object *o = (Object *)args->data[i]; Expression *e; unsigned errors = global.errors; global.gag++; Type *t = isType(o); if (t) { Dsymbol *s; t->resolve(loc, sc, &e, &t, &s); if (t) t->semantic(loc, sc); else if (e) { e = e->semantic(sc); e = e->optimize(WANTvalue); } } else { e = isExpression(o); if (e) { e = e->semantic(sc); e = e->optimize(WANTvalue); } } global.gag--; if (errors != global.errors) { global.errors = errors; goto Lfalse; } } goto Ltrue; } else if (ident == Id::isSame) { /* Determine if two symbols are the same */ if (dim != 2) goto Ldimerror; TemplateInstance::semanticTiargs(loc, sc, args, 0); Object *o1 = (Object *)args->data[0]; Object *o2 = (Object *)args->data[1]; Dsymbol *s1 = getDsymbol(o1); Dsymbol *s2 = getDsymbol(o2); //printf("isSame: %s, %s\n", o1->toChars(), o2->toChars()); #if 0 printf("o1: %p\n", o1); printf("o2: %p\n", o2); if (!s1) { Expression *ea = isExpression(o1); if (ea) printf("%s\n", ea->toChars()); Type *ta = isType(o1); if (ta) printf("%s\n", ta->toChars()); goto Lfalse; } else printf("%s %s\n", s1->kind(), s1->toChars()); #endif if (!s1 && !s2) { Expression *ea1 = isExpression(o1); Expression *ea2 = isExpression(o2); if (ea1 && ea2) { if (ea1->equals(ea2)) goto Ltrue; } } if (!s1 || !s2) goto Lfalse; s1 = s1->toAlias(); s2 = s2->toAlias(); if (s1 == s2) goto Ltrue; else goto Lfalse; } else { error("unrecognized trait %s", ident->toChars()); goto Lfalse; } return NULL; Lnottype: error("%s is not a type", o->toChars()); goto Lfalse; Ldimerror: error("wrong number of arguments %d", dim); goto Lfalse; Lfalse: return new IntegerExp(loc, 0, Type::tbool); Ltrue: return new IntegerExp(loc, 1, Type::tbool); }
void Module::importAll(Scope *prevsc) { //printf("+Module::importAll(this = %p, '%s'): parent = %p\n", this, toChars(), parent); if (scope) return; // already done if (isDocFile) { error("is a Ddoc file, cannot import it"); return; } /* Note that modules get their own scope, from scratch. * This is so regardless of where in the syntax a module * gets imported, it is unaffected by context. * Ignore prevsc. */ Scope *sc = Scope::createGlobal(this); // create root scope // Add import of "object", even for the "object" module. // If it isn't there, some compiler rewrites, like // classinst == classinst -> .object.opEquals(classinst, classinst) // would fail inside object.d. if (members->dim == 0 || ((*members)[0])->ident != Id::object) { Import *im = new Import(0, NULL, Id::object, NULL, 0); members->shift(im); } if (!symtab) { // Add all symbols into module's symbol table symtab = new DsymbolTable(); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = members->tdata()[i]; s->addMember(NULL, sc->scopesym, 1); } } // anything else should be run after addMember, so version/debug symbols are defined /* Set scope for the symbols so that if we forward reference * a symbol, it can possibly be resolved on the spot. * If this works out well, it can be extended to all modules * before any semantic() on any of them. */ setScope(sc); // remember module scope for semantic for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = members->tdata()[i]; s->setScope(sc); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = members->tdata()[i]; s->importAll(sc); } sc = sc->pop(); sc->pop(); // 2 pops because Scope::createGlobal() created 2 }