dt_t *StructInitializer::toDt() { Dts dts; dt_t *dt; dt_t *d; dt_t **pdtend; unsigned offset; //printf("StructInitializer::toDt('%s')\n", toChars()); dts.setDim(ad->fields.dim); dts.zero(); for (size_t i = 0; i < vars.dim; i++) { VarDeclaration *v = vars[i]; Initializer *val = value[i]; //printf("vars[%d] = %s\n", i, v->toChars()); for (size_t j = 0; 1; j++) { assert(j < dts.dim); //printf(" adfield[%d] = %s\n", j, (ad->fields[j])->toChars()); if (ad->fields[j] == v) { if (dts[j]) error(loc, "field %s of %s already initialized", v->toChars(), ad->toChars()); dts[j] = val->toDt(); break; } } } dt = NULL; pdtend = &dt; offset = 0; for (size_t j = 0; j < dts.dim; j++) { VarDeclaration *v = ad->fields[j]; d = dts[j]; if (!d) { // An instance specific initializer was not provided. // Look to see if there's a default initializer from the // struct definition if (v->init && v->init->isVoidInitializer()) ; else if (v->init) { d = v->init->toDt(); } else if (v->offset >= offset) { unsigned k; unsigned offset2 = v->offset + v->type->size(); // Make sure this field does not overlap any explicitly // initialized field. for (k = j + 1; 1; k++) { if (k == dts.dim) // didn't find any overlap { v->type->toDt(&d); break; } VarDeclaration *v2 = ad->fields[k]; if (v2->offset < offset2 && dts[k]) break; // overlap } } } if (d) { if (v->offset < offset) error(loc, "duplicate union initialization for %s", v->toChars()); else { size_t sz = dt_size(d); size_t vsz = v->type->size(); size_t voffset = v->offset; if (sz > vsz) { assert(v->type->ty == Tsarray && vsz == 0); error(loc, "zero length array %s has non-zero length initializer", v->toChars()); } unsigned dim = 1; for (Type *vt = v->type->toBasetype(); vt->ty == Tsarray; vt = vt->nextOf()->toBasetype()) { TypeSArray *tsa = (TypeSArray *)vt; dim *= tsa->dim->toInteger(); } //printf("sz = %d, dim = %d, vsz = %d\n", sz, dim, vsz); assert(sz == vsz || sz * dim <= vsz); for (size_t i = 0; i < dim; i++) { if (offset < voffset) pdtend = dtnzeros(pdtend, voffset - offset); if (!d) { if (v->init) d = v->init->toDt(); else v->type->toDt(&d); } pdtend = dtcat(pdtend, d); d = NULL; offset = voffset + sz; voffset += vsz / dim; if (sz == vsz) break; } } } } if (offset < ad->structsize) dtnzeros(pdtend, ad->structsize - offset); #ifdef IN_GCC dt_t * cdt = NULL; dtcontainer(&cdt, ad->type, dt); dt = cdt; #endif return dt; }
void ClassDeclaration::toDt2(dt_t **pdt, ClassDeclaration *cd) { unsigned offset; dt_t *dt; unsigned csymoffset; #define LOG 0 #if LOG printf("ClassDeclaration::toDt2(this = '%s', cd = '%s')\n", toChars(), cd->toChars()); #endif if (baseClass) { baseClass->toDt2(pdt, cd); offset = baseClass->structsize; } else { offset = PTRSIZE * 2; } // Note equivalence of this loop to struct's for (size_t i = 0; i < fields.dim; i++) { VarDeclaration *v = fields[i]; Initializer *init; //printf("\t\tv = '%s' v->offset = %2d, offset = %2d\n", v->toChars(), v->offset, offset); dt = NULL; init = v->init; if (init) { //printf("\t\t%s has initializer %s\n", v->toChars(), init->toChars()); ExpInitializer *ei = init->isExpInitializer(); Type *tb = v->type->toBasetype(); if (init->isVoidInitializer()) ; else if (ei && tb->ty == Tsarray) ((TypeSArray *)tb)->toDtElem(&dt, ei->exp); else dt = init->toDt(); } else if (v->offset >= offset) { //printf("\t\tdefault initializer\n"); v->type->toDt(&dt); } if (dt) { if (v->offset < offset) error("duplicated union initialization for %s", v->toChars()); else { if (offset < v->offset) dtnzeros(pdt, v->offset - offset); dtcat(pdt, dt); offset = v->offset + v->type->size(); } } } // Interface vptr initializations toSymbol(); // define csym for (size_t i = 0; i < vtblInterfaces->dim; i++) { BaseClass *b = (*vtblInterfaces)[i]; #if 1 || INTERFACE_VIRTUAL for (ClassDeclaration *cd2 = cd; 1; cd2 = cd2->baseClass) { assert(cd2); csymoffset = cd2->baseVtblOffset(b); if (csymoffset != ~0) { if (offset < b->offset) dtnzeros(pdt, b->offset - offset); dtxoff(pdt, cd2->toSymbol(), csymoffset); break; } } #else csymoffset = baseVtblOffset(b); assert(csymoffset != ~0); dtxoff(pdt, csym, csymoffset); #endif offset = b->offset + PTRSIZE; } if (offset < structsize) dtnzeros(pdt, structsize - offset); #undef LOG }
void IrAggr::addFieldInitializers( llvm::SmallVectorImpl<llvm::Constant*>& constants, const VarInitMap& explicitInitializers, AggregateDeclaration* decl, unsigned& offset, bool populateInterfacesWithVtbls ) { if (ClassDeclaration* cd = decl->isClassDeclaration()) { if (cd->baseClass) { addFieldInitializers(constants, explicitInitializers, cd->baseClass, offset, populateInterfacesWithVtbls); } } // Build up vector with one-to-one mapping to field indices. const size_t n = decl->fields.dim; llvm::SmallVector<VarInitConst, 16> data(n); // Fill in explicit initializers. for (size_t i = 0; i < n; ++i) { VarDeclaration* vd = decl->fields[i]; VarInitMap::const_iterator expl = explicitInitializers.find(vd); if (expl != explicitInitializers.end()) data[i] = *expl; } // Fill in implicit initializers for (size_t i = 0; i < n; i++) { if (data[i].first) continue; VarDeclaration* vd = decl->fields[i]; /* Skip void initializers for unions. DMD bug 3991: union X { int a = void; dchar b = 'a'; } */ if (decl->isUnionDeclaration() && vd->init && vd->init->isVoidInitializer()) continue; unsigned vd_begin = vd->offset; unsigned vd_end = vd_begin + vd->type->size(); /* Skip zero size fields like zero-length static arrays, LDC issue 812: class B { ubyte[0] test; } */ if (vd_begin == vd_end) continue; // make sure it doesn't overlap any explicit initializers. bool overlaps = false; if (type->ty == Tstruct) { // Only structs and unions can have overlapping fields. for (size_t j = 0; j < n; ++j) { if (i == j || !data[j].first) continue; VarDeclaration* it = decl->fields[j]; unsigned f_begin = it->offset; unsigned f_end = f_begin + it->type->size(); if (vd_begin >= f_end || vd_end <= f_begin) continue; overlaps = true; break; } } // add if no overlap found if (!overlaps) { IF_LOG Logger::println("Implicit initializer: %s @+%u", vd->toChars(), vd->offset); LOG_SCOPE; data[i].first = vd; data[i].second = get_default_initializer(vd, NULL); } } // Sort data array by offset. // TODO: Figure out whether this is really necessary, fields should already // be in offset order. Not having do do this would mean we could use a plain // llvm::Constant* vector for initializers and avoid all the VarInitConst business. std::sort(data.begin(), data.end(), struct_init_data_sort); // build array of constants and make sure explicit zero padding is inserted when necessary. for (size_t i = 0; i < n; i++) { VarDeclaration* vd = data[i].first; if (vd == NULL) continue; // Explicitly zero the padding as per TDPL §7.1.1. Otherwise, it would // be left uninitialized by LLVM. if (offset < vd->offset) { add_zeros(constants, offset, vd->offset); offset = vd->offset; } IF_LOG Logger::println("adding field %s", vd->toChars()); constants.push_back(FillSArrayDims(vd->type, data[i].second)); offset += getMemberSize(vd->type); } if (ClassDeclaration* cd = decl->isClassDeclaration()) { // has interface vtbls? if (cd->vtblInterfaces && cd->vtblInterfaces->dim > 0) { // Align interface infos to pointer size. unsigned aligned = (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1); if (offset < aligned) { add_zeros(constants, offset, aligned); offset = aligned; } // false when it's not okay to use functions from super classes bool newinsts = (cd == aggrdecl->isClassDeclaration()); size_t inter_idx = interfacesWithVtbls.size(); offset = (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1); for (BaseClasses::iterator I = cd->vtblInterfaces->begin(), E = cd->vtblInterfaces->end(); I != E; ++I) { constants.push_back(getInterfaceVtbl(*I, newinsts, inter_idx)); offset += Target::ptrsize; inter_idx++; if (populateInterfacesWithVtbls) interfacesWithVtbls.push_back(*I); } } } }
/*************************************** * This works by transforming a struct initializer into * a struct literal. In the future, the two should be the * same thing. */ Expression *StructInitializer::toExpression() { Expression *e; size_t offset; //printf("StructInitializer::toExpression() %s\n", toChars()); if (!ad) // if fwd referenced { return NULL; } StructDeclaration *sd = ad->isStructDeclaration(); if (!sd) return NULL; Expressions *elements = new Expressions(); size_t nfields = ad->fields.dim; #if DMDV2 if (sd->isnested) nfields--; #endif elements->setDim(nfields); for (size_t i = 0; i < elements->dim; i++) { elements->tdata()[i] = NULL; } unsigned fieldi = 0; for (size_t i = 0; i < value.dim; i++) { Identifier *id = field.tdata()[i]; if (id) { Dsymbol * s = ad->search(loc, id, 0); if (!s) { error(loc, "'%s' is not a member of '%s'", id->toChars(), sd->toChars()); goto Lno; } s = s->toAlias(); // Find out which field index it is for (fieldi = 0; 1; fieldi++) { if (fieldi >= nfields) { s->error("is not a per-instance initializable field"); goto Lno; } if (s == ad->fields.tdata()[fieldi]) break; } } else if (fieldi >= nfields) { error(loc, "too many initializers for '%s'", ad->toChars()); goto Lno; } Initializer *iz = value.tdata()[i]; if (!iz) goto Lno; Expression *ex = iz->toExpression(); if (!ex) goto Lno; if (elements->tdata()[fieldi]) { error(loc, "duplicate initializer for field '%s'", ad->fields.tdata()[fieldi]->toChars()); goto Lno; } elements->tdata()[fieldi] = ex; ++fieldi; } // Now, fill in any missing elements with default initializers. // We also need to validate any anonymous unions offset = 0; for (size_t i = 0; i < elements->dim; ) { VarDeclaration * vd = ad->fields.tdata()[i]->isVarDeclaration(); //printf("test2 [%d] : %s %d %d\n", i, vd->toChars(), (int)offset, (int)vd->offset); if (vd->offset < offset) { // Only the first field of a union can have an initializer if (elements->tdata()[i]) goto Lno; } else { if (!elements->tdata()[i]) // Default initialize elements->tdata()[i] = vd->type->defaultInit(); } offset = vd->offset + vd->type->size(); i++; #if 0 int unionSize = ad->numFieldsInUnion(i); if (unionSize == 1) { // Not a union -- default initialize if missing if (!elements->tdata()[i]) elements->tdata()[i] = vd->type->defaultInit(); } else { // anonymous union -- check for errors int found = -1; // index of the first field with an initializer for (int j = i; j < i + unionSize; ++j) { if (!elements->tdata()[j]) continue; if (found >= 0) { VarDeclaration * v1 = ((Dsymbol *)ad->fields.data[found])->isVarDeclaration(); VarDeclaration * v = ((Dsymbol *)ad->fields.data[j])->isVarDeclaration(); error(loc, "%s cannot have initializers for fields %s and %s in same union", ad->toChars(), v1->toChars(), v->toChars()); goto Lno; } found = j; } if (found == -1) { error(loc, "no initializer for union that contains field %s", vd->toChars()); goto Lno; } } i += unionSize; #endif } e = new StructLiteralExp(loc, sd, elements); e->type = sd->type; return e; Lno: delete elements; return NULL; }
void AliasDeclaration::semantic(Scope *sc) { //printf("AliasDeclaration::semantic() %s\n", toChars()); if (aliassym) { if (aliassym->isTemplateInstance()) aliassym->semantic(sc); return; } this->inSemantic = 1; #if DMDV1 // don't really know why this is here if (storage_class & STCconst) error("cannot be const"); #endif storage_class |= sc->stc & STCdeprecated; // Given: // alias foo.bar.abc def; // it is not knowable from the syntax whether this is an alias // for a type or an alias for a symbol. It is up to the semantic() // pass to distinguish. // If it is a type, then type is set and getType() will return that // type. If it is a symbol, then aliassym is set and type is NULL - // toAlias() will return aliasssym. Dsymbol *s; Type *t; Expression *e; /* This section is needed because resolve() will: * const x = 3; * alias x y; * try to alias y to 3. */ s = type->toDsymbol(sc); if (s #if DMDV2 ` && ((s->getType() && type->equals(s->getType())) || s->isEnumMember()) #endif ) goto L2; // it's a symbolic alias #if DMDV2 type = type->addStorageClass(storage_class); if (storage_class & (STCref | STCnothrow | STCpure | STCdisable)) { // For 'ref' to be attached to function types, and picked // up by Type::resolve(), it has to go into sc. sc = sc->push(); sc->stc |= storage_class & (STCref | STCnothrow | STCpure | STCshared | STCdisable); type->resolve(loc, sc, &e, &t, &s); sc = sc->pop(); } else #endif type->resolve(loc, sc, &e, &t, &s); if (s) { goto L2; } else if (e) { // Try to convert Expression to Dsymbol if (e->op == TOKvar) { s = ((VarExp *)e)->var; goto L2; } else if (e->op == TOKfunction) { s = ((FuncExp *)e)->fd; goto L2; } else { error("cannot alias an expression %s", e->toChars()); t = e->type; } } else if (t) { type = t; } if (overnext) ScopeDsymbol::multiplyDefined(0, this, overnext); this->inSemantic = 0; return; L2: //printf("alias is a symbol %s %s\n", s->kind(), s->toChars()); type = NULL; VarDeclaration *v = s->isVarDeclaration(); if (0 && v && v->linkage == LINKdefault) { error("forward reference of %s", v->toChars()); s = NULL; } else { FuncDeclaration *f = s->toAlias()->isFuncDeclaration(); if (f) { if (overnext) { FuncAliasDeclaration *fa = new FuncAliasDeclaration(f); if (!fa->overloadInsert(overnext)) ScopeDsymbol::multiplyDefined(0, f, overnext); overnext = NULL; s = fa; s->parent = sc->parent; } } if (overnext) ScopeDsymbol::multiplyDefined(0, s, overnext); if (s == this) { assert(global.errors); s = NULL; } } //printf("setting aliassym %s to %s %s\n", toChars(), s->kind(), s->toChars()); aliassym = s; this->inSemantic = 0; }
/*************************************** * Fit elements[] to the corresponding type of field[]. * Input: * loc * sc * elements The explicit arguments that given to construct object. * stype The constructed object type. * Returns false if any errors occur. * Otherwise, returns true and elements[] are rewritten for the output. */ bool StructDeclaration::fit(Loc loc, Scope *sc, Expressions *elements, Type *stype) { if (!elements) return true; size_t nfields = fields.dim - isNested(); size_t offset = 0; for (size_t i = 0; i < elements->dim; i++) { Expression *e = (*elements)[i]; if (!e) continue; e = resolveProperties(sc, e); if (i >= nfields) { if (i == fields.dim - 1 && isNested() && e->op == TOKnull) { // CTFE sometimes creates null as hidden pointer; we'll allow this. continue; } ::error(loc, "more initializers than fields (%d) of %s", nfields, toChars()); return false; } VarDeclaration *v = fields[i]; if (v->offset < offset) { ::error(loc, "overlapping initialization for %s", v->toChars()); return false; } offset = (unsigned)(v->offset + v->type->size()); Type *t = v->type; if (stype) t = t->addMod(stype->mod); Type *origType = t; Type *tb = t->toBasetype(); /* Look for case of initializing a static array with a too-short * string literal, such as: * char[5] foo = "abc"; * Allow this by doing an explicit cast, which will lengthen the string * literal. */ if (e->op == TOKstring && tb->ty == Tsarray) { StringExp *se = (StringExp *)e; Type *typeb = se->type->toBasetype(); TY tynto = tb->nextOf()->ty; if (!se->committed && (typeb->ty == Tarray || typeb->ty == Tsarray) && (tynto == Tchar || tynto == Twchar || tynto == Tdchar) && se->length((int)tb->nextOf()->size()) < ((TypeSArray *)tb)->dim->toInteger()) { e = se->castTo(sc, t); goto L1; } } while (!e->implicitConvTo(t) && tb->ty == Tsarray) { /* Static array initialization, as in: * T[3][5] = e; */ t = tb->nextOf(); tb = t->toBasetype(); } if (!e->implicitConvTo(t)) t = origType; // restore type for better diagnostic e = e->implicitCastTo(sc, t); L1: if (e->op == TOKerror) return false; (*elements)[i] = e->isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e); } return true; }
void DtoCreateNestedContext(FuncDeclaration* fd) { Logger::println("DtoCreateNestedContext for %s", fd->toChars()); LOG_SCOPE DtoCreateNestedContextType(fd); if (nestedCtx == NCArray) { // construct nested variables array if (!fd->nestedVars.empty()) { Logger::println("has nested frame"); // start with adding all enclosing parent frames until a static parent is reached int nparelems = 0; if (!fd->isStatic()) { Dsymbol* par = fd->toParent2(); while (par) { if (FuncDeclaration* parfd = par->isFuncDeclaration()) { nparelems += parfd->nestedVars.size(); // stop at first static if (parfd->isStatic()) break; } else if (par->isClassDeclaration()) { // nothing needed } else { break; } par = par->toParent2(); } } int nelems = fd->nestedVars.size() + nparelems; // make array type for nested vars LLType* nestedVarsTy = LLArrayType::get(getVoidPtrType(), nelems); // alloca it // FIXME align ? LLValue* nestedVars = DtoRawAlloca(nestedVarsTy, 0, ".nested_vars"); IrFunction* irfunction = fd->ir.irFunc; // copy parent frame into beginning if (nparelems) { LLValue* src = irfunction->nestArg; if (!src) { assert(irfunction->thisArg); assert(fd->isMember2()); LLValue* thisval = DtoLoad(irfunction->thisArg); ClassDeclaration* cd = fd->isMember2()->isClassDeclaration(); assert(cd); assert(cd->vthis); src = DtoLoad(DtoGEPi(thisval, 0,cd->vthis->ir.irField->index, ".vthis")); } else { src = DtoLoad(src); } DtoMemCpy(nestedVars, src, DtoConstSize_t(nparelems*PTRSIZE), getABITypeAlign(getVoidPtrType())); } // store in IrFunction irfunction->nestedVar = nestedVars; // go through all nested vars and assign indices int idx = nparelems; for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) { VarDeclaration* vd = *i; if (!vd->ir.irLocal) vd->ir.irLocal = new IrLocal(vd); if (vd->isParameter()) { Logger::println("nested param: %s", vd->toChars()); LLValue* gep = DtoGEPi(nestedVars, 0, idx); LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType()); DtoAlignedStore(val, gep); } else { Logger::println("nested var: %s", vd->toChars()); } vd->ir.irLocal->nestedIndex = idx++; } } } else if (nestedCtx == NCHybrid) { // construct nested variables array if (!fd->nestedVars.empty()) { IrFunction* irfunction = fd->ir.irFunc; unsigned depth = irfunction->depth; LLStructType *frameType = irfunction->frameType; // Create frame for current function and append to frames list // FIXME: alignment ? LLValue* frame = 0; #if DMDV2 if (fd->needsClosure()) frame = DtoGcMalloc(frameType, ".frame"); else #endif frame = DtoRawAlloca(frameType, 0, ".frame"); // copy parent frames into beginning if (depth != 0) { LLValue* src = irfunction->nestArg; if (!src) { assert(irfunction->thisArg); assert(fd->isMember2()); LLValue* thisval = DtoLoad(irfunction->thisArg); #if DMDV2 AggregateDeclaration* cd = fd->isMember2(); #else ClassDeclaration* cd = fd->isMember2()->isClassDeclaration(); #endif assert(cd); assert(cd->vthis); Logger::println("Indexing to 'this'"); #if DMDV2 if (cd->isStructDeclaration()) src = DtoExtractValue(thisval, cd->vthis->ir.irField->index, ".vthis"); else #endif src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis")); } else { src = DtoLoad(src); } if (depth > 1) { src = DtoBitCast(src, getVoidPtrType()); LLValue* dst = DtoBitCast(frame, getVoidPtrType()); DtoMemCpy(dst, src, DtoConstSize_t((depth-1) * PTRSIZE), getABITypeAlign(getVoidPtrType())); } // Copy nestArg into framelist; the outer frame is not in the list of pointers src = DtoBitCast(src, frameType->getContainedType(depth-1)); LLValue* gep = DtoGEPi(frame, 0, depth-1); DtoAlignedStore(src, gep); } // store context in IrFunction irfunction->nestedVar = frame; // go through all nested vars and assign addresses where possible. for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) { VarDeclaration* vd = *i; LLValue* gep = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex, vd->toChars()); if (vd->isParameter()) { Logger::println("nested param: %s", vd->toChars()); LOG_SCOPE LLValue* value = vd->ir.irLocal->value; if (llvm::isa<llvm::AllocaInst>(llvm::GetUnderlyingObject(value))) { Logger::println("Copying to nested frame"); // The parameter value is an alloca'd stack slot. // Copy to the nesting frame and leave the alloca for // the optimizers to clean up. assert(!vd->ir.irLocal->byref); DtoStore(DtoLoad(value), gep); gep->takeName(value); vd->ir.irLocal->value = gep; } else { Logger::println("Adding pointer to nested frame"); // The parameter value is something else, such as a // passed-in pointer (for 'ref' or 'out' parameters) or // a pointer arg with byval attribute. // Store the address into the frame. assert(vd->ir.irLocal->byref); storeVariable(vd, gep); } } else if (vd->isRef() || vd->isOut()) { // This slot is initialized in DtoNestedInit, to handle things like byref foreach variables // which move around in memory. assert(vd->ir.irLocal->byref); } else { Logger::println("nested var: %s", vd->toChars()); if (vd->ir.irLocal->value) Logger::cout() << "Pre-existing value: " << *vd->ir.irLocal->value << '\n'; assert(!vd->ir.irLocal->value); vd->ir.irLocal->value = gep; assert(!vd->ir.irLocal->byref); } if (global.params.symdebug) { LLSmallVector<LLValue*, 2> addr; dwarfOpOffset(addr, frameType, vd->ir.irLocal->nestedIndex); DtoDwarfLocalVariable(frame, vd, addr); } } } else if (FuncDeclaration* parFunc = getParentFunc(fd, true)) { // Propagate context arg properties if the context arg is passed on unmodified. DtoDeclareFunction(parFunc); fd->ir.irFunc->frameType = parFunc->ir.irFunc->frameType; fd->ir.irFunc->depth = parFunc->ir.irFunc->depth; } } else { assert(0 && "Not implemented yet"); } }
void ClassDeclaration::semantic(Scope *sc) { //printf("ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); //printf("\tparent = %p, '%s'\n", sc->parent, sc->parent ? sc->parent->toChars() : ""); //printf("sc->stc = %x\n", sc->stc); //{ static int n; if (++n == 20) *(char*)0=0; } if (!ident) // if anonymous class { const char *id = "__anonclass"; ident = Identifier::generateId(id); } if (!sc) sc = scope; if (!parent && sc->parent && !sc->parent->isModule()) parent = sc->parent; type = type->semantic(loc, sc); handle = type; if (!members) // if opaque declaration { //printf("\tclass '%s' is forward referenced\n", toChars()); return; } if (symtab) { if (sizeok == SIZEOKdone || !scope) { //printf("\tsemantic for '%s' is already completed\n", toChars()); return; // semantic() already completed } } else symtab = new DsymbolTable(); Scope *scx = NULL; if (scope) { sc = scope; scx = scope; // save so we don't make redundant copies scope = NULL; } unsigned dprogress_save = Module::dprogress; int errors = global.errors; if (sc->stc & STCdeprecated) { isdeprecated = true; } userAttributes = sc->userAttributes; if (sc->linkage == LINKcpp) error("cannot create C++ classes"); // Expand any tuples in baseclasses[] for (size_t i = 0; i < baseclasses->dim; ) { BaseClass *b = (*baseclasses)[i]; unsigned oldgag = global.gag; if (global.isSpeculativeGagging() && !isSpeculative()) global.gag = 0; b->type = b->type->semantic(loc, sc); global.gag = oldgag; Type *tb = b->type->toBasetype(); if (tb->ty == Ttuple) { TypeTuple *tup = (TypeTuple *)tb; PROT protection = b->protection; baseclasses->remove(i); size_t dim = Parameter::dim(tup->arguments); for (size_t j = 0; j < dim; j++) { Parameter *arg = Parameter::getNth(tup->arguments, j); b = new BaseClass(arg->type, protection); baseclasses->insert(i + j, b); } } else i++; } // See if there's a base class as first in baseclasses[] if (baseclasses->dim) { TypeClass *tc; BaseClass *b; Type *tb; b = (*baseclasses)[0]; //b->type = b->type->semantic(loc, sc); tb = b->type->toBasetype(); if (tb->ty != Tclass) { if (b->type != Type::terror) error("base type must be class or interface, not %s", b->type->toChars()); baseclasses->remove(0); } else { tc = (TypeClass *)(tb); if (tc->sym->isDeprecated()) { if (!isDeprecated()) { // Deriving from deprecated class makes this one deprecated too isdeprecated = true; tc->checkDeprecated(loc, sc); } } if (tc->sym->isInterfaceDeclaration()) ; else { for (ClassDeclaration *cdb = tc->sym; cdb; cdb = cdb->baseClass) { if (cdb == this) { error("circular inheritance"); baseclasses->remove(0); goto L7; } } if (!tc->sym->symtab || tc->sym->sizeok == SIZEOKnone) { // Try to resolve forward reference if (/*doAncestorsSemantic == SemanticIn &&*/ tc->sym->scope) tc->sym->semantic(NULL); } if (!tc->sym->symtab || tc->sym->scope || tc->sym->sizeok == SIZEOKnone) { //printf("%s: forward reference of base class %s\n", toChars(), tc->sym->toChars()); //error("forward reference of base class %s", baseClass->toChars()); // Forward reference of base class, try again later //printf("\ttry later, forward reference of base class %s\n", tc->sym->toChars()); scope = scx ? scx : new Scope(*sc); scope->setNoFree(); if (tc->sym->scope) tc->sym->scope->module->addDeferredSemantic(tc->sym); scope->module->addDeferredSemantic(this); return; } else { baseClass = tc->sym; b->base = baseClass; } L7: ; } } } // Treat the remaining entries in baseclasses as interfaces // Check for errors, handle forward references for (size_t i = (baseClass ? 1 : 0); i < baseclasses->dim; ) { TypeClass *tc; BaseClass *b; Type *tb; b = (*baseclasses)[i]; b->type = b->type->semantic(loc, sc); tb = b->type->toBasetype(); if (tb->ty == Tclass) tc = (TypeClass *)tb; else tc = NULL; if (!tc || !tc->sym->isInterfaceDeclaration()) { if (b->type != Type::terror) error("base type must be interface, not %s", b->type->toChars()); baseclasses->remove(i); continue; } else { if (tc->sym->isDeprecated()) { if (!isDeprecated()) { // Deriving from deprecated class makes this one deprecated too isdeprecated = true; tc->checkDeprecated(loc, sc); } } // Check for duplicate interfaces for (size_t j = (baseClass ? 1 : 0); j < i; j++) { BaseClass *b2 = (*baseclasses)[j]; if (b2->base == tc->sym) error("inherits from duplicate interface %s", b2->base->toChars()); } if (!tc->sym->symtab) { // Try to resolve forward reference if (/*doAncestorsSemantic == SemanticIn &&*/ tc->sym->scope) tc->sym->semantic(NULL); } b->base = tc->sym; if (!b->base->symtab || b->base->scope) { //error("forward reference of base class %s", baseClass->toChars()); // Forward reference of base, try again later //printf("\ttry later, forward reference of base %s\n", baseClass->toChars()); scope = scx ? scx : new Scope(*sc); scope->setNoFree(); if (tc->sym->scope) tc->sym->scope->module->addDeferredSemantic(tc->sym); scope->module->addDeferredSemantic(this); return; } } i++; } if (doAncestorsSemantic == SemanticIn) doAncestorsSemantic = SemanticDone; // If no base class, and this is not an Object, use Object as base class if (!baseClass && ident != Id::Object) { if (!object) { error("missing or corrupt object.d"); fatal(); } Type *t = object->type; t = t->semantic(loc, sc)->toBasetype(); assert(t->ty == Tclass); TypeClass *tc = (TypeClass *)t; BaseClass *b = new BaseClass(tc, PROTpublic); baseclasses->shift(b); baseClass = tc->sym; assert(!baseClass->isInterfaceDeclaration()); b->base = baseClass; } interfaces_dim = baseclasses->dim; interfaces = baseclasses->tdata(); if (baseClass) { if (baseClass->storage_class & STCfinal) error("cannot inherit from final class %s", baseClass->toChars()); interfaces_dim--; interfaces++; // Copy vtbl[] from base class vtbl.setDim(baseClass->vtbl.dim); memcpy(vtbl.tdata(), baseClass->vtbl.tdata(), sizeof(void *) * vtbl.dim); // Inherit properties from base class com = baseClass->isCOMclass(); isscope = baseClass->isscope; vthis = baseClass->vthis; enclosing = baseClass->enclosing; storage_class |= baseClass->storage_class & STC_TYPECTOR; } else { // No base class, so this is the root of the class hierarchy vtbl.setDim(0); vtbl.push(this); // leave room for classinfo as first member } protection = sc->protection; storage_class |= sc->stc; if (sizeok == SIZEOKnone) { interfaceSemantic(sc); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->addMember(sc, this, 1); } /* If this is a nested class, add the hidden 'this' * member which is a pointer to the enclosing scope. */ if (vthis) // if inheriting from nested class { // Use the base class's 'this' member if (storage_class & STCstatic) error("static class cannot inherit from nested class %s", baseClass->toChars()); if (toParent2() != baseClass->toParent2() && (!toParent2() || !baseClass->toParent2()->getType() || !baseClass->toParent2()->getType()->isBaseOf(toParent2()->getType(), NULL))) { if (toParent2()) { error("is nested within %s, but super class %s is nested within %s", toParent2()->toChars(), baseClass->toChars(), baseClass->toParent2()->toChars()); } else { error("is not nested, but super class %s is nested within %s", baseClass->toChars(), baseClass->toParent2()->toChars()); } enclosing = NULL; } } else makeNested(); } if (storage_class & STCauto) error("storage class 'auto' is invalid when declaring a class, did you mean to use 'scope'?"); if (storage_class & STCscope) isscope = 1; if (storage_class & STCabstract) isabstract = 1; sc = sc->push(this); //sc->stc &= ~(STCfinal | STCauto | STCscope | STCstatic | STCabstract | STCdeprecated | STC_TYPECTOR | STCtls | STCgshared); //sc->stc |= storage_class & STC_TYPECTOR; sc->stc &= STCsafe | STCtrusted | STCsystem; sc->parent = this; sc->inunion = 0; if (isCOMclass()) { if (global.params.isWindows) sc->linkage = LINKwindows; else /* This enables us to use COM objects under Linux and * work with things like XPCOM */ sc->linkage = LINKc; } sc->protection = PROTpublic; sc->explicitProtection = 0; sc->structalign = STRUCTALIGN_DEFAULT; if (baseClass) { sc->offset = baseClass->structsize; alignsize = baseClass->alignsize; sc->offset = (sc->offset + alignsize - 1) & ~(alignsize - 1); // if (enclosing) // sc->offset += Target::ptrsize; // room for uplevel context pointer } else { sc->offset = Target::ptrsize * 2; // allow room for __vptr and __monitor alignsize = Target::ptrsize; } sc->userAttributes = NULL; structsize = sc->offset; Scope scsave = *sc; size_t members_dim = members->dim; sizeok = SIZEOKnone; /* 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("[%d] setScope %s %s, sc = %p\n", i, s->kind(), s->toChars(), sc); s->setScope(sc); } for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (*members)[i]; s->semantic(sc); } // Set the offsets of the fields and determine the size of the class unsigned offset = structsize; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->setFieldOffset(this, &offset, false); } sc->offset = structsize; if (global.errors != errors) { // The type is no good. type = Type::terror; } if (sizeok == SIZEOKfwd) // failed due to forward references { // semantic() failed due to forward references // Unwind what we did, and defer it for later for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *vd = s->isVarDeclaration(); if (vd) vd->offset = 0; } fields.setDim(0); structsize = 0; alignsize = 0; // structalign = 0; sc = sc->pop(); scope = scx ? scx : new Scope(*sc); scope->setNoFree(); scope->module->addDeferredSemantic(this); Module::dprogress = dprogress_save; //printf("\tsemantic('%s') failed due to forward references\n", toChars()); return; } //printf("\tsemantic('%s') successful\n", toChars()); //members->print(); /* Look for special member functions. * They must be in this class, not in a base class. */ ctor = search(Loc(), Id::ctor, 0); #if DMDV1 if (ctor && (ctor->toParent() != this || !ctor->isCtorDeclaration())) ctor = NULL; #else if (ctor && (ctor->toParent() != this || !(ctor->isCtorDeclaration() || ctor->isTemplateDeclaration()))) ctor = NULL; // search() looks through ancestor classes if (!ctor && noDefaultCtor) { // A class object is always created by constructor, so this check is legitimate. for (size_t i = 0; i < fields.dim; i++) { VarDeclaration *v = fields[i]->isVarDeclaration(); if (v->storage_class & STCnodefaultctor) ::error(v->loc, "field %s must be initialized in constructor", v->toChars()); } } #endif // dtor = (DtorDeclaration *)search(Id::dtor, 0); // if (dtor && dtor->toParent() != this) // dtor = NULL; inv = buildInv(sc); // Can be in base class aggNew = (NewDeclaration *)search(Loc(), Id::classNew, 0); aggDelete = (DeleteDeclaration *)search(Loc(), Id::classDelete, 0); // If this class has no constructor, but base class has a default // ctor, create a constructor: // this() { } if (!ctor && baseClass && baseClass->ctor) { if (FuncDeclaration *fd = resolveFuncCall(loc, sc, baseClass->ctor, NULL, NULL, NULL, 1)) { //printf("Creating default this(){} for class %s\n", toChars()); TypeFunction *btf = (TypeFunction *)fd->type; TypeFunction *tf = new TypeFunction(NULL, NULL, 0, LINKd, fd->storage_class); tf->purity = btf->purity; tf->isnothrow = btf->isnothrow; tf->trust = btf->trust; CtorDeclaration *ctor = new CtorDeclaration(loc, Loc(), 0, tf); ctor->fbody = new CompoundStatement(Loc(), new Statements()); members->push(ctor); ctor->addMember(sc, this, 1); *sc = scsave; // why? What about sc->nofree? ctor->semantic(sc); this->ctor = ctor; defaultCtor = ctor; } else { error("Cannot implicitly generate a default ctor when base class %s is missing a default ctor", baseClass->toPrettyChars()); } } #if 0 if (baseClass) { if (!aggDelete) aggDelete = baseClass->aggDelete; if (!aggNew) aggNew = baseClass->aggNew; } #endif // Allocate instance of each new interface sc->offset = structsize; for (size_t i = 0; i < vtblInterfaces->dim; i++) { BaseClass *b = (*vtblInterfaces)[i]; unsigned thissize = Target::ptrsize; alignmember(STRUCTALIGN_DEFAULT, thissize, &sc->offset); assert(b->offset == 0); b->offset = sc->offset; // Take care of single inheritance offsets while (b->baseInterfaces_dim) { b = &b->baseInterfaces[0]; b->offset = sc->offset; } sc->offset += thissize; if (alignsize < thissize) alignsize = thissize; } structsize = sc->offset; sizeok = SIZEOKdone; Module::dprogress++; dtor = buildDtor(sc); if (FuncDeclaration *f = hasIdentityOpAssign(sc)) { if (!(f->storage_class & STCdisable)) error("identity assignment operator overload is illegal"); } sc->pop(); #if 0 // Do not call until toObjfile() because of forward references // Fill in base class vtbl[]s for (i = 0; i < vtblInterfaces->dim; i++) { BaseClass *b = (*vtblInterfaces)[i]; //b->fillVtbl(this, &b->vtbl, 1); } #endif //printf("-ClassDeclaration::semantic(%s), type = %p\n", toChars(), type); if (deferred && !global.gag) { deferred->semantic2(sc); deferred->semantic3(sc); } #if 0 if (type->ty == Tclass && ((TypeClass *)type)->sym != this) { printf("this = %p %s\n", this, this->toChars()); printf("type = %d sym = %p\n", type->ty, ((TypeClass *)type)->sym); } #endif assert(type->ty != Tclass || ((TypeClass *)type)->sym == this); }
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 IrStruct::addBaseClassInits( std::vector<llvm::Constant*>& constants, ClassDeclaration* base, size_t& offset, size_t& field_index) { if (base->baseClass) { addBaseClassInits(constants, base->baseClass, offset, field_index); } IrTypeClass* tc = stripModifiers(base->type)->irtype->isClass(); assert(tc); // go through fields IrTypeAggr::iterator it; for (it = tc->def_begin(); it != tc->def_end(); ++it) { VarDeclaration* vd = *it; IF_LOG Logger::println("Adding default field %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset); LOG_SCOPE; assert(vd->offset >= offset && "default fields not sorted by offset"); // get next aligned offset for this type size_t alignedoffset = realignOffset(offset, vd->type); // insert explicit padding? if (alignedoffset < vd->offset) { add_zeros(constants, vd->offset - alignedoffset); } // add default type constants.push_back(get_default_initializer(vd, vd->init)); // advance offset to right past this field offset = vd->offset + vd->type->size(); } // has interface vtbls? if (base->vtblInterfaces && base->vtblInterfaces->dim > 0) { // false when it's not okay to use functions from super classes bool newinsts = (base == aggrdecl->isClassDeclaration()); size_t inter_idx = interfacesWithVtbls.size(); offset = (offset + PTRSIZE - 1) & ~(PTRSIZE - 1); ArrayIter<BaseClass> it2(*base->vtblInterfaces); for (; !it2.done(); it2.next()) { BaseClass* b = it2.get(); constants.push_back(getInterfaceVtbl(b, newinsts, inter_idx)); offset += PTRSIZE; // add to the interface list interfacesWithVtbls.push_back(b); inter_idx++; } } }
/*************************************** * Fill out remainder of elements[] with default initializers for fields[]. * Input: * loc * elements explicit arguments which given to construct object. * ctorinit true if the elements will be used for default initialization. * Returns false if any errors occur. * Otherwise, returns true and the missing arguments will be pushed in elements[]. */ bool StructDeclaration::fill(Loc loc, Expressions *elements, bool ctorinit) { assert(sizeok == SIZEOKdone); size_t nfields = fields.dim - isNested(); if (elements) { size_t dim = elements->dim; elements->setDim(nfields); for (size_t i = dim; i < nfields; i++) (*elements)[i] = NULL; } // Fill in missing any elements with default initializers for (size_t i = 0; i < nfields; i++) { if (elements && (*elements)[i]) continue; VarDeclaration *vd = 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 = fields[j]; bool overlap = (vd->offset < v2->offset + v2->type->size() && v2->offset < vd->offset + vd->type->size()); if (!overlap) continue; if (elements) { if ((*elements)[j]) { vx = NULL; break; } } else { vd->overlapped = true; } if (v2->init && v2->init->isVoidInitializer()) continue; if (elements) { /* 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 { // Will fix Bugzilla 1432 by enabling this path always /* 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 != vd && !(vx->offset < v2->offset + v2->type->size() && v2->offset < vx->offset + vx->type->size())) { // Both vx and v2 fills vd, but vx and v2 does not overlap } 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); } } if (elements && vx) { Expression *e; if (vx->init) { assert(!vx->init->isVoidInitializer()); e = vx->getConstInitializer(false); } else { if ((vx->storage_class & STCnodefaultctor) && !ctorinit) { ::error(loc, "field %s.%s must be initialized because it has no default constructor", type->toChars(), vx->toChars()); } /* Bugzilla 12509: Get the element of static array type. */ Type *telem = vx->type; if (telem->ty == Tsarray) { telem = telem->baseElemOf(); if (telem->ty == Tvoid) telem = Type::tuns8->addMod(telem->mod); } if (telem->needsNested() && ctorinit) e = telem->defaultInit(loc); else e = telem->defaultInitLiteral(loc); } (*elements)[fieldi] = e; } } if (elements) { for (size_t i = 0; i < elements->dim; i++) { Expression *e = (*elements)[i]; if (e && e->op == TOKerror) return false; } } return true; }
void AggrTypeBuilder::addAggregate(AggregateDeclaration *ad) { // mirror the ad->fields array but only fill in contributors const size_t n = ad->fields.dim; LLSmallVector<VarDeclaration*, 16> data(n, NULL); unsigned int errors = global.errors; // first fill in the fields with explicit initializers for (size_t index = 0; index < n; ++index) { VarDeclaration *field = ad->fields[index]; // init is !null for explicit inits if (field->init != NULL && !field->init->isVoidInitializer()) { IF_LOG Logger::println("adding explicit initializer for struct field %s", field->toChars()); size_t f_size = field->type->size(); size_t f_begin = field->offset; size_t f_end = f_begin + f_size; if (f_size == 0) continue; data[index] = field; // make sure there is no overlap for (size_t i = 0; i < index; i++) { if (data[i] != NULL) { VarDeclaration* vd = data[i]; size_t v_begin = vd->offset; size_t v_end = v_begin + vd->type->size(); if (v_begin >= f_end || v_end <= f_begin) continue; ad->error(vd->loc, "has overlapping initialization for %s and %s", field->toChars(), vd->toChars()); } } } } if (errors != global.errors) { // There was an overlapping initialization. // Return if errors are gagged otherwise abort. if (global.gag) return; fatal(); } // fill in default initializers for (size_t index = 0; index < n; ++index) { if (data[index]) continue; VarDeclaration *field = ad->fields[index]; size_t f_size = field->type->size(); size_t f_begin = field->offset; size_t f_end = f_begin + f_size; if (f_size == 0) continue; // make sure it doesn't overlap anything explicit bool overlaps = false; for (size_t i = 0; i < n; i++) { if (data[i]) { size_t v_begin = data[i]->offset; size_t v_end = v_begin + data[i]->type->size(); if (v_begin >= f_end || v_end <= f_begin) continue; overlaps = true; break; } } // if no overlap was found, add the default initializer if (!overlaps) { IF_LOG Logger::println("adding default initializer for struct field %s", field->toChars()); data[index] = field; } } // // ok. now we can build a list of llvm types. and make sure zeros are inserted if necessary. // // first we sort the list by offset std::sort(data.begin(), data.end(), var_offset_sort_cb); // add types to list for (size_t i = 0; i < n; i++) { VarDeclaration* vd = data[i]; if (vd == NULL) continue; assert(vd->offset >= m_offset && "it's a bug... most likely DMD bug 2481"); // get next aligned offset for this type size_t alignedoffset = m_offset; if (!m_packed) { alignedoffset = realignOffset(alignedoffset, vd->type); } // insert explicit padding? if (alignedoffset < vd->offset) { m_fieldIndex += add_zeros(m_defaultTypes, alignedoffset, vd->offset); } // add default type m_defaultTypes.push_back(DtoType(vd->type)); // advance offset to right past this field m_offset = vd->offset + vd->type->size(); // set the field index m_varGEPIndices[vd] = m_fieldIndex; ++m_fieldIndex; } }
/*************************************** * Fill out remainder of elements[] with default initializers for fields[]. * Input: * loc * elements explicit arguments which given to construct object. * ctorinit true if the elements will be used for default initialization. * Returns false if any errors occur. * Otherwise, returns true and the missing arguments will be pushed in elements[]. */ bool StructDeclaration::fill(Loc loc, Expressions *elements, bool ctorinit) { //printf("StructDeclaration::fill() %s\n", toChars()); assert(sizeok == SIZEOKdone); size_t nfields = fields.dim - isNested(); bool errors = false; if (elements) { size_t dim = elements->dim; elements->setDim(nfields); for (size_t i = dim; i < nfields; i++) (*elements)[i] = NULL; } // Fill in missing any elements with default initializers for (size_t i = 0; i < nfields; i++) { if (elements && (*elements)[i]) continue; VarDeclaration *vd = 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 = fields[j]; bool overlap = (vd->offset < v2->offset + v2->type->size() && v2->offset < vd->offset + vd->type->size()); if (!overlap) continue; // vd and v2 are overlapping. If either has destructors, postblits, etc., then error //printf("overlapping fields %s and %s\n", vd->toChars(), v2->toChars()); VarDeclaration *v = vd; for (int k = 0; k < 2; ++k, v = v2) { Type *tv = v->type->baseElemOf(); Dsymbol *sv = tv->toDsymbol(NULL); if (sv && !errors) { StructDeclaration *sd = sv->isStructDeclaration(); if (sd && (sd->dtor || sd->inv || sd->postblit)) { error("destructors, postblits and invariants are not allowed in overlapping fields %s and %s", vd->toChars(), v2->toChars()); errors = true; break; } } } if (elements) { if ((*elements)[j]) { vx = NULL; break; } } else { vd->overlapped = true; } if (v2->init && v2->init->isVoidInitializer()) continue; if (elements) { /* 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 { // Will fix Bugzilla 1432 by enabling this path always /* 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 != vd && !(vx->offset < v2->offset + v2->type->size() && v2->offset < vx->offset + vx->type->size())) { // Both vx and v2 fills vd, but vx and v2 does not overlap } 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); } } if (elements && vx) { Expression *e; if (vx->type->size() == 0) { e = NULL; } else if (vx->init) { assert(!vx->init->isVoidInitializer()); e = vx->getConstInitializer(false); } else { if ((vx->storage_class & STCnodefaultctor) && !ctorinit) { ::error(loc, "field %s.%s must be initialized because it has no default constructor", type->toChars(), vx->toChars()); } /* Bugzilla 12509: Get the element of static array type. */ Type *telem = vx->type; if (telem->ty == Tsarray) { /* We cannot use Type::baseElemOf() here. * If the bottom of the Tsarray is an enum type, baseElemOf() * will return the base of the enum, and its default initializer * would be different from the enum's. */ while (telem->toBasetype()->ty == Tsarray) telem = ((TypeSArray *)telem->toBasetype())->next; if (telem->ty == Tvoid) telem = Type::tuns8->addMod(telem->mod); } if (telem->needsNested() && ctorinit) e = telem->defaultInit(loc); else e = telem->defaultInitLiteral(loc); } (*elements)[fieldi] = e; } } if (elements) { for (size_t i = 0; i < elements->dim; i++) { Expression *e = (*elements)[i]; if (e && e->op == TOKerror) return false; } } return !errors; }
void StructDeclaration::toDt(dt_t **pdt) { if (zeroInit) { dtnzeros(pdt, structsize); return; } unsigned offset; dt_t *dt; dt_t *sdt = NULL; //printf("StructDeclaration::toDt(), this='%s'\n", toChars()); offset = 0; // Note equivalence of this loop to class's for (size_t i = 0; i < fields.dim; i++) { VarDeclaration *v = fields[i]; //printf("\tfield '%s' voffset %d, offset = %d\n", v->toChars(), v->offset, offset); dt = NULL; int sz; if (v->storage_class & STCref) { sz = PTRSIZE; if (v->offset >= offset) dtnzeros(&dt, sz); } else { sz = v->type->size(); Initializer *init = v->init; if (init) { //printf("\t\thas initializer %s\n", init->toChars()); ExpInitializer *ei = init->isExpInitializer(); Type *tb = v->type->toBasetype(); if (init->isVoidInitializer()) ; else if (ei && tb->ty == Tsarray) ((TypeSArray *)tb)->toDtElem(&dt, ei->exp); else dt = init->toDt(); } else if (v->offset >= offset) v->type->toDt(&dt); } if (dt) { if (v->offset < offset) error("overlapping initialization for struct %s.%s", toChars(), v->toChars()); else { if (offset < v->offset) dtnzeros(&sdt, v->offset - offset); dtcat(&sdt, dt); offset = v->offset + sz; } } } if (offset < structsize) dtnzeros(&sdt, structsize - offset); #ifdef IN_GCC dtcontainer(pdt, type, sdt); #else dtcat(pdt, sdt); #endif dt_optimize(*pdt); }
static void DtoCreateNestedContextType(FuncDeclaration* fd) { Logger::println("DtoCreateNestedContextType for %s", fd->toChars()); LOG_SCOPE DtoDeclareFunction(fd); if (fd->ir.irFunc->nestedContextCreated) return; fd->ir.irFunc->nestedContextCreated = true; if (fd->nestedVars.empty()) { // fill nestedVars size_t nnest = fd->closureVars.dim; for (size_t i = 0; i < nnest; ++i) { VarDeclaration* vd = static_cast<VarDeclaration*>(fd->closureVars.data[i]); fd->nestedVars.insert(vd); } } // construct nested variables array if (!fd->nestedVars.empty()) { Logger::println("has nested frame"); // start with adding all enclosing parent frames until a static parent is reached LLStructType* innerFrameType = NULL; unsigned depth = -1; if (!fd->isStatic()) { if (FuncDeclaration* parfd = getParentFunc(fd, true)) { // Make sure the parent has already been analyzed. DtoCreateNestedContextType(parfd); innerFrameType = parfd->ir.irFunc->frameType; if (innerFrameType) depth = parfd->ir.irFunc->depth; } } fd->ir.irFunc->depth = ++depth; Logger::cout() << "Function " << fd->toChars() << " has depth " << depth << '\n'; typedef std::vector<LLType*> TypeVec; TypeVec types; if (depth != 0) { assert(innerFrameType); // Add frame pointer types for all but last frame if (depth > 1) { for (unsigned i = 0; i < (depth - 1); ++i) { types.push_back(innerFrameType->getElementType(i)); } } // Add frame pointer type for last frame types.push_back(LLPointerType::getUnqual(innerFrameType)); } if (Logger::enabled() && depth != 0) { Logger::println("Frame types: "); LOG_SCOPE; for (TypeVec::iterator i = types.begin(); i != types.end(); ++i) Logger::cout() << **i << '\n'; } // Add the direct nested variables of this function, and update their indices to match. // TODO: optimize ordering for minimal space usage? for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) { VarDeclaration* vd = *i; if (!vd->ir.irLocal) vd->ir.irLocal = new IrLocal(vd); vd->ir.irLocal->nestedIndex = types.size(); vd->ir.irLocal->nestedDepth = depth; if (vd->isParameter()) { // Parameters will have storage associated with them (to handle byref etc.), // so handle those cases specially by storing a pointer instead of a value. const IrParameter* irparam = vd->ir.irParam; const bool refout = vd->storage_class & (STCref | STCout); const bool lazy = vd->storage_class & STClazy; const bool byref = irparam->arg->byref; const bool isVthisPtr = irparam->isVthis && !byref; if (!(refout || (byref && !lazy)) || isVthisPtr) { // This will be copied to the nesting frame. if (lazy) types.push_back(irparam->value->getType()->getContainedType(0)); else types.push_back(DtoType(vd->type)); } else { types.push_back(irparam->value->getType()); } } else if (isSpecialRefVar(vd)) { types.push_back(DtoType(vd->type->pointerTo())); } else { types.push_back(DtoType(vd->type)); } if (Logger::enabled()) { Logger::cout() << "Nested var '" << vd->toChars() << "' of type " << *types.back() << "\n"; } } LLStructType* frameType = LLStructType::create(gIR->context(), types, std::string("nest.") + fd->toChars()); Logger::cout() << "frameType = " << *frameType << '\n'; // Store type in IrFunction fd->ir.irFunc->frameType = frameType; } else if (FuncDeclaration* parFunc = getParentFunc(fd, true)) { // Propagate context arg properties if the context arg is passed on unmodified. DtoCreateNestedContextType(parFunc); fd->ir.irFunc->frameType = parFunc->ir.irFunc->frameType; fd->ir.irFunc->depth = parFunc->ir.irFunc->depth; } }
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; } Symbol **params; // Estimate number of parameters, pi size_t pi = (v_arguments != NULL); if (parameters) pi += parameters->dim; // Allow extra 2 for sthis and shidden params = (Symbol **)alloca((pi + 2) * sizeof(Symbol *)); // 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; } } } if (func->fbody) { block *b; Blockx bx; localgot = NULL; Statement *sbody = func->fbody; 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 0 if (func->isSynchronized()) { if (cd) { elem *esync; if (func->isStatic()) { // monitor is in ClassInfo esync = el_ptr(cd->toSymbol()); } else { // 'this' is the monitor esync = el_var(sthis); } if (func->isStatic() || sbody->usesEH() || !(config.flags2 & CFG2seh)) { // BUG: what if frequire or fensure uses EH? sbody = new SynchronizedStatement(func->loc, esync, sbody); } else { #if TARGET_WINDOS if (config.flags2 & CFG2seh) { /* The "jmonitor" uses an optimized exception handling frame * which is a little shorter than the more general EH frame. * It isn't strictly necessary. */ s->Sfunc->Fflags3 |= Fjmonitor; } #endif el_free(esync); } } else { error("synchronized function %s must be a member of a class", func->toChars()); } } #elif 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 (b = f->Fstartblock; b; b = b->Bnext) { if (b->BC == BCret) { b->BC = BCretexp; b->Belem = el_combine(b->Belem, el_var(sthis)); } } } } // If static constructor #if DMDV2 if (isSharedStaticCtorDeclaration()) // must come first because it derives from StaticCtorDeclaration { ssharedctors.push(s); } else #endif if (isStaticCtorDeclaration()) { sctors.push(s); } // If static destructor #if DMDV2 if (isSharedStaticDtorDeclaration()) // must come first because it derives from StaticDtorDeclaration { SharedStaticDtorDeclaration *f = isSharedStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ esharedctorgates.push(f); } sshareddtors.shift(s); } else #endif if (isStaticDtorDeclaration()) { StaticDtorDeclaration *f = isStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ ectorgates.push(f); } sdtors.shift(s); } // If unit test if (isUnitTestDeclaration()) { stests.push(s); } if (global.errors) return; writefunc(s); if (isExport()) objmod->export_symbol(s, Para.offset); for (size_t i = 0; i < irs.deferToObj->dim; i++) { Dsymbol *s = (*irs.deferToObj)[i]; FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { FuncDeclaration *fdp = fd->toParent2()->isFuncDeclaration(); if (fdp && fdp->semanticRun < PASSobj) { /* Bugzilla 7595 * FuncDeclaration::buildClosure() relies on nested functions * being toObjFile'd after the outer function. Otherwise, the * v->offset's for the closure variables are wrong. * So, defer fd until after fdp is done. */ fdp->deferred.push(fd); continue; } } s->toObjFile(0); } for (size_t i = 0; i < deferred.dim; i++) { FuncDeclaration *fd = deferred[i]; fd->toObjFile(0); } #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS // A hack to get a pointer to this function put in the .dtors segment if (ident && memcmp(ident->toChars(), "_STD", 4) == 0) objmod->staticdtor(s); #endif #if DMDV2 if (irs.startaddress) { //printf("Setting start address\n"); objmod->startaddress(irs.startaddress); } #endif }
void DtoCreateNestedContext(FuncDeclaration* fd) { Logger::println("DtoCreateNestedContext for %s", fd->toChars()); LOG_SCOPE DtoCreateNestedContextType(fd); // construct nested variables array if (!fd->nestedVars.empty()) { IrFunction* irfunction = fd->ir.irFunc; unsigned depth = irfunction->depth; LLStructType *frameType = irfunction->frameType; // Create frame for current function and append to frames list // FIXME: alignment ? LLValue* frame = 0; if (fd->needsClosure()) frame = DtoGcMalloc(frameType, ".frame"); else frame = DtoRawAlloca(frameType, 0, ".frame"); // copy parent frames into beginning if (depth != 0) { LLValue* src = irfunction->nestArg; if (!src) { assert(irfunction->thisArg); assert(fd->isMember2()); LLValue* thisval = DtoLoad(irfunction->thisArg); AggregateDeclaration* cd = fd->isMember2(); assert(cd); assert(cd->vthis); Logger::println("Indexing to 'this'"); if (cd->isStructDeclaration()) src = DtoExtractValue(thisval, cd->vthis->ir.irField->index, ".vthis"); else src = DtoLoad(DtoGEPi(thisval, 0, cd->vthis->ir.irField->index, ".vthis")); } else { src = DtoLoad(src); } if (depth > 1) { src = DtoBitCast(src, getVoidPtrType()); LLValue* dst = DtoBitCast(frame, getVoidPtrType()); DtoMemCpy(dst, src, DtoConstSize_t((depth-1) * PTRSIZE), getABITypeAlign(getVoidPtrType())); } // Copy nestArg into framelist; the outer frame is not in the list of pointers src = DtoBitCast(src, frameType->getContainedType(depth-1)); LLValue* gep = DtoGEPi(frame, 0, depth-1); DtoAlignedStore(src, gep); } // store context in IrFunction irfunction->nestedVar = frame; // go through all nested vars and assign addresses where possible. for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i) { VarDeclaration* vd = *i; LLValue* gep = DtoGEPi(frame, 0, vd->ir.irLocal->nestedIndex, vd->toChars()); if (vd->isParameter()) { Logger::println("nested param: %s", vd->toChars()); LOG_SCOPE IrParameter* parm = vd->ir.irParam; if (parm->arg->byref) { storeVariable(vd, gep); } else { Logger::println("Copying to nested frame"); // The parameter value is an alloca'd stack slot. // Copy to the nesting frame and leave the alloca for // the optimizers to clean up. DtoStore(DtoLoad(parm->value), gep); gep->takeName(parm->value); parm->value = gep; } } else { Logger::println("nested var: %s", vd->toChars()); assert(!vd->ir.irLocal->value); vd->ir.irLocal->value = gep; } if (global.params.symdebug) { LLSmallVector<LLValue*, 2> addr; dwarfOpOffset(addr, frameType, vd->ir.irLocal->nestedIndex); DtoDwarfLocalVariable(frame, vd, addr); } } } }
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 *arguments = new Parameters; Type *tf = new TypeFunction(arguments, 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(); }
static llvm::DIType dwarfCompositeType(Type* type) { LLType* T = DtoType(type); Type* t = type->toBasetype(); assert((t->ty == Tstruct || t->ty == Tclass) && "unsupported type for dwarfCompositeType"); AggregateDeclaration* sd; if (t->ty == Tstruct) { TypeStruct* ts = static_cast<TypeStruct*>(t); sd = ts->sym; } else { TypeClass* tc = static_cast<TypeClass*>(t); sd = tc->sym; } assert(sd); // make sure it's resolved sd->codegen(Type::sir); // if we don't know the aggregate's size, we don't know enough about it // to provide debug info. probably a forward-declared struct? if (sd->sizeok == 0) return llvm::DICompositeType(NULL); IrStruct* ir = sd->ir.irStruct; assert(ir); if (static_cast<llvm::MDNode*>(ir->diCompositeType) != 0) return ir->diCompositeType; // elements std::vector<llvm::Value*> elems; // defaults llvm::StringRef name = sd->toChars(); unsigned linnum = sd->loc.linnum; llvm::DIFile file = DtoDwarfFile(sd->loc); llvm::DIType derivedFrom; // set diCompositeType to handle recursive types properly if (!ir->diCompositeType) { #if LDC_LLVM_VER >= 301 unsigned tag = (t->ty == Tstruct) ? llvm::dwarf::DW_TAG_structure_type : llvm::dwarf::DW_TAG_class_type; ir->diCompositeType = gIR->dibuilder.createForwardDecl(tag, name, #if LDC_LLVM_VER >= 302 llvm::DIDescriptor(file), #endif file, linnum); #else ir->diCompositeType = gIR->dibuilder.createTemporaryType(); #endif } if (!ir->aggrdecl->isInterfaceDeclaration()) // plain interfaces don't have one { if (t->ty == Tstruct) { ArrayIter<VarDeclaration> it(sd->fields); size_t narr = sd->fields.dim; elems.reserve(narr); for (; !it.done(); it.next()) { VarDeclaration* vd = it.get(); llvm::DIType dt = dwarfMemberType(vd->loc.linnum, vd->type, file, vd->toChars(), vd->offset); elems.push_back(dt); } } else { ClassDeclaration *classDecl = ir->aggrdecl->isClassDeclaration(); add_base_fields(classDecl, file, elems); if (classDecl->baseClass) derivedFrom = dwarfCompositeType(classDecl->baseClass->getType()); } } llvm::DIArray elemsArray = gIR->dibuilder.getOrCreateArray(elems); llvm::DIType ret; if (t->ty == Tclass) { ret = gIR->dibuilder.createClassType( llvm::DIDescriptor(file), name, // name file, // compile unit where defined linnum, // line number where defined getTypeBitSize(T), // size in bits getABITypeAlign(T)*8, // alignment in bits 0, // offset in bits, llvm::DIType::FlagFwdDecl, // flags derivedFrom, // DerivedFrom elemsArray ); } else { ret = gIR->dibuilder.createStructType( llvm::DIDescriptor(file), name, // name file, // compile unit where defined linnum, // line number where defined getTypeBitSize(T), // size in bits getABITypeAlign(T)*8, // alignment in bits llvm::DIType::FlagFwdDecl, // flags #if LDC_LLVM_VER >= 303 derivedFrom, // DerivedFrom #endif elemsArray ); } ir->diCompositeType.replaceAllUsesWith(ret); ir->diCompositeType = ret; return ret; }
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; } assert(semanticRun == PASSsemantic3done); semanticRun = PASSobj; if (global.params.verbose) printf("function %s\n",func->toChars()); 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; } else { const char *libname = (global.params.symdebug) ? global.params.debuglibname : global.params.defaultlibname; // Pull in RTL startup code if (func->isMain()) { objextdef("_main"); #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS obj_ehsections(); // initialize exception handling sections #endif #if TARGET_WINDOS objextdef("__acrtused_con"); #endif obj_includelib(libname); s->Sclass = SCglobal; } else if (strcmp(s->Sident, "main") == 0 && linkage == LINKc) { #if TARGET_WINDOS objextdef("__acrtused_con"); // bring in C startup code obj_includelib("snn.lib"); // bring in C runtime library #endif s->Sclass = SCglobal; } else if (func->isWinMain()) { objextdef("__acrtused"); obj_includelib(libname); s->Sclass = SCglobal; } // Pull in RTL startup code else if (func->isDllMain()) { objextdef("__acrtused_dll"); obj_includelib(libname); s->Sclass = SCglobal; } } 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; } Symbol **params; unsigned pi; // Estimate number of parameters, pi pi = (v_arguments != NULL); if (parameters) pi += parameters->dim; // Allow extra 2 for sthis and shidden params = (Symbol **)alloca((pi + 2) * sizeof(Symbol *)); // 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->tdata()[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) { if (global.params.is64bit) { // Order of assignment of pointer or integer parameters static const unsigned char argregs[6] = { DI,SI,DX,CX,R8,R9 }; int r = 0; int xmmcnt = XMM0; for (size_t i = 0; i < pi; i++) { Symbol *sp = params[i]; tym_t ty = tybasic(sp->Stype->Tty); // BUG: doesn't work for structs if (r < sizeof(argregs)/sizeof(argregs[0])) { if (type_jparam(sp->Stype)) { sp->Sclass = SCfastpar; sp->Spreg = argregs[r]; sp->Sfl = FLauto; ++r; } } if (xmmcnt <= XMM7) { if (tyxmmreg(ty)) { sp->Sclass = SCfastpar; sp->Spreg = xmmcnt; sp->Sfl = FLauto; ++xmmcnt; } } } } else { // First parameter goes in register Symbol *sp = params[0]; if ((tyf == TYjfunc || tyf == TYmfunc) && type_jparam(sp->Stype)) { sp->Sclass = SCfastpar; sp->Spreg = (tyf == TYjfunc) ? AX : CX; sp->Sfl = FLauto; //printf("'%s' is SCfastpar\n",sp->Sident); } } } if (func->fbody) { block *b; Blockx bx; Statement *sbody; localgot = NULL; sbody = func->fbody; 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 DMDV2 buildClosure(&irs); #endif #if 0 if (func->isSynchronized()) { if (cd) { elem *esync; if (func->isStatic()) { // monitor is in ClassInfo esync = el_ptr(cd->toSymbol()); } else { // 'this' is the monitor esync = el_var(sthis); } if (func->isStatic() || sbody->usesEH() || !(config.flags2 & CFG2seh)) { // BUG: what if frequire or fensure uses EH? sbody = new SynchronizedStatement(func->loc, esync, sbody); } else { #if TARGET_WINDOS if (config.flags2 & CFG2seh) { /* The "jmonitor" uses an optimized exception handling frame * which is a little shorter than the more general EH frame. * It isn't strictly necessary. */ s->Sfunc->Fflags3 |= Fjmonitor; } #endif el_free(esync); } } else { error("synchronized function %s must be a member of a class", func->toChars()); } } #elif 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 (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()) obj_export(s, Poffset); 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) obj_staticdtor(s); #endif #if DMDV2 if (irs.startaddress) { printf("Setting start address\n"); obj_startaddress(irs.startaddress); } #endif }
dt_t *StructInitializer::toDt() { Array dts; unsigned i; unsigned j; dt_t *dt; dt_t *d; dt_t **pdtend; unsigned offset; //printf("StructInitializer::toDt('%s')\n", toChars()); dts.setDim(ad->fields.dim); dts.zero(); for (i = 0; i < vars.dim; i++) { VarDeclaration *v = (VarDeclaration *)vars.data[i]; Initializer *val = (Initializer *)value.data[i]; //printf("vars[%d] = %s\n", i, v->toChars()); for (j = 0; 1; j++) { assert(j < dts.dim); //printf(" adfield[%d] = %s\n", j, ((VarDeclaration *)ad->fields.data[j])->toChars()); if ((VarDeclaration *)ad->fields.data[j] == v) { if (dts.data[j]) error(loc, "field %s of %s already initialized", v->toChars(), ad->toChars()); dts.data[j] = (void *)val->toDt(); break; } } } dt = NULL; pdtend = &dt; offset = 0; for (j = 0; j < dts.dim; j++) { VarDeclaration *v = (VarDeclaration *)ad->fields.data[j]; d = (dt_t *)dts.data[j]; if (!d) { // An instance specific initializer was not provided. // Look to see if there's a default initializer from the // struct definition VarDeclaration *v = (VarDeclaration *)ad->fields.data[j]; if (v->init) { d = v->init->toDt(); } else if (v->offset >= offset) { unsigned k; unsigned offset2 = v->offset + v->type->size(); // Make sure this field does not overlap any explicitly // initialized field. for (k = j + 1; 1; k++) { if (k == dts.dim) // didn't find any overlap { v->type->toDt(&d); break; } VarDeclaration *v2 = (VarDeclaration *)ad->fields.data[k]; if (v2->offset < offset2 && dts.data[k]) break; // overlap } } } if (d) { if (v->offset < offset) error(loc, "duplicate union initialization for %s", v->toChars()); else { unsigned sz = dt_size(d); unsigned vsz = v->type->size(); unsigned voffset = v->offset; unsigned dim = 1; for (Type *vt = v->type->toBasetype(); vt->ty == Tsarray; vt = vt->nextOf()->toBasetype()) { TypeSArray *tsa = (TypeSArray *)vt; dim *= tsa->dim->toInteger(); } assert(sz == vsz || sz * dim <= vsz); for (size_t i = 0; i < dim; i++) { if (offset < voffset) pdtend = dtnzeros(pdtend, voffset - offset); if (!d) { if (v->init) d = v->init->toDt(); else v->type->toDt(&d); } pdtend = dtcat(pdtend, d); d = NULL; offset = voffset + sz; voffset += vsz / dim; if (sz == vsz) break; } } } } if (offset < ad->structsize) dtnzeros(pdtend, ad->structsize - offset); return dt; }