/****************************************** * Build __xopCmp for TypeInfo_Struct * static bool __xopCmp(ref const S p, ref const S q) * { * return p.opCmp(q); * } * * This is called by TypeInfo.compare(p1, p2). If the struct does not support * const objects comparison, it will throw "not implemented" Error in runtime. */ FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc) { //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); if (Dsymbol *cmp = search_function(sd, Id::cmp)) { if (FuncDeclaration *fd = cmp->isFuncDeclaration()) { TypeFunction *tfcmpptr; { Scope scx; /* const int opCmp(ref const S s); */ Parameters *parameters = new Parameters; parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL)); tfcmpptr = new TypeFunction(parameters, Type::tint32, 0, LINKd); tfcmpptr->mod = MODconst; tfcmpptr = (TypeFunction *)tfcmpptr->semantic(Loc(), &scx); } fd = fd->overloadExactMatch(tfcmpptr); if (fd) return fd; } } else { #if 0 // FIXME: doesn't work for recursive alias this /* Check opCmp member exists. * Consider 'alias this', but except opDispatch. */ Expression *e = new DsymbolExp(sd->loc, sd); e = new DotIdExp(sd->loc, e, Id::cmp); Scope *sc2 = sc->push(); e = e->trySemantic(sc2); sc2->pop(); if (e) { Dsymbol *s = NULL; switch (e->op) { case TOKoverloadset: s = ((OverExp *)e)->vars; break; case TOKimport: s = ((ScopeExp *)e)->sds; break; case TOKvar: s = ((VarExp *)e)->var; break; default: break; } if (!s || s->ident != Id::cmp) e = NULL; // there's no valid member 'opCmp' } if (!e) return NULL; // bitwise comparison would work /* Essentially, a struct which does not define opCmp is not comparable. * At this time, typeid(S).compare might be correct that throwing "not implement" Error. * But implementing it would break existing code, such as: * * struct S { int value; } // no opCmp * int[S] aa; // Currently AA key uses bitwise comparison * // (It's default behavior of TypeInfo_Strust.compare). * * Not sure we should fix this inconsistency, so just keep current behavior. */ #else return NULL; #endif } if (!sd->xerrcmp) { // object._xopCmp Identifier *id = Identifier::idPool("_xopCmp"); Expression *e = new IdentifierExp(sd->loc, Id::empty); e = new DotIdExp(sd->loc, e, Id::object); e = new DotIdExp(sd->loc, e, id); e = e->semantic(sc); Dsymbol *s = getDsymbol(e); assert(s); sd->xerrcmp = s->isFuncDeclaration(); } Loc declLoc = Loc(); // loc is unnecessary so __xopCmp is never called directly Loc loc = Loc(); // loc is unnecessary so errors are gagged Parameters *parameters = new Parameters; parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL)); parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::tint32, 0, LINKd); Identifier *id = Id::xopCmp; FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); Expression *e1 = new IdentifierExp(loc, Id::p); Expression *e2 = new IdentifierExp(loc, Id::q); Expression *e = new CallExp(loc, new DotIdExp(loc, e2, Id::cmp), e1); fop->fbody = new ReturnStatement(loc, e); unsigned errors = global.startGagging(); // Do not report errors Scope *sc2 = sc->push(); sc2->stc = 0; sc2->linkage = LINKd; fop->semantic(sc2); fop->semantic2(sc2); sc2->pop(); if (global.endGagging(errors)) // if errors happened fop = sd->xerrcmp; return fop; }
/********************************* * Operator overloading for op= */ Expression *BinAssignExp::op_overload(Scope *sc) { //printf("BinAssignExp::op_overload() (%s)\n", toChars()); #if DMDV2 if (e1->op == TOKarray) { ArrayExp *ae = (ArrayExp *)e1; ae->e1 = ae->e1->semantic(sc); ae->e1 = resolveProperties(sc, ae->e1); AggregateDeclaration *ad = isAggregate(ae->e1->type); if (ad) { /* Rewrite a[args]+=e2 as: * a.opIndexOpAssign!("+")(e2, args); */ Dsymbol *fd = search_function(ad, Id::opIndexOpAssign); if (fd) { ae = resolveOpDollar(sc, ae); Expressions *a = (Expressions *)ae->arguments->copy(); a->insert(0, e2); Objects *tiargs = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, ae->e1, fd->ident, tiargs); e = new CallExp(loc, e, a); e = e->semantic(sc); return e; } // Didn't find it. Forward to aliasthis if (ad->aliasthis && ae->e1->type != att1) { /* Rewrite a[arguments] op= e2 as: * a.aliasthis[arguments] op= e2 */ Expression *e1 = ae->copy(); ((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att1 && ae->e1->type->checkAliasThisRec()) be->att1 = ae->e1->type; be->e1 = e1; if (Expression *e = be->trySemantic(sc)) return e; } att1 = NULL; } } else if (e1->op == TOKslice) { SliceExp *se = (SliceExp *)e1; se->e1 = se->e1->semantic(sc); se->e1 = resolveProperties(sc, se->e1); AggregateDeclaration *ad = isAggregate(se->e1->type); if (ad) { /* Rewrite a[lwr..upr]+=e2 as: * a.opSliceOpAssign!("+")(e2, lwr, upr); */ Dsymbol *fd = search_function(ad, Id::opSliceOpAssign); if (fd) { se = resolveOpDollar(sc, se); Expressions *a = new Expressions(); a->push(e2); assert(!se->lwr || se->upr); if (se->lwr) { a->push(se->lwr); a->push(se->upr); } Objects *tiargs = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, se->e1, fd->ident, tiargs); e = new CallExp(loc, e, a); e = e->semantic(sc); return e; } // Didn't find it. Forward to aliasthis if (ad->aliasthis && se->e1->type != att1) { /* Rewrite a[lwr..upr] op= e2 as: * a.aliasthis[lwr..upr] op= e2 */ Expression *e1 = se->copy(); ((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att1 && se->e1->type->checkAliasThisRec()) be->att1 = se->e1->type; be->e1 = e1; if (Expression *e = be->trySemantic(sc)) return e; } att1 = NULL; } } #endif BinExp::semantic(sc); e1 = resolveProperties(sc, e1); e2 = resolveProperties(sc, e2); // Don't attempt 'alias this' if an error occured if (e1->type->ty == Terror || e2->type->ty == Terror) return new ErrorExp(); Identifier *id = opId(); Expressions args2; AggregateDeclaration *ad1 = isAggregate(e1->type); Dsymbol *s = NULL; #if 1 // the old D1 scheme if (ad1 && id) { s = search_function(ad1, id); } #endif Objects *tiargs = NULL; #if DMDV2 if (!s) { /* Try the new D2 scheme, opOpAssign */ if (ad1) { s = search_function(ad1, Id::opOpAssign); if (s && !s->isTemplateDeclaration()) { error("%s.opOpAssign isn't a template", e1->toChars()); return new ErrorExp(); } } // Set tiargs, the template argument list, which will be the operator string if (s) { id = Id::opOpAssign; tiargs = opToArg(sc, op); } } #endif if (s) { /* Try: * a.opOpAssign(b) */ args2.setDim(1); args2[0] = e2; Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; if (s) { FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args2); } else { TemplateDeclaration *td = s->isTemplateDeclaration(); templateResolve(&m, td, loc, sc, tiargs, e1, &args2); } } if (m.count > 1) { // Error, ambiguous error("overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), m.nextf->type->toChars(), m.lastf->toChars()); } else if (m.last == MATCHnomatch) { m.lastf = m.anyf; if (tiargs) goto L1; } // Rewrite (e1 op e2) as e1.opOpAssign(e2) return build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); } L1: #if DMDV2 // Try alias this on first operand if (ad1 && ad1->aliasthis) { /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ if (att1 && this->e1->type == att1) return NULL; //printf("att %s e1 = %s\n", Token::toChars(op), this->e1->type->toChars()); Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att1 && this->e1->type->checkAliasThisRec()) be->att1 = this->e1->type; be->e1 = e1; return be->trySemantic(sc); } // Try alias this on second operand AggregateDeclaration *ad2 = isAggregate(e2->type); if (ad2 && ad2->aliasthis) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ if (att2 && this->e2->type == att2) return NULL; //printf("att %s e2 = %s\n", Token::toChars(op), this->e2->type->toChars()); Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); BinExp *be = (BinExp *)copy(); if (!be->att2 && this->e2->type->checkAliasThisRec()) be->att2 = this->e2->type; be->e2 = e2; return be->trySemantic(sc); } #endif return NULL; }
/****************************************** * Build __xopEquals for TypeInfo_Struct * static bool __xopEquals(ref const S p, ref const S q) * { * return p == q; * } * * This is called by TypeInfo.equals(p1, p2). If the struct does not support * const objects comparison, it will throw "not implemented" Error in runtime. */ FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc) { if (!needOpEquals(sd)) return NULL; // bitwise comparison would work //printf("StructDeclaration::buildXopEquals() %s\n", sd->toChars()); if (Dsymbol *eq = search_function(sd, Id::eq)) { if (FuncDeclaration *fd = eq->isFuncDeclaration()) { TypeFunction *tfeqptr; { Scope scx; /* const bool opEquals(ref const S s); */ Parameters *parameters = new Parameters; parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL)); tfeqptr = new TypeFunction(parameters, Type::tbool, 0, LINKd); tfeqptr->mod = MODconst; tfeqptr = (TypeFunction *)tfeqptr->semantic(Loc(), &scx); } fd = fd->overloadExactMatch(tfeqptr); if (fd) return fd; } } if (!sd->xerreq) { // object._xopEquals Identifier *id = Identifier::idPool("_xopEquals"); Expression *e = new IdentifierExp(sd->loc, Id::empty); e = new DotIdExp(sd->loc, e, Id::object); e = new DotIdExp(sd->loc, e, id); e = e->semantic(sc); Dsymbol *s = getDsymbol(e); assert(s); sd->xerreq = s->isFuncDeclaration(); } Loc declLoc = Loc(); // loc is unnecessary so __xopEquals is never called directly Loc loc = Loc(); // loc is unnecessary so errors are gagged Parameters *parameters = new Parameters; parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL)); parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); Identifier *id = Id::xopEquals; FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); Expression *e1 = new IdentifierExp(loc, Id::p); Expression *e2 = new IdentifierExp(loc, Id::q); Expression *e = new EqualExp(TOKequal, loc, e1, e2); fop->fbody = new ReturnStatement(loc, e); unsigned errors = global.startGagging(); // Do not report errors Scope *sc2 = sc->push(); sc2->stc = 0; sc2->linkage = LINKd; fop->semantic(sc2); fop->semantic2(sc2); sc2->pop(); if (global.endGagging(errors)) // if errors happened fop = sd->xerreq; return fop; }
void VarDeclaration::semantic(Scope *sc) { #if 0 printf("VarDeclaration::semantic('%s', parent = '%s')\n", toChars(), sc->parent->toChars()); printf(" type = %s\n", type ? type->toChars() : "null"); printf(" stc = x%x\n", sc->stc); printf(" storage_class = x%x\n", storage_class); printf("linkage = %d\n", sc->linkage); //if (strcmp(toChars(), "mul") == 0) halt(); #endif storage_class |= sc->stc; if (storage_class & STCextern && init) error("extern symbols cannot have initializers"); /* If auto type inference, do the inference */ int inferred = 0; if (!type) { inuse++; type = init->inferType(sc); inuse--; inferred = 1; /* This is a kludge to support the existing syntax for RAII * declarations. */ storage_class &= ~STCauto; originalType = type; } else { if (!originalType) originalType = type; type = type->semantic(loc, sc); } //printf(" semantic type = %s\n", type ? type->toChars() : "null"); type->checkDeprecated(loc, sc); linkage = sc->linkage; this->parent = sc->parent; //printf("this = %p, parent = %p, '%s'\n", this, parent, parent->toChars()); protection = sc->protection; //printf("sc->stc = %x\n", sc->stc); //printf("storage_class = x%x\n", storage_class); #if DMDV2 if (storage_class & STCgshared && global.params.safe && !sc->module->safe) { error("__gshared not allowed in safe mode; use shared"); } #endif Dsymbol *parent = toParent(); FuncDeclaration *fd = parent->isFuncDeclaration(); Type *tb = type->toBasetype(); if (tb->ty == Tvoid && !(storage_class & STClazy)) { error("voids have no value"); type = Type::terror; tb = type; } if (tb->ty == Tfunction) { error("cannot be declared to be a function"); type = Type::terror; tb = type; } if (tb->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tb; if (!ts->sym->members) { error("no definition of struct %s", ts->toChars()); } } if (tb->ty == Ttuple) { /* Instead, declare variables for each of the tuple elements * and add those. */ TypeTuple *tt = (TypeTuple *)tb; size_t nelems = Parameter::dim(tt->arguments); Objects *exps = new Objects(); exps->setDim(nelems); Expression *ie = init ? init->toExpression() : NULL; for (size_t i = 0; i < nelems; i++) { Parameter *arg = Parameter::getNth(tt->arguments, i); OutBuffer buf; buf.printf("_%s_field_%zu", ident->toChars(), i); buf.writeByte(0); const char *name = (const char *)buf.extractData(); Identifier *id = Lexer::idPool(name); Expression *einit = ie; if (ie && ie->op == TOKtuple) { einit = (Expression *)((TupleExp *)ie)->exps->data[i]; } Initializer *ti = init; if (einit) { ti = new ExpInitializer(einit->loc, einit); } VarDeclaration *v = new VarDeclaration(loc, arg->type, id, ti); //printf("declaring field %s of type %s\n", v->toChars(), v->type->toChars()); v->semantic(sc); if (sc->scopesym) { //printf("adding %s to %s\n", v->toChars(), sc->scopesym->toChars()); if (sc->scopesym->members) sc->scopesym->members->push(v); } Expression *e = new DsymbolExp(loc, v); exps->data[i] = e; } TupleDeclaration *v2 = new TupleDeclaration(loc, ident, exps); v2->isexp = 1; aliassym = v2; return; } if (storage_class & STCconst && !init && !fd) // Initialize by constructor only storage_class = (storage_class & ~STCconst) | STCctorinit; if (isConst()) { } else if (isStatic()) { } else if (isSynchronized()) { error("variable %s cannot be synchronized", toChars()); } else if (isOverride()) { error("override cannot be applied to variable"); } else if (isAbstract()) { error("abstract cannot be applied to variable"); } else if (storage_class & STCtemplateparameter) { } else if (storage_class & STCctfe) { } else { AggregateDeclaration *aad = sc->anonAgg; if (!aad) aad = parent->isAggregateDeclaration(); if (aad) { #if DMDV2 assert(!(storage_class & (STCextern | STCstatic | STCtls | STCgshared))); if (storage_class & (STCconst | STCimmutable) && init) { if (!type->toBasetype()->isTypeBasic()) storage_class |= STCstatic; } else #endif aad->addField(sc, this); } InterfaceDeclaration *id = parent->isInterfaceDeclaration(); if (id) { error("field not allowed in interface"); } /* Templates cannot add fields to aggregates */ TemplateInstance *ti = parent->isTemplateInstance(); if (ti) { // Take care of nested templates while (1) { TemplateInstance *ti2 = ti->tempdecl->parent->isTemplateInstance(); if (!ti2) break; ti = ti2; } // If it's a member template AggregateDeclaration *ad = ti->tempdecl->isMember(); if (ad && storage_class != STCundefined) { error("cannot use template to add field to aggregate '%s'", ad->toChars()); } } } #if DMDV2 if ((storage_class & (STCref | STCparameter | STCforeach)) == STCref && ident != Id::This) { error("only parameters or foreach declarations can be ref"); } #endif if (type->isauto() && !noauto) { if (storage_class & (STCfield | STCout | STCref | STCstatic) || !fd) { error("globals, statics, fields, ref and out parameters cannot be auto"); } if (!(storage_class & (STCauto | STCscope))) { if (!(storage_class & STCparameter) && ident != Id::withSym) error("reference to scope class must be scope"); } } enum TOK op = TOKconstruct; if (!init && !sc->inunion && !isStatic() && !isConst() && fd && !(storage_class & (STCfield | STCin | STCforeach)) && type->size() != 0) { // Provide a default initializer //printf("Providing default initializer for '%s'\n", toChars()); if (type->ty == Tstruct && ((TypeStruct *)type)->sym->zeroInit == 1) { /* If a struct is all zeros, as a special case * set it's initializer to the integer 0. * In AssignExp::toElem(), we check for this and issue * a memset() to initialize the struct. * Must do same check in interpreter. */ Expression *e = new IntegerExp(loc, 0, Type::tint32); Expression *e1; e1 = new VarExp(loc, this); e = new AssignExp(loc, e1, e); e->op = TOKconstruct; e->type = e1->type; // don't type check this, it would fail init = new ExpInitializer(loc, e); return; } else if (type->ty == Ttypedef) { TypeTypedef *td = (TypeTypedef *)type; if (td->sym->init) { init = td->sym->init; ExpInitializer *ie = init->isExpInitializer(); if (ie) // Make copy so we can modify it init = new ExpInitializer(ie->loc, ie->exp); } else init = getExpInitializer(); } else { init = getExpInitializer(); } // Default initializer is always a blit op = TOKblit; } if (init) { sc = sc->push(); sc->stc &= ~(STC_TYPECTOR | STCpure | STCnothrow | STCref); ArrayInitializer *ai = init->isArrayInitializer(); if (ai && tb->ty == Taarray) { init = ai->toAssocArrayInitializer(); } StructInitializer *si = init->isStructInitializer(); ExpInitializer *ei = init->isExpInitializer(); // See if initializer is a NewExp that can be allocated on the stack if (ei && isScope() && ei->exp->op == TOKnew) { NewExp *ne = (NewExp *)ei->exp; if (!(ne->newargs && ne->newargs->dim)) { ne->onstack = 1; onstack = 1; if (type->isBaseOf(ne->newtype->semantic(loc, sc), NULL)) onstack = 2; } } // If inside function, there is no semantic3() call if (sc->func) { // If local variable, use AssignExp to handle all the various // possibilities. if (fd && !isStatic() && !isConst() && !init->isVoidInitializer()) { //printf("fd = '%s', var = '%s'\n", fd->toChars(), toChars()); if (!ei) { Expression *e = init->toExpression(); if (!e) { init = init->semantic(sc, type); e = init->toExpression(); if (!e) { error("is not a static and cannot have static initializer"); return; } } ei = new ExpInitializer(init->loc, e); init = ei; } Expression *e1 = new VarExp(loc, this); Type *t = type->toBasetype(); if (t->ty == Tsarray && !(storage_class & (STCref | STCout))) { ei->exp = ei->exp->semantic(sc); if (!ei->exp->implicitConvTo(type)) { int dim = ((TypeSArray *)t)->dim->toInteger(); // If multidimensional static array, treat as one large array while (1) { t = t->nextOf()->toBasetype(); if (t->ty != Tsarray) break; dim *= ((TypeSArray *)t)->dim->toInteger(); e1->type = new TypeSArray(t->nextOf(), new IntegerExp(0, dim, Type::tindex)); } } e1 = new SliceExp(loc, e1, NULL, NULL); } else if (t->ty == Tstruct) { ei->exp = ei->exp->semantic(sc); ei->exp = resolveProperties(sc, ei->exp); StructDeclaration *sd = ((TypeStruct *)t)->sym; #if DMDV2 /* Look to see if initializer is a call to the constructor */ if (sd->ctor && // there are constructors ei->exp->type->ty == Tstruct && // rvalue is the same struct ((TypeStruct *)ei->exp->type)->sym == sd && ei->exp->op == TOKstar) { /* Look for form of constructor call which is: * *__ctmp.ctor(arguments...) */ PtrExp *pe = (PtrExp *)ei->exp; if (pe->e1->op == TOKcall) { CallExp *ce = (CallExp *)pe->e1; if (ce->e1->op == TOKdotvar) { DotVarExp *dve = (DotVarExp *)ce->e1; if (dve->var->isCtorDeclaration()) { /* It's a constructor call, currently constructing * a temporary __ctmp. */ /* Before calling the constructor, initialize * variable with a bit copy of the default * initializer */ Expression *e = new AssignExp(loc, new VarExp(loc, this), t->defaultInit(loc)); e->op = TOKblit; e->type = t; ei->exp = new CommaExp(loc, e, ei->exp); /* Replace __ctmp being constructed with e1 */ dve->e1 = e1; return; } } } } #endif if (!ei->exp->implicitConvTo(type)) { /* Look for opCall * See bugzilla 2702 for more discussion */ Type *ti = ei->exp->type->toBasetype(); // Don't cast away invariant or mutability in initializer if (search_function(sd, Id::call) && /* Initializing with the same type is done differently */ !(ti->ty == Tstruct && t->toDsymbol(sc) == ti->toDsymbol(sc))) { // Rewrite as e1.call(arguments) Expression * eCall = new DotIdExp(loc, e1, Id::call); ei->exp = new CallExp(loc, eCall, ei->exp); } } } ei->exp = new AssignExp(loc, e1, ei->exp); ei->exp->op = TOKconstruct; canassign++; ei->exp = ei->exp->semantic(sc); canassign--; ei->exp->optimize(WANTvalue); } else { init = init->semantic(sc, type); if (fd && isConst() && !isStatic()) { // Make it static storage_class |= STCstatic; } } } else if (isConst() || isFinal() || parent->isAggregateDeclaration()) { /* Because we may need the results of a const declaration in a * subsequent type, such as an array dimension, before semantic2() * gets ordinarily run, try to run semantic2() now. * Ignore failure. */ if (!global.errors && !inferred) { unsigned errors = global.errors; global.gag++; //printf("+gag\n"); Expression *e; Initializer *i2 = init; inuse++; if (ei) { e = ei->exp->syntaxCopy(); e = e->semantic(sc); e = e->implicitCastTo(sc, type); } else if (si || ai) { i2 = init->syntaxCopy(); i2 = i2->semantic(sc, type); } inuse--; global.gag--; //printf("-gag\n"); if (errors != global.errors) // if errors happened { if (global.gag == 0) global.errors = errors; // act as if nothing happened #if DMDV2 /* Save scope for later use, to try again */ scope = new Scope(*sc); scope->setNoFree(); #endif } else if (ei) { e = e->optimize(WANTvalue | WANTinterpret); if (e->op == TOKint64 || e->op == TOKstring || e->op == TOKfloat64) { ei->exp = e; // no errors, keep result } #if DMDV2 else { /* Save scope for later use, to try again */ scope = new Scope(*sc); scope->setNoFree(); } #endif } else init = i2; // no errors, keep result } } sc = sc->pop(); } }
int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply) { Identifier *idapply = (op == TOKforeach) ? Id::apply : Id::applyReverse; #if DMDV2 Identifier *idfront = (op == TOKforeach) ? Id::Ffront : Id::Fback; int sliced = 0; #endif Type *tab; Type *att = NULL; Expression *org_aggr = aggr; AggregateDeclaration *ad; while (1) { aggr = aggr->semantic(sc); aggr = resolveProperties(sc, aggr); aggr = aggr->optimize(WANTvalue); if (!aggr->type) goto Lerr; tab = aggr->type->toBasetype(); if (att == tab) { aggr = org_aggr; goto Lerr; } switch (tab->ty) { case Tarray: case Tsarray: case Ttuple: case Taarray: break; case Tclass: ad = ((TypeClass *)tab)->sym; goto Laggr; case Tstruct: ad = ((TypeStruct *)tab)->sym; goto Laggr; Laggr: #if DMDV2 if (!sliced) { sapply = search_function(ad, idapply); if (sapply) { // opApply aggregate break; } Dsymbol *s = search_function(ad, Id::slice); if (s) { Expression *rinit = new SliceExp(aggr->loc, aggr, NULL, NULL); rinit = rinit->trySemantic(sc); if (rinit) // if application of [] succeeded { aggr = rinit; sliced = 1; continue; } } } if (Dsymbol *shead = ad->search(Loc(), idfront, 0)) { // range aggregate break; } if (ad->aliasthis) { if (!att && tab->checkAliasThisRec()) att = tab; aggr = new DotIdExp(aggr->loc, aggr, ad->aliasthis->ident); continue; } #else sapply = search_function(ad, idapply); if (sapply) { // opApply aggregate break; } #endif goto Lerr; case Tdelegate: if (aggr->op == TOKdelegate) { DelegateExp *de = (DelegateExp *)aggr; sapply = de->func->isFuncDeclaration(); } break; case Terror: break; default: goto Lerr; } break; } return 1; Lerr: return 0; }
Expression *UnaExp::op_overload(Scope *sc) { //printf("UnaExp::op_overload() (%s)\n", toChars()); #if DMDV2 if (e1->op == TOKarray) { ArrayExp *ae = (ArrayExp *)e1; ae->e1 = ae->e1->semantic(sc); ae->e1 = resolveProperties(sc, ae->e1); AggregateDeclaration *ad = isAggregate(ae->e1->type); if (ad) { /* Rewrite as: * a.opIndexUnary!("+")(args); */ Dsymbol *fd = search_function(ad, Id::opIndexUnary); if (fd) { Objects *targsi = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, ae->e1, fd->ident, targsi); e = new CallExp(loc, e, ae->arguments); e = e->semantic(sc); return e; } // Didn't find it. Forward to aliasthis if (ad->aliasthis) { /* Rewrite op(a[arguments]) as: * op(a.aliasthis[arguments]) */ Expression *e1 = ae->copy(); ((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); Expression *e = copy(); ((UnaExp *)e)->e1 = e1; e = e->trySemantic(sc); return e; } } } else if (e1->op == TOKslice) { SliceExp *se = (SliceExp *)e1; se->e1 = se->e1->semantic(sc); se->e1 = resolveProperties(sc, se->e1); AggregateDeclaration *ad = isAggregate(se->e1->type); if (ad) { /* Rewrite as: * a.opSliceUnary!("+")(lwr, upr); */ Dsymbol *fd = search_function(ad, Id::opSliceUnary); if (fd) { Expressions *a = new Expressions(); if (se->lwr) { a->push(se->lwr); a->push(se->upr); } Objects *targsi = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, se->e1, fd->ident, targsi); e = new CallExp(loc, e, a); e = e->semantic(sc); return e; } // Didn't find it. Forward to aliasthis if (ad->aliasthis) { /* Rewrite op(a[lwr..upr]) as: * op(a.aliasthis[lwr..upr]) */ Expression *e1 = se->copy(); ((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident); Expression *e = copy(); ((UnaExp *)e)->e1 = e1; e = e->trySemantic(sc); return e; } } } #endif e1 = e1->semantic(sc); e1 = resolveProperties(sc, e1); AggregateDeclaration *ad = isAggregate(e1->type); if (ad) { Dsymbol *fd = NULL; #if 1 // Old way, kept for compatibility with D1 if (op != TOKpreplusplus && op != TOKpreminusminus) { fd = search_function(ad, opId()); if (fd) { if (op == TOKarray) { /* Rewrite op e1[arguments] as: * e1.fd(arguments) */ Expression *e = new DotIdExp(loc, e1, fd->ident); ArrayExp *ae = (ArrayExp *)this; e = new CallExp(loc, e, ae->arguments); e = e->semantic(sc); return e; } else { // Rewrite +e1 as e1.add() return build_overload(loc, sc, e1, NULL, fd); } } } #endif #if DMDV2 /* Rewrite as: * e1.opUnary!("+")(); */ fd = search_function(ad, Id::opUnary); if (fd) { Objects *targsi = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, e1, fd->ident, targsi); e = new CallExp(loc, e); e = e->semantic(sc); return e; } // Didn't find it. Forward to aliasthis if (ad->aliasthis) { /* Rewrite op(e1) as: * op(e1.aliasthis) */ Expression *e1 = new DotIdExp(loc, this->e1, ad->aliasthis->ident); Expression *e = copy(); ((UnaExp *)e)->e1 = e1; e = e->trySemantic(sc); return e; } #endif } return NULL; }
/****************************************** * Common code for overloading of EqualExp and CmpExp */ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) { //printf("BinExp::compare_overload(id = %s) %s\n", id->toChars(), toChars()); AggregateDeclaration *ad1 = isAggregate(e1->type); AggregateDeclaration *ad2 = isAggregate(e2->type); Dsymbol *s = NULL; Dsymbol *s_r = NULL; if (ad1) { s = search_function(ad1, id); } if (ad2) { s_r = search_function(ad2, id); if (s == s_r) s_r = NULL; } Objects *targsi = NULL; if (s || s_r) { /* Try: * a.opEquals(b) * b.opEquals(a) * and see which is better. */ Expressions args1; Expressions args2; args1.setDim(1); args1.tdata()[0] = e1; args2.setDim(1); args2.tdata()[0] = e2; Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; if (0 && s && s_r) { printf("s : %s\n", s->toPrettyChars()); printf("s_r: %s\n", s_r->toPrettyChars()); } if (s) { FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args2, sc->module); } else { TemplateDeclaration *td = s->isTemplateDeclaration(); templateResolve(&m, td, sc, loc, targsi, NULL, &args2); } } FuncDeclaration *lastf = m.lastf; int count = m.count; if (s_r) { FuncDeclaration *fd = s_r->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args1, sc->module); } else { TemplateDeclaration *td = s_r->isTemplateDeclaration(); templateResolve(&m, td, sc, loc, targsi, NULL, &args1); } } if (m.count > 1) { /* The following if says "not ambiguous" if there's one match * from s and one from s_r, in which case we pick s. * This doesn't follow the spec, but is a workaround for the case * where opEquals was generated from templates and we cannot figure * out if both s and s_r came from the same declaration or not. * The test case is: * import std.typecons; * void main() { * assert(tuple("has a", 2u) == tuple("has a", 1)); * } */ if (!(m.lastf == lastf && m.count == 2 && count == 1)) { // Error, ambiguous error("overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), m.nextf->type->toChars(), m.lastf->toChars()); } } else if (m.last == MATCHnomatch) { m.lastf = m.anyf; } Expression *e; if (lastf && m.lastf == lastf || !s_r && m.last == MATCHnomatch) // Rewrite (e1 op e2) as e1.opfunc(e2) e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); else { // Rewrite (e1 op e2) as e2.opfunc_r(e1) e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s_r); // When reversing operands of comparison operators, // need to reverse the sense of the op switch (op) { case TOKlt: op = TOKgt; break; case TOKgt: op = TOKlt; break; case TOKle: op = TOKge; break; case TOKge: op = TOKle; break; // Floating point compares case TOKule: op = TOKuge; break; case TOKul: op = TOKug; break; case TOKuge: op = TOKule; break; case TOKug: op = TOKul; break; // The rest are symmetric default: break; } } return e; } // Try alias this on first operand if (ad1 && ad1->aliasthis) { /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); Expression *e = copy(); ((BinExp *)e)->e1 = e1; e = e->trySemantic(sc); return e; } // Try alias this on second operand if (ad2 && ad2->aliasthis) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); Expression *e = copy(); ((BinExp *)e)->e2 = e2; e = e->trySemantic(sc); return e; } return NULL; }
FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) { if (!search_function(this, Id::eq)) return NULL; /* static bool__xopEquals(in void* p, in void* q) { * return ( *cast(const S*)(p) ).opEquals( *cast(const S*)(q) ); * } */ Parameters *parameters = new Parameters; parameters->push(new Parameter(STCin, Type::tvoidptr, Id::p, NULL)); parameters->push(new Parameter(STCin, Type::tvoidptr, Id::q, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); tf = (TypeFunction *)tf->semantic(loc, sc); Identifier *id = Lexer::idPool("__xopEquals"); FuncDeclaration *fop = new FuncDeclaration(loc, 0, id, STCstatic, tf); Expression *e = new CallExp(0, new DotIdExp(0, new PtrExp(0, new CastExp(0, new IdentifierExp(0, Id::p), type->pointerTo()->constOf())), Id::eq), new PtrExp(0, new CastExp(0, new IdentifierExp(0, Id::q), type->pointerTo()->constOf()))); fop->fbody = new ReturnStatement(loc, e); size_t index = members->dim; members->push(fop); sc = sc->push(); sc->stc = 0; sc->linkage = LINKd; unsigned errors = global.startGagging(); fop->semantic(sc); if (errors == global.gaggedErrors) { fop->semantic2(sc); if (errors == global.gaggedErrors) { fop->semantic3(sc); if (errors == global.gaggedErrors) fop->addMember(sc, this, 1); } } if (global.endGagging(errors)) // if errors happened { members->remove(index); if (!xerreq) { Expression *e = new IdentifierExp(loc, Id::empty); e = new DotIdExp(loc, e, Id::object); e = new DotIdExp(loc, e, Lexer::idPool("_xopEquals")); e = e->semantic(sc); Dsymbol *s = getDsymbol(e); FuncDeclaration *fd = s->isFuncDeclaration(); xerreq = fd; } fop = xerreq; } sc->pop(); return fop; }
FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) { if (FuncDeclaration *f = hasIdentityOpAssign(sc)) { hasIdentityAssign = 1; return f; } // Even if non-identity opAssign is defined, built-in identity opAssign // will be defined. if (!needOpAssign()) return NULL; //printf("StructDeclaration::buildOpAssign() %s\n", toChars()); StorageClass stc = STCsafe | STCnothrow | STCpure; Loc declLoc = this->loc; Loc loc = Loc(); // internal code should have no loc to prevent coverage if (dtor || postblit) { if (dtor) stc = mergeFuncAttrs(stc, dtor->storage_class); } else { for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->isField()); if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); while (tv->ty == Tsarray) { TypeSArray *ta = (TypeSArray *)tv; tv = tv->nextOf()->toBasetype(); } if (tv->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tv; StructDeclaration *sd = ts->sym; if (FuncDeclaration *f = sd->hasIdentityOpAssign(sc)) stc = mergeFuncAttrs(stc, f->storage_class); } } } Parameters *fparams = new Parameters; fparams->push(new Parameter(STCnodtor, type, Id::p, NULL)); Type *tf = new TypeFunction(fparams, handle, 0, LINKd, stc | STCref); FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), Id::assign, stc, tf); Expression *e = NULL; if (stc & STCdisable) { } else if (dtor || postblit) { /* Do swap this and rhs * tmp = this; this = s; tmp.dtor(); */ //printf("\tswap copy\n"); Identifier *idtmp = Lexer::uniqueId("__tmp"); VarDeclaration *tmp; AssignExp *ec = NULL; if (dtor) { tmp = new VarDeclaration(loc, type, idtmp, new VoidInitializer(loc)); tmp->noscope = 1; tmp->storage_class |= STCctfe; e = new DeclarationExp(loc, tmp); ec = new AssignExp(loc, new VarExp(loc, tmp), new ThisExp(loc) ); ec->op = TOKblit; e = Expression::combine(e, ec); } ec = new AssignExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id::p)); ec->op = TOKblit; e = Expression::combine(e, ec); if (dtor) { /* Instead of running the destructor on s, run it * on tmp. This avoids needing to copy tmp back in to s. */ Expression *ec2 = new DotVarExp(loc, new VarExp(loc, tmp), dtor, 0); ec2 = new CallExp(loc, ec2); e = Expression::combine(e, ec2); } } else { /* Do memberwise copy */ //printf("\tmemberwise copy\n"); for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->isField()); // this.v = s.v; AssignExp *ec = new AssignExp(loc, new DotVarExp(loc, new ThisExp(loc), v, 0), new DotVarExp(loc, new IdentifierExp(loc, Id::p), v, 0)); e = Expression::combine(e, ec); } } if (e) { Statement *s1 = new ExpStatement(loc, e); /* Add: * return this; */ e = new ThisExp(loc); Statement *s2 = new ReturnStatement(loc, e); fop->fbody = new CompoundStatement(loc, s1, s2); } Dsymbol *s = fop; #if 1 // workaround until fixing issue 1528 Dsymbol *assign = search_function(this, Id::assign); if (assign && assign->isTemplateDeclaration()) { // Wrap a template around the function declaration TemplateParameters *tpl = new TemplateParameters(); Dsymbols *decldefs = new Dsymbols(); decldefs->push(s); TemplateDeclaration *tempdecl = new TemplateDeclaration(assign->loc, fop->ident, tpl, NULL, decldefs, 0); s = tempdecl; } #endif members->push(s); s->addMember(sc, this, 1); this->hasIdentityAssign = 1; // temporary mark identity assignable unsigned errors = global.startGagging(); // Do not report errors, even if the unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it. global.speculativeGag = global.gag; Scope *sc2 = sc->push(); sc2->stc = 0; sc2->linkage = LINKd; sc2->speculative = true; s->semantic(sc2); s->semantic2(sc2); s->semantic3(sc2); sc2->pop(); global.speculativeGag = oldspec; if (global.endGagging(errors)) // if errors happened { // Disable generated opAssign, because some members forbid identity assignment. fop->storage_class |= STCdisable; fop->fbody = NULL; // remove fbody which contains the error } //printf("-StructDeclaration::buildOpAssign() %s %s, errors = %d\n", toChars(), s->kind(), (fop->storage_class & STCdisable) != 0); return fop; }
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; } unsigned dprogress_save = Module::dprogress; int errors = global.errors; 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]; //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 Ungag ungag = ungagSpeculative(); 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++) { 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); xeq = buildXopEquals(sc2); xcmp = buildXopCmp(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 Type::getTypeInfo(). */ #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); }
FuncDeclaration *StructDeclaration::buildOpEquals(Scope *sc) { Dsymbol *eq = search_function(this, Id::eq); if (eq) { for (size_t i = 0; i <= 1; i++) { Expression *e = i == 0 ? new NullExp(loc, type->constOf()) // dummy rvalue : type->constOf()->defaultInit(); // dummy lvalue Expressions *arguments = new Expressions(); arguments->push(e); // check identity opEquals exists FuncDeclaration *fd = eq->isFuncDeclaration(); if (fd) { fd = fd->overloadResolve(loc, e, arguments, 1); if (fd && !(fd->storage_class & STCdisable)) return fd; } TemplateDeclaration *td = eq->isTemplateDeclaration(); if (td) { fd = td->deduceFunctionTemplate(sc, loc, NULL, e, arguments, 1); if (fd && !(fd->storage_class & STCdisable)) return fd; } } return NULL; } if (!needOpEquals()) return NULL; //printf("StructDeclaration::buildOpEquals() %s\n", toChars()); Parameters *parameters = new Parameters; parameters->push(new Parameter(STCin, type, Id::p, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); tf->mod = MODconst; tf = (TypeFunction *)tf->semantic(loc, sc); FuncDeclaration *fop = new FuncDeclaration(loc, 0, Id::eq, STCundefined, tf); Expression *e = NULL; /* Do memberwise compare */ //printf("\tmemberwise compare\n"); for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *v = s->isVarDeclaration(); assert(v && v->storage_class & STCfield); if (v->storage_class & STCref) assert(0); // what should we do with this? // this.v == s.v; EqualExp *ec = new EqualExp(TOKequal, loc, new DotVarExp(loc, new ThisExp(loc), v, 0), new DotVarExp(loc, new IdentifierExp(loc, Id::p), v, 0)); if (e) e = new AndAndExp(loc, e, ec); else e = ec; } if (!e) e = new IntegerExp(loc, 1, Type::tbool); fop->fbody = new ReturnStatement(loc, e); members->push(fop); fop->addMember(sc, this, 1); sc = sc->push(); sc->stc = 0; sc->linkage = LINKd; fop->semantic(sc); sc->pop(); //printf("-StructDeclaration::buildOpEquals() %s\n", toChars()); return fop; }
void TypeInfoStructDeclaration::toDt(dt_t **pdt) { //printf("TypeInfoStructDeclaration::toDt() '%s'\n", toChars()); unsigned offset = Type::typeinfostruct->structsize; dtxoff(pdt, Type::typeinfostruct->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Struct dtdword(pdt, 0); // monitor assert(tinfo->ty == Tstruct); TypeStruct *tc = (TypeStruct *)tinfo; StructDeclaration *sd = tc->sym; /* Put out: * char[] name; * void[] init; * hash_t function(void*) xtoHash; * int function(void*,void*) xopEquals; * int function(void*,void*) xopCmp; * char[] function(void*) xtoString; * uint m_flags; * * name[] */ const char *name = sd->toPrettyChars(); size_t namelen = strlen(name); dtdword(pdt, namelen); //dtabytes(pdt, TYnptr, 0, namelen + 1, name); dtxoff(pdt, toSymbol(), offset, TYnptr); offset += namelen + 1; // void[] init; dtdword(pdt, sd->structsize); // init.length if (sd->zeroInit) dtdword(pdt, 0); // NULL for 0 initialization else dtxoff(pdt, sd->toInitializer(), 0, TYnptr); // init.ptr FuncDeclaration *fd; FuncDeclaration *fdx; TypeFunction *tf; Type *ta; Dsymbol *s; static TypeFunction *tftohash; static TypeFunction *tftostring; if (!tftohash) { Scope sc; tftohash = new TypeFunction(NULL, Type::thash_t, 0, LINKd); tftohash = (TypeFunction *)tftohash->semantic(0, &sc); tftostring = new TypeFunction(NULL, Type::tchar->arrayOf(), 0, LINKd); tftostring = (TypeFunction *)tftostring->semantic(0, &sc); } TypeFunction *tfeqptr; { Scope sc; Parameters *arguments = new Parameters; #if STRUCTTHISREF // arg type is ref const T Parameter *arg = new Parameter(STCref, tc->constOf(), NULL, NULL); #else // arg type is const T* Parameter *arg = new Parameter(STCin, tc->pointerTo(), NULL, NULL); #endif arguments->push(arg); tfeqptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeqptr = (TypeFunction *)tfeqptr->semantic(0, &sc); } #if 0 TypeFunction *tfeq; { Scope sc; Array *arguments = new Array; Parameter *arg = new Parameter(In, tc, NULL, NULL); arguments->push(arg); tfeq = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfeq = (TypeFunction *)tfeq->semantic(0, &sc); } #endif s = search_function(sd, Id::tohash); fdx = s ? s->isFuncDeclaration() : NULL; if (fdx) { fd = fdx->overloadExactMatch(tftohash); if (fd) dtxoff(pdt, fd->toSymbol(), 0, TYnptr); else //fdx->error("must be declared as extern (D) uint toHash()"); dtdword(pdt, 0); } else dtdword(pdt, 0); s = search_function(sd, Id::eq); fdx = s ? s->isFuncDeclaration() : NULL; for (int i = 0; i < 2; i++) { if (fdx) { fd = fdx->overloadExactMatch(tfeqptr); if (fd) dtxoff(pdt, fd->toSymbol(), 0, TYnptr); else //fdx->error("must be declared as extern (D) int %s(%s*)", fdx->toChars(), sd->toChars()); dtdword(pdt, 0); } else //fdx->error("must be declared as extern (D) int %s(%s*)", fdx->toChars(), sd->toChars()); dtdword(pdt, 0); s = search_function(sd, Id::cmp); fdx = s ? s->isFuncDeclaration() : NULL; } s = search_function(sd, Id::tostring); fdx = s ? s->isFuncDeclaration() : NULL; if (fdx) { fd = fdx->overloadExactMatch(tftostring); if (fd) dtxoff(pdt, fd->toSymbol(), 0, TYnptr); else //fdx->error("must be declared as extern (D) char[] toString()"); dtdword(pdt, 0); } else dtdword(pdt, 0); // uint m_flags; dtdword(pdt, tc->hasPointers()); #if DMDV2 // xgetMembers FuncDeclaration *sgetmembers = sd->findGetMembers(); if (sgetmembers) dtxoff(pdt, sgetmembers->toSymbol(), 0, TYnptr); else dtdword(pdt, 0); // xgetMembers // xdtor FuncDeclaration *sdtor = sd->dtor; if (sdtor) dtxoff(pdt, sdtor->toSymbol(), 0, TYnptr); else dtdword(pdt, 0); // xdtor // xpostblit FuncDeclaration *spostblit = sd->postblit; if (spostblit) dtxoff(pdt, spostblit->toSymbol(), 0, TYnptr); else dtdword(pdt, 0); // xpostblit #endif // name[] dtnbytes(pdt, namelen + 1, name); }
int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) { if (!arguments || !arguments->dim) return 0; if (sapply) // prefer opApply { for (size_t u = 0; u < arguments->dim; u++) { Parameter *arg = arguments->tdata()[u]; if (arg->type) arg->type = arg->type->semantic(loc, sc); } Expression *ethis; Type *tab = aggr->type->toBasetype(); if (tab->ty == Tclass || tab->ty == Tstruct) ethis = aggr; else { assert(tab->ty == Tdelegate && aggr->op == TOKdelegate); ethis = ((DelegateExp *)aggr)->e1; } /* Look for like an * int opApply(int delegate(ref Type [, ...]) dg); * overload */ FuncDeclaration *fd = sapply->isFuncDeclaration(); if (fd) { sapply = inferApplyArgTypesX(ethis, fd, arguments); } #if 0 TemplateDeclaration *td = sapply->isTemplateDeclaration(); if (td) { inferApplyArgTypesZ(td, arguments); } #endif return sapply ? 1 : 0; } /* Return if no arguments need types. */ for (size_t u = 0; u < arguments->dim; u++) { Parameter *arg = arguments->tdata()[u]; if (!arg->type) break; } AggregateDeclaration *ad; Parameter *arg = arguments->tdata()[0]; Type *taggr = aggr->type; assert(taggr); Type *tab = taggr->toBasetype(); switch (tab->ty) { case Tarray: case Tsarray: case Ttuple: if (arguments->dim == 2) { if (!arg->type) arg->type = Type::tsize_t; // key type arg = arguments->tdata()[1]; } if (!arg->type && tab->ty != Ttuple) arg->type = tab->nextOf(); // value type break; case Taarray: { TypeAArray *taa = (TypeAArray *)tab; if (arguments->dim == 2) { if (!arg->type) arg->type = taa->index; // key type arg = arguments->tdata()[1]; } if (!arg->type) arg->type = taa->next; // value type break; } case Tclass: ad = ((TypeClass *)tab)->sym; goto Laggr; case Tstruct: ad = ((TypeStruct *)tab)->sym; goto Laggr; Laggr: if (arguments->dim == 1) { if (!arg->type) { /* Look for a head() or rear() overload */ Identifier *id = (op == TOKforeach) ? Id::Ffront : Id::Fback; Dsymbol *s = search_function(ad, id); FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL; if (!fd) { if (s && s->isTemplateDeclaration()) break; break; } // Resolve inout qualifier of front type arg->type = fd->type->nextOf(); if (arg->type) arg->type = arg->type->substWildTo(tab->mod); } break; } break; case Tdelegate: { if (!inferApplyArgTypesY((TypeFunction *)tab->nextOf(), arguments)) return 0; break; } default: break; // ignore error, caught later } return 1; }
Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) { //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident->toChars(), flags); if (ident == Id::length || ident == Id::dollar) { VarDeclaration **pvar; Expression *ce; if (ident == Id::length && !global.params.useDeprecated) error("using 'length' inside [ ] is deprecated, use '$' instead"); L1: if (td) { /* $ gives the number of elements in the tuple */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); Expression *e = new IntegerExp(0, td->objects->dim, Type::tsize_t); v->init = new ExpInitializer(0, e); v->storage_class |= STCstatic | STCconst; v->semantic(sc); return v; } if (type) { /* $ gives the number of type entries in the type tuple */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); Expression *e = new IntegerExp(0, type->arguments->dim, Type::tsize_t); v->init = new ExpInitializer(0, e); v->storage_class |= STCstatic | STCconst; v->semantic(sc); return v; } if (exp->op == TOKindex) { /* array[index] where index is some function of $ */ IndexExp *ie = (IndexExp *)exp; pvar = &ie->lengthVar; ce = ie->e1; } else if (exp->op == TOKslice) { /* array[lwr .. upr] where lwr or upr is some function of $ */ SliceExp *se = (SliceExp *)exp; pvar = &se->lengthVar; ce = se->e1; } else if (exp->op == TOKarray) { /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $ * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...) */ ArrayExp *ae = (ArrayExp *)exp; AggregateDeclaration *ad = NULL; Type *t = ae->e1->type->toBasetype(); if (t->ty == Tclass) { ad = ((TypeClass *)t)->sym; } else if (t->ty == Tstruct) { ad = ((TypeStruct *)t)->sym; } assert(ad); Dsymbol *dsym = search_function(ad, Id::opDollar); if (!dsym) // no dollar exists -- search in higher scope return NULL; VarDeclaration *v = ae->lengthVar; if (!v) { // $ is lazily initialized. Create it now. TemplateDeclaration *td = dsym->isTemplateDeclaration(); if (td) { // Instantiate opDollar!(dim) with the index as a template argument Objects *tdargs = new Objects(); tdargs->setDim(1); Expression *x = new IntegerExp(0, ae->currentDimension, Type::tsize_t); x = x->semantic(sc); tdargs->data[0] = x; //TemplateInstance *ti = new TemplateInstance(loc, td, tdargs); //ti->semantic(sc); DotTemplateInstanceExp *dte = new DotTemplateInstanceExp(loc, ae->e1, td->ident, tdargs); v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(0, dte)); } else { /* opDollar exists, but it's a function, not a template. * This is acceptable ONLY for single-dimension indexing. * Note that it's impossible to have both template & function opDollar, * because both take no arguments. */ if (ae->arguments->dim != 1) { ae->error("%s only defines opDollar for one dimension", ad->toChars()); return NULL; } FuncDeclaration *fd = dsym->isFuncDeclaration(); assert(fd); Expression * x = new DotVarExp(loc, ae->e1, fd); v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(0, x)); } v->semantic(sc); ae->lengthVar = v; } return v; } else /* Didn't find $, look in enclosing scope(s). */ return NULL; /* If we are indexing into an array that is really a type * tuple, rewrite this as an index into a type tuple and * try again. */ if (ce->op == TOKtype) { Type *t = ((TypeExp *)ce)->type; if (t->ty == Ttuple) { type = (TypeTuple *)t; goto L1; } } /* *pvar is lazily initialized, so if we refer to $ * multiple times, it gets set only once. */ if (!*pvar) // if not already initialized { /* Create variable v and set it to the value of $ */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); if (ce->op == TOKtuple) { /* It is for an expression tuple, so the * length will be a const. */ Expression *e = new IntegerExp(0, ((TupleExp *)ce)->exps->dim, Type::tsize_t); v->init = new ExpInitializer(0, e); v->storage_class |= STCstatic | STCconst; } else { /* For arrays, $ will either be a compile-time constant * (in which case its value in set during constant-folding), * or a variable (in which case an expression is created in * toir.c). */ VoidInitializer *e = new VoidInitializer(0); e->type = Type::tsize_t; v->init = e; v->storage_class |= STCctfe; // it's never a true static variable } *pvar = v; } (*pvar)->semantic(sc); return (*pvar); } return NULL; }
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 forward reference { //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.gaggedErrors; 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]; b->type = b->type->semantic(loc, sc); Type *tb = b->type->toBasetype(); if (tb->ty == Ttuple) { TypeTuple *tup = (TypeTuple *)tb; enum 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 (/*sc->mustsemantic &&*/ 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 (/*sc->mustsemantic &&*/ 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 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; 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 isnested = true; 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()); } isnested = false; } } else if (!(storage_class & STCstatic)) { Dsymbol *s = toParent2(); if (s) { AggregateDeclaration *ad = s->isClassDeclaration(); FuncDeclaration *fd = s->isFuncDeclaration(); if (ad || fd) { isnested = true; Type *t; if (ad) t = ad->handle; else if (fd) { AggregateDeclaration *ad2 = fd->isMember2(); if (ad2) t = ad2->handle; else { t = Type::tvoidptr; } } else assert(0); if (t->ty == Tstruct) // ref to struct t = Type::tvoidptr; assert(!vthis); vthis = new ThisDeclaration(loc, t); members->push(vthis); } } } } 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 IN_LLVM if (global.params.targetTriple.isOSWindows()) #else if (global.params.isWindows) #endif 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; // if (isnested) // sc->offset += PTRSIZE; // room for uplevel context pointer } else { sc->offset = PTRSIZE * 2; // allow room for __vptr and __monitor alignsize = 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]; /* There are problems doing this in the general case because * Scope keeps track of things like 'offset' */ if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident) || s->isTemplateMixin() || s->isAttribDeclaration() || s->isAliasDeclaration()) { //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; bool isunion = isUnionDeclaration() != NULL; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->setFieldOffset(this, &offset, false); } sc->offset = structsize; if (global.gag && global.gaggedErrors != errors) { // The type is no good, yet the error messages were gagged. 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(0, 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 #endif // dtor = (DtorDeclaration *)search(Id::dtor, 0); // if (dtor && dtor->toParent() != this) // dtor = NULL; // inv = (InvariantDeclaration *)search(Id::classInvariant, 0); // if (inv && inv->toParent() != this) // inv = NULL; // Can be in base class aggNew = (NewDeclaration *)search(0, Id::classNew, 0); aggDelete = (DeleteDeclaration *)search(0, Id::classDelete, 0); // If this class has no constructor, but base class does, create // a constructor: // this() { } if (!ctor && baseClass && baseClass->ctor) { //printf("Creating default this(){} for class %s\n", toChars()); Type *tf = new TypeFunction(NULL, NULL, 0, LINKd, 0); CtorDeclaration *ctor = new CtorDeclaration(loc, 0, 0, tf); ctor->isImplicit = true; ctor->fbody = new CompoundStatement(0, 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; } #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 = 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; #if IN_LLVM if (sc->structalign == STRUCTALIGN_DEFAULT) structsize = (structsize + alignsize - 1) & ~(alignsize - 1); else structsize = (structsize + sc->structalign - 1) & ~(sc->structalign - 1); #endif sizeok = SIZEOKdone; Module::dprogress++; dtor = buildDtor(sc); if (Dsymbol *assign = search_function(this, Id::assign)) { if (FuncDeclaration *f = hasIdentityOpAssign(sc, assign)) { 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); } }
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 inferApplyArgTypes(enum TOK op, Parameters *arguments, Expression *aggr, Module* from) { if (!arguments || !arguments->dim) return; /* Return if no arguments need types. */ for (size_t u = 0; 1; u++) { if (u == arguments->dim) return; Parameter *arg = arguments->tdata()[u]; if (!arg->type) break; } Dsymbol *s; AggregateDeclaration *ad; Parameter *arg = arguments->tdata()[0]; Type *taggr = aggr->type; if (!taggr) return; Type *tab = taggr->toBasetype(); switch (tab->ty) { case Tarray: case Tsarray: case Ttuple: if (arguments->dim == 2) { if (!arg->type) arg->type = Type::tsize_t; // key type arg = arguments->tdata()[1]; } if (!arg->type && tab->ty != Ttuple) arg->type = tab->nextOf(); // value type break; case Taarray: { TypeAArray *taa = (TypeAArray *)tab; if (arguments->dim == 2) { if (!arg->type) arg->type = taa->index; // key type arg = arguments->tdata()[1]; } if (!arg->type) arg->type = taa->next; // value type break; } case Tclass: ad = ((TypeClass *)tab)->sym; goto Laggr; case Tstruct: ad = ((TypeStruct *)tab)->sym; goto Laggr; Laggr: s = search_function(ad, (op == TOKforeach_reverse) ? Id::applyReverse : Id::apply); if (s) goto Lapply; // prefer opApply if (arguments->dim == 1) { if (!arg->type) { /* Look for a head() or rear() overload */ Identifier *id = (op == TOKforeach) ? Id::Fhead : Id::Ftoe; Dsymbol *s = search_function(ad, id); FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL; if (!fd) { if (s && s->isTemplateDeclaration()) break; goto Lapply; } arg->type = fd->type->nextOf(); } break; } Lapply: { /* Look for an * int opApply(int delegate(ref Type [, ...]) dg); * overload */ if (s) { FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { inferApplyArgTypesX(from, fd, arguments); break; } #if 0 TemplateDeclaration *td = s->isTemplateDeclaration(); if (td) { inferApplyArgTypesZ(td, arguments); break; } #endif } break; } case Tdelegate: { if (0 && aggr->op == TOKdelegate) { DelegateExp *de = (DelegateExp *)aggr; FuncDeclaration *fd = de->func->isFuncDeclaration(); if (fd) inferApplyArgTypesX(from, fd, arguments); } else { inferApplyArgTypesY((TypeFunction *)tab->nextOf(), arguments); } break; } default: break; // ignore error, caught later } }
void TypeInfoStructDeclaration::toDt(dt_t **pdt) { //printf("TypeInfoStructDeclaration::toDt() '%s'\n", toChars()); unsigned offset = Type::typeinfostruct->structsize; dtxoff(pdt, Type::typeinfostruct->toVtblSymbol(), 0, TYnptr); // vtbl for TypeInfo_Struct dtsize_t(pdt, 0); // monitor assert(tinfo->ty == Tstruct); TypeStruct *tc = (TypeStruct *)tinfo; StructDeclaration *sd = tc->sym; /* Put out: * char[] name; * void[] init; * hash_t function(in void*) xtoHash; * bool function(in void*, in void*) xopEquals; * int function(in void*, in void*) xopCmp; * string function(const(void)*) xtoString; * uint m_flags; * xgetMembers; * xdtor; * xpostblit; * uint m_align; * version (X86_64) * TypeInfo m_arg1; * TypeInfo m_arg2; * * name[] */ const char *name = sd->toPrettyChars(); size_t namelen = strlen(name); dtsize_t(pdt, namelen); //dtabytes(pdt, TYnptr, 0, namelen + 1, name); dtxoff(pdt, toSymbol(), offset, TYnptr); offset += namelen + 1; // void[] init; dtsize_t(pdt, sd->structsize); // init.length if (sd->zeroInit) dtsize_t(pdt, 0); // NULL for 0 initialization else dtxoff(pdt, sd->toInitializer(), 0, TYnptr); // init.ptr FuncDeclaration *fd; FuncDeclaration *fdx; Dsymbol *s; static TypeFunction *tftohash; static TypeFunction *tftostring; if (!tftohash) { Scope sc; /* const hash_t toHash(); */ tftohash = new TypeFunction(NULL, Type::thash_t, 0, LINKd); tftohash->mod = MODconst; tftohash = (TypeFunction *)tftohash->semantic(0, &sc); tftostring = new TypeFunction(NULL, Type::tchar->invariantOf()->arrayOf(), 0, LINKd); tftostring = (TypeFunction *)tftostring->semantic(0, &sc); } TypeFunction *tfcmpptr; { Scope sc; /* const int opCmp(ref const KeyType s); */ Parameters *arguments = new Parameters; #if STRUCTTHISREF // arg type is ref const T Parameter *arg = new Parameter(STCref, tc->constOf(), NULL, NULL); #else // arg type is const T* Parameter *arg = new Parameter(STCin, tc->pointerTo(), NULL, NULL); #endif arguments->push(arg); tfcmpptr = new TypeFunction(arguments, Type::tint32, 0, LINKd); tfcmpptr->mod = MODconst; tfcmpptr = (TypeFunction *)tfcmpptr->semantic(0, &sc); } s = search_function(sd, Id::tohash); fdx = s ? s->isFuncDeclaration() : NULL; if (fdx) { fd = fdx->overloadExactMatch(tftohash); if (fd) { dtxoff(pdt, fd->toSymbol(), 0, TYnptr); TypeFunction *tf = (TypeFunction *)fd->type; assert(tf->ty == Tfunction); if (global.params.warnings) { /* I'm a little unsure this is the right way to do it. Perhaps a better * way would to automatically add these attributes to any struct member * function with the name "toHash". * So I'm leaving this here as an experiment for the moment. */ if (!tf->isnothrow || tf->trust == TRUSTsystem /*|| tf->purity == PUREimpure*/) { warning(fd->loc, "toHash() must be declared as extern (D) uint toHash() const nothrow @safe, not %s", tf->toChars()); if (global.params.warnings == 1) global.errors++; } } } else { //fdx->error("must be declared as extern (D) uint toHash()"); dtsize_t(pdt, 0); } } else dtsize_t(pdt, 0); if (sd->xeq) dtxoff(pdt, sd->xeq->toSymbol(), 0, TYnptr); else dtsize_t(pdt, 0); s = search_function(sd, Id::cmp); fdx = s ? s->isFuncDeclaration() : NULL; if (fdx) { //printf("test1 %s, %s, %s\n", fdx->toChars(), fdx->type->toChars(), tfeqptr->toChars()); fd = fdx->overloadExactMatch(tfcmpptr); if (fd) { dtxoff(pdt, fd->toSymbol(), 0, TYnptr); //printf("test2\n"); } else //fdx->error("must be declared as extern (D) int %s(%s*)", fdx->toChars(), sd->toChars()); dtsize_t(pdt, 0); } else dtsize_t(pdt, 0); s = search_function(sd, Id::tostring); fdx = s ? s->isFuncDeclaration() : NULL; if (fdx) { fd = fdx->overloadExactMatch(tftostring); if (fd) dtxoff(pdt, fd->toSymbol(), 0, TYnptr); else //fdx->error("must be declared as extern (D) char[] toString()"); dtsize_t(pdt, 0); } else dtsize_t(pdt, 0); // uint m_flags; dtsize_t(pdt, tc->hasPointers()); #if DMDV2 // xgetMembers FuncDeclaration *sgetmembers = sd->findGetMembers(); if (sgetmembers) dtxoff(pdt, sgetmembers->toSymbol(), 0, TYnptr); else dtsize_t(pdt, 0); // xgetMembers // xdtor FuncDeclaration *sdtor = sd->dtor; if (sdtor) dtxoff(pdt, sdtor->toSymbol(), 0, TYnptr); else dtsize_t(pdt, 0); // xdtor // xpostblit FuncDeclaration *spostblit = sd->postblit; if (spostblit && !(spostblit->storage_class & STCdisable)) dtxoff(pdt, spostblit->toSymbol(), 0, TYnptr); else dtsize_t(pdt, 0); // xpostblit #endif // uint m_align; dtsize_t(pdt, tc->alignsize()); if (global.params.is64bit) { TypeTuple *tup = tc->toArgTypes(); assert(tup->arguments->dim <= 2); for (size_t i = 0; i < 2; i++) { if (i < tup->arguments->dim) { Type *targ = (tup->arguments->tdata()[i])->type; targ = targ->merge(); targ->getTypeInfo(NULL); dtxoff(pdt, targ->vtinfo->toSymbol(), 0, TYnptr); // m_argi } else dtsize_t(pdt, 0); // m_argi } } // name[] dtnbytes(pdt, namelen + 1, name); }
Expression *BinExp::op_overload(Scope *sc) { //printf("BinExp::op_overload() (%s)\n", toChars()); Identifier *id = opId(); Identifier *id_r = opId_r(); Expressions args1; Expressions args2; int argsset = 0; AggregateDeclaration *ad1 = isAggregate(e1->type); AggregateDeclaration *ad2 = isAggregate(e2->type); Dsymbol *s = NULL; Dsymbol *s_r = NULL; #if 1 // the old D1 scheme if (ad1 && id) { s = search_function(ad1, id); } if (ad2 && id_r) { s_r = search_function(ad2, id_r); } #endif Objects *targsi = NULL; #if DMDV2 if (op == TOKplusplus || op == TOKminusminus) { // Bug4099 fix if (ad1 && search_function(ad1, Id::opUnary)) return NULL; } if (!s && !s_r && op != TOKequal && op != TOKnotequal && op != TOKassign && op != TOKplusplus && op != TOKminusminus) { /* Try the new D2 scheme, opBinary and opBinaryRight */ if (ad1) s = search_function(ad1, Id::opBinary); if (ad2) s_r = search_function(ad2, Id::opBinaryRight); // Set targsi, the template argument list, which will be the operator string if (s || s_r) { id = Id::opBinary; id_r = Id::opBinaryRight; targsi = opToArg(sc, op); } } #endif if (s || s_r) { /* Try: * a.opfunc(b) * b.opfunc_r(a) * and see which is better. */ args1.setDim(1); args1.tdata()[0] = e1; args2.setDim(1); args2.tdata()[0] = e2; argsset = 1; Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; if (s) { FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args2, sc->module); } else { TemplateDeclaration *td = s->isTemplateDeclaration(); templateResolve(&m, td, sc, loc, targsi, e1, &args2); } } FuncDeclaration *lastf = m.lastf; if (s_r) { FuncDeclaration *fd = s_r->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args1, sc->module); } else { TemplateDeclaration *td = s_r->isTemplateDeclaration(); templateResolve(&m, td, sc, loc, targsi, e2, &args1); } } if (m.count > 1) { // Error, ambiguous error("overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), m.nextf->type->toChars(), m.lastf->toChars()); } else if (m.last == MATCHnomatch) { m.lastf = m.anyf; if (targsi) goto L1; } Expression *e; if (op == TOKplusplus || op == TOKminusminus) // Kludge because operator overloading regards e++ and e-- // as unary, but it's implemented as a binary. // Rewrite (e1 ++ e2) as e1.postinc() // Rewrite (e1 -- e2) as e1.postdec() e = build_overload(loc, sc, e1, NULL, m.lastf ? m.lastf : s); else if (lastf && m.lastf == lastf || !s_r && m.last == MATCHnomatch) // Rewrite (e1 op e2) as e1.opfunc(e2) e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); else // Rewrite (e1 op e2) as e2.opfunc_r(e1) e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s_r); return e; } L1: #if 1 // Retained for D1 compatibility if (isCommutative() && !targsi) { s = NULL; s_r = NULL; if (ad1 && id_r) { s_r = search_function(ad1, id_r); } if (ad2 && id) { s = search_function(ad2, id); } if (s || s_r) { /* Try: * a.opfunc_r(b) * b.opfunc(a) * and see which is better. */ if (!argsset) { args1.setDim(1); args1.tdata()[0] = e1; args2.setDim(1); args2.tdata()[0] = e2; } Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; if (s_r) { FuncDeclaration *fd = s_r->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args2, sc->module); } else { TemplateDeclaration *td = s_r->isTemplateDeclaration(); templateResolve(&m, td, sc, loc, targsi, e1, &args2); } } FuncDeclaration *lastf = m.lastf; if (s) { FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args1, sc->module); } else { TemplateDeclaration *td = s->isTemplateDeclaration(); templateResolve(&m, td, sc, loc, targsi, e2, &args1); } } if (m.count > 1) { // Error, ambiguous error("overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), m.nextf->type->toChars(), m.lastf->toChars()); } else if (m.last == MATCHnomatch) { m.lastf = m.anyf; } Expression *e; if (lastf && m.lastf == lastf || !s && m.last == MATCHnomatch) // Rewrite (e1 op e2) as e1.opfunc_r(e2) e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s_r); else // Rewrite (e1 op e2) as e2.opfunc(e1) e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s); // When reversing operands of comparison operators, // need to reverse the sense of the op switch (op) { case TOKlt: op = TOKgt; break; case TOKgt: op = TOKlt; break; case TOKle: op = TOKge; break; case TOKge: op = TOKle; break; // Floating point compares case TOKule: op = TOKuge; break; case TOKul: op = TOKug; break; case TOKuge: op = TOKule; break; case TOKug: op = TOKul; break; // These are symmetric case TOKunord: case TOKlg: case TOKleg: case TOKue: break; } return e; } } #endif #if DMDV2 // Try alias this on first operand if (ad1 && ad1->aliasthis && !(op == TOKassign && ad2 && ad1 == ad2)) // See Bugzilla 2943 { /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); Expression *e = copy(); ((BinExp *)e)->e1 = e1; e = e->trySemantic(sc); return e; } // Try alias this on second operand if (ad2 && ad2->aliasthis && /* Bugzilla 2943: make sure that when we're copying the struct, we don't * just copy the alias this member */ !(op == TOKassign && ad1 && ad1 == ad2)) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); Expression *e = copy(); ((BinExp *)e)->e2 = e2; e = e->trySemantic(sc); return e; } #endif return NULL; }
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 == 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.gaggedErrors; unsigned dprogress_save = Module::dprogress; parent = sc->parent; type = type->semantic(loc, sc); #if STRUCTTHISREF handle = type; #else handle = type->pointerTo(); #endif 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 { int hasfunctions = 0; 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); 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 = 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; 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("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(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); hasIdentityAssign = (buildOpAssign(sc2) != NULL); 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); 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.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); } }
/********************************* * Operator overloading for op= */ Expression *BinAssignExp::op_overload(Scope *sc) { //printf("BinAssignExp::op_overload() (%s)\n", toChars()); #if DMDV2 if (e1->op == TOKarray) { ArrayExp *ae = (ArrayExp *)e1; ae->e1 = ae->e1->semantic(sc); ae->e1 = resolveProperties(sc, ae->e1); AggregateDeclaration *ad = isAggregate(ae->e1->type); if (ad) { /* Rewrite a[args]+=e2 as: * a.opIndexOpAssign!("+")(e2, args); */ Dsymbol *fd = search_function(ad, Id::opIndexOpAssign); if (fd) { Expressions *a = new Expressions(); a->push(e2); for (size_t i = 0; i < ae->arguments->dim; i++) a->push(ae->arguments->tdata()[i]); Objects *targsi = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, ae->e1, fd->ident, targsi); e = new CallExp(loc, e, a); e = e->semantic(sc); return e; } // Didn't find it. Forward to aliasthis if (ad->aliasthis) { /* Rewrite a[arguments] op= e2 as: * a.aliasthis[arguments] op= e2 */ Expression *e1 = ae->copy(); ((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); Expression *e = copy(); ((UnaExp *)e)->e1 = e1; e = e->trySemantic(sc); return e; } } } else if (e1->op == TOKslice) { SliceExp *se = (SliceExp *)e1; se->e1 = se->e1->semantic(sc); se->e1 = resolveProperties(sc, se->e1); AggregateDeclaration *ad = isAggregate(se->e1->type); if (ad) { /* Rewrite a[lwr..upr]+=e2 as: * a.opSliceOpAssign!("+")(e2, lwr, upr); */ Dsymbol *fd = search_function(ad, Id::opSliceOpAssign); if (fd) { Expressions *a = new Expressions(); a->push(e2); if (se->lwr) { a->push(se->lwr); a->push(se->upr); } Objects *targsi = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, se->e1, fd->ident, targsi); e = new CallExp(loc, e, a); e = e->semantic(sc); return e; } // Didn't find it. Forward to aliasthis if (ad->aliasthis) { /* Rewrite a[lwr..upr] op= e2 as: * a.aliasthis[lwr..upr] op= e2 */ Expression *e1 = se->copy(); ((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident); Expression *e = copy(); ((UnaExp *)e)->e1 = e1; e = e->trySemantic(sc); return e; } } } #endif BinExp::semantic(sc); e1 = resolveProperties(sc, e1); e2 = resolveProperties(sc, e2); Identifier *id = opId(); Expressions args2; AggregateDeclaration *ad1 = isAggregate(e1->type); Dsymbol *s = NULL; #if 1 // the old D1 scheme if (ad1 && id) { s = search_function(ad1, id); } #endif Objects *targsi = NULL; #if DMDV2 if (!s) { /* Try the new D2 scheme, opOpAssign */ if (ad1) s = search_function(ad1, Id::opOpAssign); // Set targsi, the template argument list, which will be the operator string if (s) { id = Id::opOpAssign; targsi = opToArg(sc, op); } } #endif if (s) { /* Try: * a.opOpAssign(b) */ args2.setDim(1); args2.tdata()[0] = e2; Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; if (s) { FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { overloadResolveX(&m, fd, NULL, &args2, sc->module); } else { TemplateDeclaration *td = s->isTemplateDeclaration(); templateResolve(&m, td, sc, loc, targsi, e1, &args2); } } if (m.count > 1) { // Error, ambiguous error("overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), m.nextf->type->toChars(), m.lastf->toChars()); } else if (m.last == MATCHnomatch) { m.lastf = m.anyf; if (targsi) goto L1; } // Rewrite (e1 op e2) as e1.opOpAssign(e2) return build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); } L1: #if DMDV2 // Try alias this on first operand if (ad1 && ad1->aliasthis) { /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); Expression *e = copy(); ((BinExp *)e)->e1 = e1; e = e->trySemantic(sc); return e; } // Try alias this on second operand AggregateDeclaration *ad2 = isAggregate(e2->type); if (ad2 && ad2->aliasthis) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); Expression *e = copy(); ((BinExp *)e)->e2 = e2; e = e->trySemantic(sc); return e; } #endif return NULL; }
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); } }