int DeclarationExp::inlineCost3(InlineCostState *ics) { int cost = 0; VarDeclaration *vd; //printf("DeclarationExp::inlineCost3()\n"); vd = declaration->isVarDeclaration(); if (vd) { TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); if (td) { #if 1 return COST_MAX; // finish DeclarationExp::doInline #else for (size_t i = 0; i < td->objects->dim; i++) { Object *o = (*td->objects)[i]; if (o->dyncast() != DYNCAST_EXPRESSION) return COST_MAX; Expression *eo = (Expression *)o; if (eo->op != TOKdsymbol) return COST_MAX; } return td->objects->dim; #endif } if (!ics->hdrscan && vd->isDataseg()) return COST_MAX; cost += 1; #if DMDV2 if (vd->edtor) // if destructor required return COST_MAX; // needs work to make this work #endif // Scan initializer (vd->init) if (vd->init) { ExpInitializer *ie = vd->init->isExpInitializer(); if (ie) { cost += expressionInlineCost(ie->exp, ics); } } } // These can contain functions, which when copied, get output twice. if (declaration->isStructDeclaration() || declaration->isClassDeclaration() || declaration->isFuncDeclaration() || declaration->isTypedefDeclaration() || #if DMDV2 declaration->isAttribDeclaration() || #endif declaration->isTemplateMixin()) return COST_MAX; //printf("DeclarationExp::inlineCost3('%s')\n", toChars()); return cost; }
int DeclarationExp::inlineCost(InlineCostState *ics) { int cost = 0; VarDeclaration *vd; //printf("DeclarationExp::inlineCost()\n"); vd = declaration->isVarDeclaration(); if (vd) { TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); if (td) { #if 1 return COST_MAX; // finish DeclarationExp::doInline #else for (size_t i = 0; i < td->objects->dim; i++) { Object *o = (*td->objects)[i]; if (o->dyncast() != DYNCAST_EXPRESSION) return COST_MAX; Expression *eo = (Expression *)o; if (eo->op != TOKdsymbol) return COST_MAX; } return td->objects->dim; #endif } // This breaks on LDC too, since nested static variables have internal // linkage and thus can't be referenced from other objects. if (!ics->hdrscan && vd->isDataseg()) return COST_MAX; cost += 1; // Scan initializer (vd->init) if (vd->init) { ExpInitializer *ie = vd->init->isExpInitializer(); if (ie) { cost += ie->exp->inlineCost(ics); } } } // These can contain functions, which when copied, get output twice. // These break on LDC too, since nested static variables and functions have // internal linkage and thus can't be referenced from other objects. if (declaration->isStructDeclaration() || declaration->isClassDeclaration() || declaration->isFuncDeclaration() || declaration->isTypedefDeclaration() || declaration->isTemplateMixin()) return COST_MAX; //printf("DeclarationExp::inlineCost('%s')\n", toChars()); return cost; }
void check(Loc loc, Declaration *d) { assert(d); VarDeclaration *v = d->isVarDeclaration(); if (v && v->toParent2() == sc->func) { if (v->isDataseg()) return; if ((v->storage_class & (STCref | STCout)) == 0) { error(loc, "escaping reference to local variable %s", v); return; } if (global.params.useDIP25 && (v->storage_class & (STCref | STCout)) && !(v->storage_class & (STCreturn | STCforeach))) { if (sc->func->flags & FUNCFLAGreturnInprocess) { //printf("inferring 'return' for variable '%s'\n", v->toChars()); v->storage_class |= STCreturn; if (v == sc->func->vthis) { TypeFunction *tf = (TypeFunction *)sc->func->type; if (tf->ty == Tfunction) { //printf("'this' too\n"); tf->isreturn = true; } } } else if (sc->module && sc->module->isRoot()) { //printf("escaping reference to local ref variable %s\n", v->toChars()); //printf("storage class = x%llx\n", v->storage_class); error(loc, "escaping reference to local ref variable %s", v); } return; } if (v->storage_class & STCref && v->storage_class & (STCforeach | STCtemp) && v->init) { // (ref v = ex; ex) if (ExpInitializer *ez = v->init->isExpInitializer()) { assert(ez->exp && ez->exp->op == TOKconstruct); Expression *ex = ((ConstructExp *)ez->exp)->e2; ex->accept(this); return; } } } }
void check(Loc loc, Declaration *d) { VarDeclaration *v = d->isVarDeclaration(); if (v && v->toParent2() == sc->func) { if (v->isDataseg()) return; if ((v->storage_class & (STCref | STCout)) == 0) error(loc, "escaping reference to local %s", v); } }
void visit(DeclarationExp *e) { //printf("DeclarationExp::inlineCost3()\n"); VarDeclaration *vd = e->declaration->isVarDeclaration(); if (vd) { TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); if (td) { cost = COST_MAX; // finish DeclarationExp::doInline return; } if (!hdrscan && vd->isDataseg()) { cost = COST_MAX; return; } if (vd->edtor) { // if destructor required // needs work to make this work cost = COST_MAX; return; } // Scan initializer (vd->init) if (vd->_init) { ExpInitializer *ie = vd->_init->isExpInitializer(); if (ie) { expressionInlineCost(ie->exp); } } cost += 1; } // These can contain functions, which when copied, get output twice. if (e->declaration->isStructDeclaration() || e->declaration->isClassDeclaration() || e->declaration->isFuncDeclaration() || e->declaration->isAttribDeclaration() || e->declaration->isTemplateMixin()) { cost = COST_MAX; return; } //printf("DeclarationExp::inlineCost3('%s')\n", toChars()); }
void visit(DeclarationExp *e) { // Note that, walkPostorder does not support DeclarationExp today. VarDeclaration *v = e->declaration->isVarDeclaration(); if (v && !(v->storage_class & STCmanifest) && !v->isDataseg() && v->init) { if (v->init->isVoidInitializer()) { } else { ExpInitializer *ei = v->init->isExpInitializer(); assert(ei); doCond(ei->exp); } } }
/************************************ * Detect cases where pointers to the stack can 'escape' the * lifetime of the stack frame when throwing `e`. * Print error messages when these are detected. * Params: * sc = used to determine current function and module * e = expression to check for any pointers to the stack * gag = do not print error messages * Returns: * true if pointers to the stack can escape */ bool checkThrowEscape(Scope *sc, Expression *e, bool gag) { //printf("[%s] checkThrowEscape, e = %s\n", e->loc->toChars(), e->toChars()); EscapeByResults er; escapeByValue(e, &er); if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim) return false; bool result = false; for (size_t i = 0; i < er.byvalue.dim; i++) { VarDeclaration *v = er.byvalue[i]; //printf("byvalue %s\n", v->toChars()); if (v->isDataseg()) continue; if (v->isScope()) { if (sc->_module && sc->_module->isRoot()) { // Only look for errors if in module listed on command line if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029 { if (!gag) error(e->loc, "scope variable %s may not be thrown", v->toChars()); result = true; } continue; } } else { //printf("no infer for %s\n", v->toChars()); v->doNotInferScope = true; } } return result; }
int StructDeclaration::calcZeroInit() { // Determine if struct is all zeros or not for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *vd = s->isVarDeclaration(); if (vd && !vd->isDataseg()) { if (vd->init) { // Should examine init to see if it is really all 0's return 0; } else if (!vd->type->isZeroInit(loc)) { return 0; } } } return 1; }
/**************************************** * Function parameter par is being initialized to arg, * and par may escape. * Detect if scoped values can escape this way. * Print error messages when these are detected. * Params: * sc = used to determine current function and module * par = identifier of function parameter * arg = initializer for param * gag = do not print error messages * Returns: * true if pointers to the stack can escape via assignment */ bool checkParamArgumentEscape(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag) { //printf("checkParamArgumentEscape(arg: %s par: %s)\n", arg->toChars(), par->toChars()); //printf("type = %s, %d\n", arg->type->toChars(), arg->type->hasPointers()); if (!arg->type->hasPointers()) return false; EscapeByResults er; escapeByValue(arg, &er); if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim) return false; bool result = false; for (size_t i = 0; i < er.byvalue.dim; i++) { //printf("byvalue %s\n", v->toChars()); VarDeclaration *v = er.byvalue[i]; if (v->isDataseg()) continue; Dsymbol *p = v->toParent2(); v->storage_class &= ~STCmaybescope; if (v->isScope()) { unsafeAssign(sc, fdc, par, arg, gag, result, v, "scope variable"); } else if (v->storage_class & STCvariadic && p == sc->func) { Type *tb = v->type->toBasetype(); if (tb->ty == Tarray || tb->ty == Tsarray) { unsafeAssign(sc, fdc, par, arg, gag, result, v, "variadic variable"); } } else { /* v is not 'scope', and is assigned to a parameter that may escape. * Therefore, v can never be 'scope'. */ v->doNotInferScope = true; } } for (size_t i = 0; i < er.byref.dim; i++) { VarDeclaration *v = er.byref[i]; if (v->isDataseg()) continue; Dsymbol *p = v->toParent2(); v->storage_class &= ~STCmaybescope; if ((v->storage_class & (STCref | STCout)) == 0 && p == sc->func) { unsafeAssign(sc, fdc, par, arg, gag, result, v, "reference to local variable"); continue; } } for (size_t i = 0; i < er.byfunc.dim; i++) { FuncDeclaration *fd = er.byfunc[i]; //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf); VarDeclarations vars; findAllOuterAccessedVariables(fd, &vars); for (size_t j = 0; j < vars.dim; j++) { VarDeclaration *v = vars[j]; //printf("v = %s\n", v->toChars()); assert(!v->isDataseg()); // these are not put in the closureVars[] Dsymbol *p = v->toParent2(); v->storage_class &= ~STCmaybescope; if ((v->storage_class & (STCref | STCout | STCscope)) && p == sc->func) { unsafeAssign(sc, fdc, par, arg, gag, result, v, "reference to local"); continue; } } } for (size_t i = 0; i < er.byexp.dim; i++) { Expression *ee = er.byexp[i]; if (sc->func->setUnsafe()) { if (!gag) error(ee->loc, "reference to stack allocated value returned by %s assigned to non-scope parameter %s", ee->toChars(), par ? par->toChars() : "unnamed"); result = true; } } return result; }
static bool checkReturnEscapeImpl(Scope *sc, Expression *e, bool refs, bool gag) { //printf("[%s] checkReturnEscapeImpl, e = %s\n", e->loc->toChars(), e->toChars()); EscapeByResults er; if (refs) escapeByRef(e, &er); else escapeByValue(e, &er); if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim) return false; bool result = false; for (size_t i = 0; i < er.byvalue.dim; i++) { VarDeclaration *v = er.byvalue[i]; //printf("byvalue %s\n", v->toChars()); if (v->isDataseg()) continue; Dsymbol *p = v->toParent2(); if ((v->isScope() || (v->storage_class & STCmaybescope)) && !(v->storage_class & STCreturn) && v->isParameter() && sc->func->flags & FUNCFLAGreturnInprocess && p == sc->func) { inferReturn(sc->func, v); // infer addition of 'return' continue; } if (v->isScope()) { if (v->storage_class & STCreturn) continue; if (sc->_module && sc->_module->isRoot() && /* This case comes up when the ReturnStatement of a __foreachbody is * checked for escapes by the caller of __foreachbody. Skip it. * * struct S { static int opApply(int delegate(S*) dg); } * S* foo() { * foreach (S* s; S) // create __foreachbody for body of foreach * return s; // s is inferred as 'scope' but incorrectly tested in foo() * return null; } */ !(!refs && p->parent == sc->func)) { // Only look for errors if in module listed on command line if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029 { if (!gag) error(e->loc, "scope variable %s may not be returned", v->toChars()); result = true; } continue; } } else if (v->storage_class & STCvariadic && p == sc->func) { Type *tb = v->type->toBasetype(); if (tb->ty == Tarray || tb->ty == Tsarray) { if (!gag) error(e->loc, "returning `%s` escapes a reference to variadic parameter `%s`", e->toChars(), v->toChars()); result = false; } } else { //printf("no infer for %s\n", v->toChars()); v->doNotInferScope = true; } } for (size_t i = 0; i < er.byref.dim; i++) { VarDeclaration *v = er.byref[i]; //printf("byref %s\n", v->toChars()); if (v->isDataseg()) continue; Dsymbol *p = v->toParent2(); if ((v->storage_class & (STCref | STCout)) == 0) { if (p == sc->func) { escapingRef(v, e, result, gag); continue; } FuncDeclaration *fd = p->isFuncDeclaration(); if (fd && sc->func->flags & FUNCFLAGreturnInprocess) { /* Code like: * int x; * auto dg = () { return &x; } * Making it: * auto dg = () return { return &x; } * Because dg.ptr points to x, this is returning dt.ptr+offset */ if (global.params.vsafe) sc->func->storage_class |= STCreturn; } } /* Check for returning a ref variable by 'ref', but should be 'return ref' * Infer the addition of 'return', or set result to be the offending expression. */ if ( (v->storage_class & (STCref | STCout)) && !(v->storage_class & (STCreturn | STCforeach))) { if ((sc->func->flags & FUNCFLAGreturnInprocess) && p == sc->func) { inferReturn(sc->func, v); // infer addition of 'return' } else if (global.params.useDIP25 && sc->_module && sc->_module->isRoot()) { // Only look for errors if in module listed on command line if (p == sc->func) { //printf("escaping reference to local ref variable %s\n", v->toChars()); //printf("storage class = x%llx\n", v->storage_class); escapingRef(v, e, result, gag); continue; } // Don't need to be concerned if v's parent does not return a ref FuncDeclaration *fd = p->isFuncDeclaration(); if (fd && fd->type && fd->type->ty == Tfunction) { TypeFunction *tf = (TypeFunction *)fd->type; if (tf->isref) { if (!gag) error(e->loc, "escaping reference to outer local variable %s", v->toChars()); result = true; continue; } } } } } for (size_t i = 0; i < er.byexp.dim; i++) { Expression *ee = er.byexp[i]; //printf("byexp %s\n", ee->toChars()); if (!gag) error(ee->loc, "escaping reference to stack allocated value returned by %s", ee->toChars()); result = true; } return result; }
/**************************************** * Given an AssignExp, determine if the lvalue will cause * the contents of the rvalue to escape. * Print error messages when these are detected. * Infer 'scope' for the lvalue where possible, in order * to eliminate the error. * Params: * sc = used to determine current function and module * ae = AssignExp to check for any pointers to the stack * gag = do not print error messages * Returns: * true if pointers to the stack can escape via assignment */ bool checkAssignEscape(Scope *sc, Expression *e, bool gag) { //printf("checkAssignEscape(e: %s)\n", e->toChars()); if (e->op != TOKassign && e->op != TOKblit && e->op != TOKconstruct) return false; AssignExp *ae = (AssignExp *)e; Expression *e1 = ae->e1; Expression *e2 = ae->e2; //printf("type = %s, %d\n", e1->type->toChars(), e1->type->hasPointers()); if (!e1->type->hasPointers()) return false; if (e1->op == TOKslice) return false; EscapeByResults er; escapeByValue(e2, &er); if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim) return false; VarDeclaration *va = NULL; while (e1->op == TOKdotvar) e1 = ((DotVarExp *)e1)->e1; if (e1->op == TOKvar) va = ((VarExp *)e1)->var->isVarDeclaration(); else if (e1->op == TOKthis) va = ((ThisExp *)e1)->var->isVarDeclaration(); else if (e1->op == TOKindex) { IndexExp *ie = (IndexExp *)e1; if (ie->e1->op == TOKvar && ie->e1->type->toBasetype()->ty == Tsarray) va = ((VarExp *)ie->e1)->var->isVarDeclaration(); } // Try to infer 'scope' for va if in a function not marked @system bool inferScope = false; if (va && sc->func && sc->func->type && sc->func->type->ty == Tfunction) inferScope = ((TypeFunction *)sc->func->type)->trust != TRUSTsystem; bool result = false; for (size_t i = 0; i < er.byvalue.dim; i++) { VarDeclaration *v = er.byvalue[i]; //printf("byvalue: %s\n", v->toChars()); if (v->isDataseg()) continue; Dsymbol *p = v->toParent2(); if (!(va && va->isScope())) v->storage_class &= ~STCmaybescope; if (v->isScope()) { if (va && va->isScope() && va->storage_class & STCreturn && !(v->storage_class & STCreturn) && sc->func->setUnsafe()) { if (!gag) error(ae->loc, "scope variable %s assigned to return scope %s", v->toChars(), va->toChars()); result = true; continue; } // If va's lifetime encloses v's, then error if (va && ((va->enclosesLifetimeOf(v) && !(v->storage_class & STCparameter)) || // va is class reference (ae->e1->op == TOKdotvar && va->type->toBasetype()->ty == Tclass && (va->enclosesLifetimeOf(v) || !va->isScope())) || va->storage_class & STCref) && sc->func->setUnsafe()) { if (!gag) error(ae->loc, "scope variable %s assigned to %s with longer lifetime", v->toChars(), va->toChars()); result = true; continue; } if (va && !va->isDataseg() && !va->doNotInferScope) { if (!va->isScope() && inferScope) { //printf("inferring scope for %s\n", va->toChars()); va->storage_class |= STCscope | STCscopeinferred; va->storage_class |= v->storage_class & STCreturn; } continue; } if (sc->func->setUnsafe()) { if (!gag) error(ae->loc, "scope variable %s assigned to non-scope %s", v->toChars(), e1->toChars()); result = true; } } else if (v->storage_class & STCvariadic && p == sc->func) { Type *tb = v->type->toBasetype(); if (tb->ty == Tarray || tb->ty == Tsarray) { if (va && !va->isDataseg() && !va->doNotInferScope) { if (!va->isScope() && inferScope) { //printf("inferring scope for %s\n", va->toChars()); va->storage_class |= STCscope | STCscopeinferred; } continue; } if (sc->func->setUnsafe()) { if (!gag) error(ae->loc, "variadic variable %s assigned to non-scope %s", v->toChars(), e1->toChars()); result = true; } } } else { /* v is not 'scope', and we didn't check the scope of where we assigned it to. * It may escape via that assignment, therefore, v can never be 'scope'. */ v->doNotInferScope = true; } } for (size_t i = 0; i < er.byref.dim; i++) { VarDeclaration *v = er.byref[i]; //printf("byref: %s\n", v->toChars()); if (v->isDataseg()) continue; Dsymbol *p = v->toParent2(); // If va's lifetime encloses v's, then error if (va && ((va->enclosesLifetimeOf(v) && !(v->storage_class & STCparameter)) || va->storage_class & STCref) && sc->func->setUnsafe()) { if (!gag) error(ae->loc, "address of variable %s assigned to %s with longer lifetime", v->toChars(), va->toChars()); result = true; continue; } if (!(va && va->isScope())) v->storage_class &= ~STCmaybescope; if ((v->storage_class & (STCref | STCout)) == 0 && p == sc->func) { if (va && !va->isDataseg() && !va->doNotInferScope) { if (!va->isScope() && inferScope) { //printf("inferring scope for %s\n", va->toChars()); va->storage_class |= STCscope | STCscopeinferred; } continue; } if (sc->func->setUnsafe()) { if (!gag) error(ae->loc, "reference to local variable %s assigned to non-scope %s", v->toChars(), e1->toChars()); result = true; } continue; } } for (size_t i = 0; i < er.byfunc.dim; i++) { FuncDeclaration *fd = er.byfunc[i]; //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf); VarDeclarations vars; findAllOuterAccessedVariables(fd, &vars); for (size_t j = 0; j < vars.dim; j++) { VarDeclaration *v = vars[j]; //printf("v = %s\n", v->toChars()); assert(!v->isDataseg()); // these are not put in the closureVars[] Dsymbol *p = v->toParent2(); if (!(va && va->isScope())) v->storage_class &= ~STCmaybescope; if ((v->storage_class & (STCref | STCout | STCscope)) && p == sc->func) { if (va && !va->isDataseg() && !va->doNotInferScope) { /* Don't infer STCscope for va, because then a closure * won't be generated for sc->func. */ //if (!va->isScope() && inferScope) //va->storage_class |= STCscope | STCscopeinferred; continue; } if (sc->func->setUnsafe()) { if (!gag) error(ae->loc, "reference to local %s assigned to non-scope %s in @safe code", v->toChars(), e1->toChars()); result = true; } continue; } } } for (size_t i = 0; i < er.byexp.dim; i++) { Expression *ee = er.byexp[i]; if (va && !va->isDataseg() && !va->doNotInferScope) { if (!va->isScope() && inferScope) { //printf("inferring scope for %s\n", va->toChars()); va->storage_class |= STCscope | STCscopeinferred; } continue; } if (sc->func->setUnsafe()) { if (!gag) error(ee->loc, "reference to stack allocated value returned by %s assigned to non-scope %s", ee->toChars(), e1->toChars()); result = true; } } return result; }
void StructDeclaration::semantic(Scope *sc) { Scope *sc2; //printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", this, parent->toChars(), toChars(), sizeok); //static int count; if (++count == 20) halt(); assert(type); if (!members) // if forward reference return; if (symtab) { if (sizeok == 1 || !scope) { //printf("already completed\n"); scope = NULL; return; // semantic() already completed } } else symtab = new DsymbolTable(); Scope *scx = NULL; if (scope) { sc = scope; scx = scope; // save so we don't make redundant copies scope = NULL; } int errors = global.gaggedErrors; unsigned dprogress_save = Module::dprogress; parent = sc->parent; type = type->semantic(loc, sc); #if STRUCTTHISREF handle = type; #else handle = type->pointerTo(); #endif structalign = sc->structalign; protection = sc->protection; storage_class |= sc->stc; if (sc->stc & STCdeprecated) isdeprecated = true; assert(!isAnonymous()); if (sc->stc & STCabstract) error("structs, unions cannot be abstract"); #if DMDV2 if (storage_class & STCimmutable) type = type->addMod(MODimmutable); if (storage_class & STCconst) type = type->addMod(MODconst); if (storage_class & STCshared) type = type->addMod(MODshared); #endif if (sizeok == 0) // if not already done the addMember step { int hasfunctions = 0; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = members->tdata()[i]; //printf("adding member '%s' to '%s'\n", s->toChars(), this->toChars()); s->addMember(sc, this, 1); if (s->isFuncDeclaration()) hasfunctions = 1; } // If nested struct, add in hidden 'this' pointer to outer scope if (hasfunctions && !(storage_class & STCstatic)) { Dsymbol *s = toParent2(); if (s) { AggregateDeclaration *ad = s->isAggregateDeclaration(); FuncDeclaration *fd = s->isFuncDeclaration(); TemplateInstance *ti; if (ad && (ti = ad->parent->isTemplateInstance()) != NULL && ti->isnested || fd) { isnested = 1; Type *t; if (ad) t = ad->handle; else if (fd) { AggregateDeclaration *ad = fd->isMember2(); if (ad) t = ad->handle; else t = Type::tvoidptr; } else assert(0); if (t->ty == Tstruct) t = Type::tvoidptr; // t should not be a ref type assert(!vthis); vthis = new ThisDeclaration(loc, t); //vthis->storage_class |= STCref; members->push(vthis); } } } } sizeok = 0; sc2 = sc->push(this); sc2->stc &= STCsafe | STCtrusted | STCsystem; sc2->parent = this; if (isUnionDeclaration()) sc2->inunion = 1; sc2->protection = PROTpublic; sc2->explicitProtection = 0; size_t members_dim = members->dim; /* Set scope so if there are forward references, we still might be able to * resolve individual members like enums. */ for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (*members)[i]; /* There are problems doing this in the general case because * Scope keeps track of things like 'offset' */ if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident)) { //printf("setScope %s %s\n", s->kind(), s->toChars()); s->setScope(sc2); } } for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (*members)[i]; /* If this is the last member, see if we can finish setting the size. * This could be much better - finish setting the size after the last * field was processed. The problem is the chicken-and-egg determination * of when that is. See Bugzilla 7426 for more info. */ if (i + 1 == members_dim) { if (sizeok == 0 && s->isAliasDeclaration()) finalizeSize(); } // Ungag errors when not speculative unsigned oldgag = global.gag; if (global.isSpeculativeGagging() && !isSpeculative()) global.gag = 0; s->semantic(sc2); global.gag = oldgag; } if (sizeok == 2) { // semantic() failed because of forward references. // Unwind what we did, and defer it for later fields.setDim(0); structsize = 0; alignsize = 0; structalign = 0; scope = scx ? scx : new Scope(*sc); scope->setNoFree(); scope->module->addDeferredSemantic(this); Module::dprogress = dprogress_save; //printf("\tdeferring %s\n", toChars()); return; } finalizeSize(); Module::dprogress++; //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); // Determine if struct is all zeros or not zeroInit = 1; for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields.tdata()[i]; VarDeclaration *vd = s->isVarDeclaration(); if (vd && !vd->isDataseg()) { if (vd->init) { // Should examine init to see if it is really all 0's zeroInit = 0; break; } else { if (!vd->type->isZeroInit(loc)) { zeroInit = 0; break; } } } } #if DMDV1 /* This doesn't work for DMDV2 because (ref S) and (S) parameter * lists will overload the same. */ /* The TypeInfo_Struct is expecting an opEquals and opCmp with * a parameter that is a pointer to the struct. But if there * isn't one, but is an opEquals or opCmp with a value, write * another that is a shell around the value: * int opCmp(struct *p) { return opCmp(*p); } */ TypeFunction *tfeqptr; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, handle, Id::p, NULL); arguments->push(arg); tfeqptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeqptr = (TypeFunction *)tfeqptr->semantic(0, sc); } TypeFunction *tfeq; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, type, NULL, NULL); arguments->push(arg); tfeq = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeq = (TypeFunction *)tfeq->semantic(0, sc); } Identifier *id = Id::eq; for (int i = 0; i < 2; i++) { Dsymbol *s = search_function(this, id); FuncDeclaration *fdx = s ? s->isFuncDeclaration() : NULL; if (fdx) { FuncDeclaration *fd = fdx->overloadExactMatch(tfeqptr); if (!fd) { fd = fdx->overloadExactMatch(tfeq); if (fd) { // Create the thunk, fdptr FuncDeclaration *fdptr = new FuncDeclaration(loc, loc, fdx->ident, STCundefined, tfeqptr); Expression *e = new IdentifierExp(loc, Id::p); e = new PtrExp(loc, e); Expressions *args = new Expressions(); args->push(e); e = new IdentifierExp(loc, id); e = new CallExp(loc, e, args); fdptr->fbody = new ReturnStatement(loc, e); ScopeDsymbol *s = fdx->parent->isScopeDsymbol(); assert(s); s->members->push(fdptr); fdptr->addMember(sc, s, 1); fdptr->semantic(sc2); } } } id = Id::cmp; } #endif #if DMDV2 dtor = buildDtor(sc2); postblit = buildPostBlit(sc2); cpctor = buildCpCtor(sc2); buildOpAssign(sc2); hasIdentityEquals = (buildOpEquals(sc2) != NULL); xeq = buildXopEquals(sc2); #endif sc2->pop(); /* Look for special member functions. */ #if DMDV2 ctor = search(0, Id::ctor, 0); #endif inv = (InvariantDeclaration *)search(0, Id::classInvariant, 0); aggNew = (NewDeclaration *)search(0, Id::classNew, 0); aggDelete = (DeleteDeclaration *)search(0, Id::classDelete, 0); if (sc->func) { semantic2(sc); semantic3(sc); } if (global.gag && global.gaggedErrors != errors) { // The type is no good, yet the error messages were gagged. type = Type::terror; } if (deferred && !global.gag) { deferred->semantic2(sc); deferred->semantic3(sc); } }
void StructDeclaration::semantic(Scope *sc) { int i; Scope *sc2; //printf("+StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); //static int count; if (++count == 20) *(char*)0=0; assert(type); if (!members) // if forward reference return; if (symtab) { if (!scope) return; // semantic() already completed } else symtab = new DsymbolTable(); Scope *scx = NULL; if (scope) { sc = scope; scx = scope; // save so we don't make redundant copies scope = NULL; } parent = sc->parent; handle = type->pointerTo(); structalign = sc->structalign; protection = sc->protection; storage_class |= sc->stc; assert(!isAnonymous()); if (sc->stc & STCabstract) error("structs, unions cannot be abstract"); if (storage_class & STCinvariant) type = type->invariantOf(); else if (storage_class & STCconst) type = type->constOf(); if (sizeok == 0) // if not already done the addMember step { for (i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; //printf("adding member '%s' to '%s'\n", s->toChars(), this->toChars()); s->addMember(sc, this, 1); } } sizeok = 0; sc2 = sc->push(this); sc2->stc &= storage_class & (STCconst | STCinvariant); sc2->parent = this; if (isUnionDeclaration()) sc2->inunion = 1; sc2->protection = PROTpublic; sc2->explicitProtection = 0; int members_dim = members->dim; for (i = 0; i < members_dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->semantic(sc2); if (isUnionDeclaration()) sc2->offset = 0; #if 0 if (sizeok == 2) { //printf("forward reference\n"); break; } #endif } /* The TypeInfo_Struct is expecting an opEquals and opCmp with * a parameter that is a pointer to the struct. But if there * isn't one, but is an opEquals or opCmp with a value, write * another that is a shell around the value: * int opCmp(struct *p) { return opCmp(*p); } */ TypeFunction *tfeqptr; { Arguments *arguments = new Arguments; Argument *arg = new Argument(STCin, handle, Id::p, NULL); arguments->push(arg); tfeqptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeqptr = (TypeFunction *)tfeqptr->semantic(0, sc); } TypeFunction *tfeq; { Arguments *arguments = new Arguments; Argument *arg = new Argument(STCin, type, NULL, NULL); arguments->push(arg); tfeq = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeq = (TypeFunction *)tfeq->semantic(0, sc); } Identifier *id = Id::eq; for (int i = 0; i < 2; i++) { Dsymbol *s = search_function(this, id); FuncDeclaration *fdx = s ? s->isFuncDeclaration() : NULL; if (fdx) { FuncDeclaration *fd = fdx->overloadExactMatch(tfeqptr); if (!fd) { fd = fdx->overloadExactMatch(tfeq); if (fd) { // Create the thunk, fdptr FuncDeclaration *fdptr = new FuncDeclaration(loc, loc, fdx->ident, STCundefined, tfeqptr); Expression *e = new IdentifierExp(loc, Id::p); e = new PtrExp(loc, e); Expressions *args = new Expressions(); args->push(e); e = new IdentifierExp(loc, id); e = new CallExp(loc, e, args); fdptr->fbody = new ReturnStatement(loc, e); ScopeDsymbol *s = fdx->parent->isScopeDsymbol(); assert(s); s->members->push(fdptr); fdptr->addMember(sc, s, 1); fdptr->semantic(sc2); } } } id = Id::cmp; } dtor = buildDtor(sc2); postblit = buildPostBlit(sc2); cpctor = buildCpCtor(sc2); buildOpAssign(sc2); sc2->pop(); if (sizeok == 2) { // semantic() failed because of forward references. // Unwind what we did, and defer it for later fields.setDim(0); structsize = 0; alignsize = 0; structalign = 0; scope = scx ? scx : new Scope(*sc); scope->setNoFree(); scope->module->addDeferredSemantic(this); //printf("\tdeferring %s\n", toChars()); return; } // 0 sized struct's are set to 1 byte if (structsize == 0) { structsize = 1; alignsize = 1; } // Round struct size up to next alignsize boundary. // This will ensure that arrays of structs will get their internals // aligned properly. structsize = (structsize + alignsize - 1) & ~(alignsize - 1); sizeok = 1; Module::dprogress++; //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); // Determine if struct is all zeros or not zeroInit = 1; for (i = 0; i < fields.dim; i++) { Dsymbol *s = (Dsymbol *)fields.data[i]; VarDeclaration *vd = s->isVarDeclaration(); if (vd && !vd->isDataseg()) { if (vd->init) { // Should examine init to see if it is really all 0's zeroInit = 0; break; } else { if (!vd->type->isZeroInit()) { zeroInit = 0; break; } } } } /* Look for special member functions. */ inv = (InvariantDeclaration *)search(0, Id::classInvariant, 0); aggNew = (NewDeclaration *)search(0, Id::classNew, 0); aggDelete = (DeleteDeclaration *)search(0, Id::classDelete, 0); if (sc->func) { semantic2(sc); semantic3(sc); } }
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); }
void StructDeclaration::semantic(Scope *sc) { Scope *sc2; //printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", this, toChars(), sizeok); //static int count; if (++count == 20) halt(); assert(type); if (!members) // if forward reference return; if (symtab) { if (sizeok == 1 || !scope) { //printf("already completed\n"); scope = NULL; return; // semantic() already completed } } else symtab = new DsymbolTable(); Scope *scx = NULL; if (scope) { sc = scope; scx = scope; // save so we don't make redundant copies scope = NULL; } unsigned dprogress_save = Module::dprogress; #ifdef IN_GCC methods.setDim(0); #endif parent = sc->parent; type = type->semantic(loc, sc); #if STRUCTTHISREF handle = type; #else handle = type->pointerTo(); #endif structalign = sc->structalign; protection = sc->protection; if (sc->stc & STCdeprecated) isdeprecated = 1; assert(!isAnonymous()); if (sc->stc & STCabstract) error("structs, unions cannot be abstract"); #if DMDV2 if (storage_class & STCimmutable) type = type->invariantOf(); else if (storage_class & STCconst) type = type->constOf(); #endif #if IN_GCC if (attributes) attributes->append(sc->attributes); else attributes = sc->attributes; #endif if (sizeok == 0) // if not already done the addMember step { for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; //printf("adding member '%s' to '%s'\n", s->toChars(), this->toChars()); s->addMember(sc, this, 1); } } sizeok = 0; sc2 = sc->push(this); sc2->stc = 0; #if IN_GCC sc2->attributes = NULL; #endif sc2->parent = this; if (isUnionDeclaration()) sc2->inunion = 1; sc2->protection = PROTpublic; sc2->explicitProtection = 0; size_t members_dim = members->dim; /* Set scope so if there are forward references, we still might be able to * resolve individual members like enums. */ for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; /* There are problems doing this in the general case because * Scope keeps track of things like 'offset' */ if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident)) { //printf("setScope %s %s\n", s->kind(), s->toChars()); s->setScope(sc2); } } for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->semantic(sc2); #if 0 if (sizeok == 2) { //printf("forward reference\n"); break; } #endif } #if DMDV1 /* This doesn't work for DMDV2 because (ref S) and (S) parameter * lists will overload the same. */ /* The TypeInfo_Struct is expecting an opEquals and opCmp with * a parameter that is a pointer to the struct. But if there * isn't one, but is an opEquals or opCmp with a value, write * another that is a shell around the value: * int opCmp(struct *p) { return opCmp(*p); } */ TypeFunction *tfeqptr; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, handle, Id::p, NULL); arguments->push(arg); tfeqptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeqptr = (TypeFunction *)tfeqptr->semantic(0, sc); } TypeFunction *tfeq; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, type, NULL, NULL); arguments->push(arg); tfeq = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeq = (TypeFunction *)tfeq->semantic(0, sc); } Identifier *id = Id::eq; for (int i = 0; i < 2; i++) { Dsymbol *s = search_function(this, id); FuncDeclaration *fdx = s ? s->isFuncDeclaration() : NULL; if (fdx) { FuncDeclaration *fd = fdx->overloadExactMatch(tfeqptr); if (!fd) { fd = fdx->overloadExactMatch(tfeq); if (fd) { // Create the thunk, fdptr FuncDeclaration *fdptr = new FuncDeclaration(loc, loc, fdx->ident, STCundefined, tfeqptr); Expression *e = new IdentifierExp(loc, Id::p); e = new PtrExp(loc, e); Expressions *args = new Expressions(); args->push(e); e = new IdentifierExp(loc, id); e = new CallExp(loc, e, args); fdptr->fbody = new ReturnStatement(loc, e); ScopeDsymbol *s = fdx->parent->isScopeDsymbol(); assert(s); s->members->push(fdptr); fdptr->addMember(sc, s, 1); fdptr->semantic(sc2); } } } id = Id::cmp; } #endif #if DMDV2 /* Try to find the opEquals function. Build it if necessary. */ TypeFunction *tfeqptr; { // bool opEquals(const T*) const; Parameters *parameters = new Parameters; #if STRUCTTHISREF // bool opEquals(ref const T) const; Parameter *param = new Parameter(STCref, type->constOf(), NULL, NULL); #else // bool opEquals(const T*) const; Parameter *param = new Parameter(STCin, type->pointerTo(), NULL, NULL); #endif parameters->push(param); tfeqptr = new TypeFunction(parameters, Type::tbool, 0, LINKd); tfeqptr->mod = MODconst; tfeqptr = (TypeFunction *)tfeqptr->semantic(0, sc2); Dsymbol *s = search_function(this, Id::eq); FuncDeclaration *fdx = s ? s->isFuncDeclaration() : NULL; if (fdx) { eq = fdx->overloadExactMatch(tfeqptr); if (!eq) fdx->error("type signature should be %s not %s", tfeqptr->toChars(), fdx->type->toChars()); } TemplateDeclaration *td = s ? s->isTemplateDeclaration() : NULL; // BUG: should also check that td is a function template, not just a template if (!eq && !td) eq = buildOpEquals(sc2); } dtor = buildDtor(sc2); postblit = buildPostBlit(sc2); cpctor = buildCpCtor(sc2); buildOpAssign(sc2); #endif sc2->pop(); if (sizeok == 2) { // semantic() failed because of forward references. // Unwind what we did, and defer it for later fields.setDim(0); structsize = 0; alignsize = 0; structalign = 0; scope = scx ? scx : new Scope(*sc); scope->setNoFree(); scope->module->addDeferredSemantic(this); Module::dprogress = dprogress_save; //printf("\tdeferring %s\n", toChars()); return; } // 0 sized struct's are set to 1 byte if (structsize == 0) { structsize = 1; alignsize = 1; } // Round struct size up to next alignsize boundary. // This will ensure that arrays of structs will get their internals // aligned properly. structsize = (structsize + alignsize - 1) & ~(alignsize - 1); sizeok = 1; Module::dprogress++; //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); // Determine if struct is all zeros or not zeroInit = 1; for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = (Dsymbol *)fields.data[i]; VarDeclaration *vd = s->isVarDeclaration(); if (vd && !vd->isDataseg()) { if (vd->init) { // Should examine init to see if it is really all 0's zeroInit = 0; break; } else { if (!vd->type->isZeroInit(loc)) { zeroInit = 0; break; } } } } /* Look for special member functions. */ #if DMDV2 ctor = search(0, Id::ctor, 0); #endif inv = (InvariantDeclaration *)search(0, Id::classInvariant, 0); aggNew = (NewDeclaration *)search(0, Id::classNew, 0); aggDelete = (DeleteDeclaration *)search(0, Id::classDelete, 0); if (sc->func) { semantic2(sc); semantic3(sc); } }
void StructDeclaration::semantic(Scope *sc) { Scope *sc2; //printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", this, parent->toChars(), toChars(), sizeok); //static int count; if (++count == 20) halt(); assert(type); if (!members) // if opaque declaration { return; } if (symtab) { if (sizeok == SIZEOKdone || !scope) { //printf("already completed\n"); scope = NULL; return; // semantic() already completed } } else symtab = new DsymbolTable(); Scope *scx = NULL; if (scope) { sc = scope; scx = scope; // save so we don't make redundant copies scope = NULL; } int errors = global.errors; unsigned dprogress_save = Module::dprogress; parent = sc->parent; type = type->semantic(loc, sc); handle = type; protection = sc->protection; alignment = sc->structalign; storage_class |= sc->stc; if (sc->stc & STCdeprecated) isdeprecated = true; assert(!isAnonymous()); if (sc->stc & STCabstract) error("structs, unions cannot be abstract"); userAttributes = sc->userAttributes; if (sizeok == SIZEOKnone) // if not already done the addMember step { for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; //printf("adding member '%s' to '%s'\n", s->toChars(), this->toChars()); s->addMember(sc, this, 1); } } sizeok = SIZEOKnone; sc2 = sc->push(this); sc2->stc &= STCsafe | STCtrusted | STCsystem; sc2->parent = this; if (isUnionDeclaration()) sc2->inunion = 1; sc2->protection = PROTpublic; sc2->explicitProtection = 0; sc2->structalign = STRUCTALIGN_DEFAULT; sc2->userAttributes = NULL; /* Set scope so if there are forward references, we still might be able to * resolve individual members like enums. */ for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; /* There are problems doing this in the general case because * Scope keeps track of things like 'offset' */ //if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident)) { //printf("struct: setScope %s %s\n", s->kind(), s->toChars()); s->setScope(sc2); } } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; /* If this is the last member, see if we can finish setting the size. * This could be much better - finish setting the size after the last * field was processed. The problem is the chicken-and-egg determination * of when that is. See Bugzilla 7426 for more info. */ if (i + 1 == members->dim) { if (sizeok == SIZEOKnone && s->isAliasDeclaration()) finalizeSize(sc2); } // Ungag errors when not speculative unsigned oldgag = global.gag; if (global.isSpeculativeGagging() && !isSpeculative()) { global.gag = 0; } s->semantic(sc2); global.gag = oldgag; } finalizeSize(sc2); if (sizeok == SIZEOKfwd) { // semantic() failed because of forward references. // Unwind what we did, and defer it for later for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *vd = s->isVarDeclaration(); if (vd) vd->offset = 0; } fields.setDim(0); structsize = 0; alignsize = 0; // structalign = 0; scope = scx ? scx : new Scope(*sc); scope->setNoFree(); scope->module->addDeferredSemantic(this); Module::dprogress = dprogress_save; //printf("\tdeferring %s\n", toChars()); return; } Module::dprogress++; //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); // Determine if struct is all zeros or not zeroInit = 1; for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *vd = s->isVarDeclaration(); if (vd && !vd->isDataseg()) { if (vd->init) { // Should examine init to see if it is really all 0's zeroInit = 0; break; } else { if (!vd->type->isZeroInit(loc)) { zeroInit = 0; break; } } } } #if DMDV1 /* This doesn't work for DMDV2 because (ref S) and (S) parameter * lists will overload the same. */ /* The TypeInfo_Struct is expecting an opEquals and opCmp with * a parameter that is a pointer to the struct. But if there * isn't one, but is an opEquals or opCmp with a value, write * another that is a shell around the value: * int opCmp(struct *p) { return opCmp(*p); } */ TypeFunction *tfeqptr; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, handle, Id::p, NULL); arguments->push(arg); tfeqptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeqptr = (TypeFunction *)tfeqptr->semantic(Loc(), sc); } TypeFunction *tfeq; { Parameters *arguments = new Parameters; Parameter *arg = new Parameter(STCin, type, NULL, NULL); arguments->push(arg); tfeq = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeq = (TypeFunction *)tfeq->semantic(Loc(), sc); } Identifier *id = Id::eq; for (int i = 0; i < 2; i++) { Dsymbol *s = search_function(this, id); FuncDeclaration *fdx = s ? s->isFuncDeclaration() : NULL; if (fdx) { FuncDeclaration *fd = fdx->overloadExactMatch(tfeqptr); if (!fd) { fd = fdx->overloadExactMatch(tfeq); if (fd) { // Create the thunk, fdptr FuncDeclaration *fdptr = new FuncDeclaration(loc, loc, fdx->ident, STCundefined, tfeqptr); Expression *e = new IdentifierExp(loc, Id::p); e = new PtrExp(loc, e); Expressions *args = new Expressions(); args->push(e); e = new IdentifierExp(loc, id); e = new CallExp(loc, e, args); fdptr->fbody = new ReturnStatement(loc, e); ScopeDsymbol *s = fdx->parent->isScopeDsymbol(); assert(s); s->members->push(fdptr); fdptr->addMember(sc, s, 1); fdptr->semantic(sc2); } } } id = Id::cmp; } #endif #if DMDV2 dtor = buildDtor(sc2); postblit = buildPostBlit(sc2); cpctor = buildCpCtor(sc2); buildOpAssign(sc2); buildOpEquals(sc2); #endif inv = buildInv(sc2); sc2->pop(); /* Look for special member functions. */ #if DMDV2 ctor = search(Loc(), Id::ctor, 0); #endif aggNew = (NewDeclaration *)search(Loc(), Id::classNew, 0); aggDelete = (DeleteDeclaration *)search(Loc(), Id::classDelete, 0); TypeTuple *tup = type->toArgTypes(); size_t dim = tup->arguments->dim; if (dim >= 1) { assert(dim <= 2); arg1type = (*tup->arguments)[0]->type; if (dim == 2) arg2type = (*tup->arguments)[1]->type; } if (sc->func) { semantic2(sc); semantic3(sc); } if (global.errors != errors) { // The type is no good. type = Type::terror; } if (deferred && !global.gag) { deferred->semantic2(sc); deferred->semantic3(sc); } #if 0 if (type->ty == Tstruct && ((TypeStruct *)type)->sym != this) { printf("this = %p %s\n", this, this->toChars()); printf("type = %d sym = %p\n", type->ty, ((TypeStruct *)type)->sym); } #endif assert(type->ty != Tstruct || ((TypeStruct *)type)->sym == this); }
void StructDeclaration::finalizeSize(Scope *sc) { //printf("StructDeclaration::finalizeSize() %s\n", toChars()); if (sizeok != SIZEOKnone) return; // Set the offsets of the fields and determine the size of the struct unsigned offset = 0; bool isunion = isUnionDeclaration() != NULL; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->setFieldOffset(this, &offset, isunion); } if (sizeok == SIZEOKfwd) return; // 0 sized struct's are set to 1 byte if (structsize == 0) { structsize = 1; alignsize = 1; } // Round struct size up to next alignsize boundary. // This will ensure that arrays of structs will get their internals // aligned properly. if (alignment == STRUCTALIGN_DEFAULT) structsize = (structsize + alignsize - 1) & ~(alignsize - 1); else structsize = (structsize + alignment - 1) & ~(alignment - 1); sizeok = SIZEOKdone; // Calculate fields[i]->overlapped fill(loc, NULL, true); // 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; } } } } // Look for the constructor, for the struct literal/constructor call expression ctor = searchCtor(); if (ctor) { // Finish all constructors semantics to determine this->noDefaultCtor. struct SearchCtor { static int fp(Dsymbol *s, void *ctxt) { CtorDeclaration *f = s->isCtorDeclaration(); if (f && f->semanticRun == PASSinit) f->semantic(NULL); return 0; } }; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->apply(&SearchCtor::fp, NULL); } } }