]>
Commit | Line | Data |
---|---|---|
b4c522fa IB |
1 | |
2 | /* Compiler implementation of the D programming language | |
a3b38b77 | 3 | * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved |
b4c522fa IB |
4 | * written by Walter Bright |
5 | * http://www.digitalmars.com | |
6 | * Distributed under the Boost Software License, Version 1.0. | |
7 | * http://www.boost.org/LICENSE_1_0.txt | |
8 | * https://github.com/D-Programming-Language/dmd/blob/master/src/clone.c | |
9 | */ | |
10 | ||
f9ab59ff | 11 | #include "root/dsystem.h" |
b4c522fa | 12 | #include "root/root.h" |
f9ab59ff | 13 | |
b4c522fa IB |
14 | #include "aggregate.h" |
15 | #include "scope.h" | |
16 | #include "mtype.h" | |
17 | #include "declaration.h" | |
18 | #include "module.h" | |
19 | #include "id.h" | |
20 | #include "expression.h" | |
21 | #include "statement.h" | |
22 | #include "init.h" | |
23 | #include "template.h" | |
24 | #include "tokens.h" | |
25 | ||
b4c522fa IB |
26 | /******************************************* |
27 | * Merge function attributes pure, nothrow, @safe, @nogc, and @disable | |
28 | */ | |
29 | StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration *f) | |
30 | { | |
31 | if (!f) | |
32 | return s1; | |
33 | ||
34 | StorageClass s2 = (f->storage_class & STCdisable); | |
35 | TypeFunction *tf = (TypeFunction *)f->type; | |
36 | if (tf->trust == TRUSTsafe) | |
37 | s2 |= STCsafe; | |
38 | else if (tf->trust == TRUSTsystem) | |
39 | s2 |= STCsystem; | |
40 | else if (tf->trust == TRUSTtrusted) | |
41 | s2 |= STCtrusted; | |
42 | if (tf->purity != PUREimpure) | |
43 | s2 |= STCpure; | |
44 | if (tf->isnothrow) | |
45 | s2 |= STCnothrow; | |
46 | if (tf->isnogc) | |
47 | s2 |= STCnogc; | |
48 | ||
49 | StorageClass stc = 0; | |
50 | StorageClass sa = s1 & s2; | |
51 | StorageClass so = s1 | s2; | |
52 | ||
53 | if (so & STCsystem) | |
54 | stc |= STCsystem; | |
55 | else if (sa & STCtrusted) | |
56 | stc |= STCtrusted; | |
57 | else if ((so & (STCtrusted | STCsafe)) == (STCtrusted | STCsafe)) | |
58 | stc |= STCtrusted; | |
59 | else if (sa & STCsafe) | |
60 | stc |= STCsafe; | |
61 | ||
62 | if (sa & STCpure) | |
63 | stc |= STCpure; | |
64 | ||
65 | if (sa & STCnothrow) | |
66 | stc |= STCnothrow; | |
67 | ||
68 | if (sa & STCnogc) | |
69 | stc |= STCnogc; | |
70 | ||
71 | if (so & STCdisable) | |
72 | stc |= STCdisable; | |
73 | ||
74 | return stc; | |
75 | } | |
76 | ||
77 | /******************************************* | |
78 | * Check given aggregate actually has an identity opAssign or not. | |
79 | * Params: | |
80 | * ad = struct or class | |
81 | * sc = current scope | |
82 | * Returns: | |
83 | * if found, returns FuncDeclaration of opAssign, otherwise null | |
84 | */ | |
85 | FuncDeclaration *hasIdentityOpAssign(AggregateDeclaration *ad, Scope *sc) | |
86 | { | |
87 | Dsymbol *assign = search_function(ad, Id::assign); | |
88 | if (assign) | |
89 | { | |
90 | /* check identity opAssign exists | |
91 | */ | |
92 | UnionExp er; new(&er) NullExp(ad->loc, ad->type); // dummy rvalue | |
93 | UnionExp el; new(&el) IdentifierExp(ad->loc, Id::p); // dummy lvalue | |
94 | el.exp()->type = ad->type; | |
95 | Expressions a; | |
96 | a.setDim(1); | |
97 | ||
98 | unsigned errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. | |
99 | sc = sc->push(); | |
100 | sc->tinst = NULL; | |
101 | sc->minst = NULL; | |
102 | ||
103 | a[0] = er.exp(); | |
104 | FuncDeclaration *f = resolveFuncCall(ad->loc, sc, assign, NULL, ad->type, &a, 1); | |
105 | if (!f) | |
106 | { | |
107 | a[0] = el.exp(); | |
108 | f = resolveFuncCall(ad->loc, sc, assign, NULL, ad->type, &a, 1); | |
109 | } | |
110 | ||
111 | sc = sc->pop(); | |
112 | global.endGagging(errors); | |
113 | ||
114 | if (f) | |
115 | { | |
116 | if (f->errors) | |
117 | return NULL; | |
c3a2ba10 IB |
118 | ParameterList fparams = f->getParameterList(); |
119 | if (fparams.length()) | |
b4c522fa | 120 | { |
c3a2ba10 | 121 | Parameter *fparam0 = fparams[0]; |
b4c522fa IB |
122 | if (fparam0->type->toDsymbol(NULL) != ad) |
123 | f = NULL; | |
124 | } | |
125 | } | |
126 | // BUGS: This detection mechanism cannot find some opAssign-s like follows: | |
127 | // struct S { void opAssign(ref immutable S) const; } | |
128 | return f; | |
129 | } | |
130 | return NULL; | |
131 | } | |
132 | ||
133 | /******************************************* | |
134 | * We need an opAssign for the struct if | |
135 | * it has a destructor or a postblit. | |
136 | * We need to generate one if a user-specified one does not exist. | |
137 | */ | |
138 | bool needOpAssign(StructDeclaration *sd) | |
139 | { | |
140 | //printf("StructDeclaration::needOpAssign() %s\n", sd->toChars()); | |
141 | if (sd->isUnionDeclaration()) | |
142 | return false; | |
143 | ||
144 | if (sd->hasIdentityAssign) | |
145 | goto Lneed; // because has identity==elaborate opAssign | |
146 | ||
147 | if (sd->dtor || sd->postblit) | |
148 | goto Lneed; | |
149 | ||
150 | /* If any of the fields need an opAssign, then we | |
151 | * need it too. | |
152 | */ | |
2cbc99d1 | 153 | for (size_t i = 0; i < sd->fields.length; i++) |
b4c522fa IB |
154 | { |
155 | VarDeclaration *v = sd->fields[i]; | |
156 | if (v->storage_class & STCref) | |
157 | continue; | |
158 | if (v->overlapped) // if field of a union | |
159 | continue; // user must handle it themselves | |
160 | Type *tv = v->type->baseElemOf(); | |
161 | if (tv->ty == Tstruct) | |
162 | { | |
163 | TypeStruct *ts = (TypeStruct *)tv; | |
164 | if (ts->sym->isUnionDeclaration()) | |
165 | continue; | |
166 | if (needOpAssign(ts->sym)) | |
167 | goto Lneed; | |
168 | } | |
169 | } | |
170 | //printf("\tdontneed\n"); | |
171 | return false; | |
172 | ||
173 | Lneed: | |
174 | //printf("\tneed\n"); | |
175 | return true; | |
176 | } | |
177 | ||
178 | /****************************************** | |
179 | * Build opAssign for struct. | |
180 | * ref S opAssign(S s) { ... } | |
181 | * | |
182 | * Note that s will be constructed onto the stack, and probably | |
183 | * copy-constructed in caller site. | |
184 | * | |
185 | * If S has copy copy construction and/or destructor, | |
186 | * the body will make bit-wise object swap: | |
187 | * S __swap = this; // bit copy | |
188 | * this = s; // bit copy | |
189 | * __swap.dtor(); | |
190 | * Instead of running the destructor on s, run it on tmp instead. | |
191 | * | |
192 | * Otherwise, the body will make member-wise assignments: | |
193 | * Then, the body is: | |
194 | * this.field1 = s.field1; | |
195 | * this.field2 = s.field2; | |
196 | * ...; | |
197 | */ | |
198 | FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc) | |
199 | { | |
200 | if (FuncDeclaration *f = hasIdentityOpAssign(sd, sc)) | |
201 | { | |
202 | sd->hasIdentityAssign = true; | |
203 | return f; | |
204 | } | |
205 | // Even if non-identity opAssign is defined, built-in identity opAssign | |
206 | // will be defined. | |
207 | ||
208 | if (!needOpAssign(sd)) | |
209 | return NULL; | |
210 | ||
211 | //printf("StructDeclaration::buildOpAssign() %s\n", sd->toChars()); | |
212 | StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; | |
213 | Loc declLoc = sd->loc; | |
214 | Loc loc = Loc(); // internal code should have no loc to prevent coverage | |
215 | ||
216 | // One of our sub-field might have `@disable opAssign` so we need to | |
217 | // check for it. | |
218 | // In this event, it will be reflected by having `stc` (opAssign's | |
219 | // storage class) include `STCdisabled`. | |
2cbc99d1 | 220 | for (size_t i = 0; i < sd->fields.length; i++) |
b4c522fa IB |
221 | { |
222 | VarDeclaration *v = sd->fields[i]; | |
223 | if (v->storage_class & STCref) | |
224 | continue; | |
225 | if (v->overlapped) | |
226 | continue; | |
227 | Type *tv = v->type->baseElemOf(); | |
228 | if (tv->ty != Tstruct) | |
229 | continue; | |
230 | ||
231 | StructDeclaration *sdv = ((TypeStruct *)tv)->sym; | |
232 | stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc)); | |
233 | } | |
234 | ||
235 | if (sd->dtor || sd->postblit) | |
236 | { | |
237 | if (!sd->type->isAssignable()) // Bugzilla 13044 | |
238 | return NULL; | |
239 | stc = mergeFuncAttrs(stc, sd->dtor); | |
240 | if (stc & STCsafe) | |
241 | stc = (stc & ~STCsafe) | STCtrusted; | |
242 | } | |
243 | ||
244 | Parameters *fparams = new Parameters; | |
dddea6d4 | 245 | fparams->push(new Parameter(STCnodtor, sd->type, Id::p, NULL, NULL)); |
c3a2ba10 | 246 | TypeFunction *tf = new TypeFunction(ParameterList(fparams), sd->handleType(), LINKd, stc | STCref); |
b4c522fa IB |
247 | |
248 | FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), Id::assign, stc, tf); | |
249 | fop->storage_class |= STCinference; | |
250 | fop->generated = true; | |
251 | Expression *e = NULL; | |
252 | if (stc & STCdisable) | |
253 | { | |
254 | } | |
255 | else if (sd->dtor || sd->postblit) | |
256 | { | |
257 | /* Do swap this and rhs. | |
258 | * __swap = this; this = s; __swap.dtor(); | |
259 | */ | |
260 | //printf("\tswap copy\n"); | |
261 | Identifier *idtmp = Identifier::generateId("__swap"); | |
262 | VarDeclaration *tmp = NULL; | |
263 | AssignExp *ec = NULL; | |
264 | if (sd->dtor) | |
265 | { | |
266 | tmp = new VarDeclaration(loc, sd->type, idtmp, new VoidInitializer(loc)); | |
267 | tmp->storage_class |= STCnodtor | STCtemp | STCctfe; | |
268 | e = new DeclarationExp(loc, tmp); | |
269 | ec = new BlitExp(loc, new VarExp(loc, tmp), new ThisExp(loc)); | |
270 | e = Expression::combine(e, ec); | |
271 | } | |
272 | ec = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id::p)); | |
273 | e = Expression::combine(e, ec); | |
274 | if (sd->dtor) | |
275 | { | |
276 | /* Instead of running the destructor on s, run it | |
277 | * on tmp. This avoids needing to copy tmp back in to s. | |
278 | */ | |
279 | Expression *ec2 = new DotVarExp(loc, new VarExp(loc, tmp), sd->dtor, false); | |
280 | ec2 = new CallExp(loc, ec2); | |
281 | e = Expression::combine(e, ec2); | |
282 | } | |
283 | } | |
284 | else | |
285 | { | |
286 | /* Do memberwise copy. | |
287 | * | |
288 | * If sd is a nested struct, its vthis field assignment is: | |
289 | * 1. If it's nested in a class, it's a rebind of class reference. | |
290 | * 2. If it's nested in a function or struct, it's an update of void*. | |
291 | * In both cases, it will change the parent context. | |
292 | */ | |
293 | //printf("\tmemberwise copy\n"); | |
2cbc99d1 | 294 | for (size_t i = 0; i < sd->fields.length; i++) |
b4c522fa IB |
295 | { |
296 | VarDeclaration *v = sd->fields[i]; | |
297 | // this.v = s.v; | |
298 | AssignExp *ec = new AssignExp(loc, | |
299 | new DotVarExp(loc, new ThisExp(loc), v), | |
300 | new DotVarExp(loc, new IdentifierExp(loc, Id::p), v)); | |
301 | e = Expression::combine(e, ec); | |
302 | } | |
303 | } | |
304 | if (e) | |
305 | { | |
306 | Statement *s1 = new ExpStatement(loc, e); | |
307 | ||
308 | /* Add: | |
309 | * return this; | |
310 | */ | |
311 | e = new ThisExp(loc); | |
312 | Statement *s2 = new ReturnStatement(loc, e); | |
313 | ||
314 | fop->fbody = new CompoundStatement(loc, s1, s2); | |
315 | tf->isreturn = true; | |
316 | } | |
317 | ||
318 | sd->members->push(fop); | |
319 | fop->addMember(sc, sd); | |
320 | sd->hasIdentityAssign = true; // temporary mark identity assignable | |
321 | ||
322 | unsigned errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. | |
323 | Scope *sc2 = sc->push(); | |
324 | sc2->stc = 0; | |
325 | sc2->linkage = LINKd; | |
326 | ||
a3b38b77 IB |
327 | dsymbolSemantic(fop, sc2); |
328 | semantic2(fop, sc2); | |
b4c522fa IB |
329 | // Bugzilla 15044: fop->semantic3 isn't run here for lazy forward reference resolution. |
330 | ||
331 | sc2->pop(); | |
332 | if (global.endGagging(errors)) // if errors happened | |
333 | { | |
334 | // Disable generated opAssign, because some members forbid identity assignment. | |
335 | fop->storage_class |= STCdisable; | |
336 | fop->fbody = NULL; // remove fbody which contains the error | |
337 | } | |
338 | ||
339 | //printf("-StructDeclaration::buildOpAssign() %s, errors = %d\n", sd->toChars(), (fop->storage_class & STCdisable) != 0); | |
340 | ||
341 | return fop; | |
342 | } | |
343 | ||
344 | /******************************************* | |
345 | * We need an opEquals for the struct if | |
346 | * any fields has an opEquals. | |
347 | * Generate one if a user-specified one does not exist. | |
348 | */ | |
349 | bool needOpEquals(StructDeclaration *sd) | |
350 | { | |
351 | //printf("StructDeclaration::needOpEquals() %s\n", sd->toChars()); | |
352 | if (sd->isUnionDeclaration()) | |
353 | goto Ldontneed; | |
354 | ||
355 | if (sd->hasIdentityEquals) | |
356 | goto Lneed; | |
357 | ||
358 | /* If any of the fields has an opEquals, then we | |
359 | * need it too. | |
360 | */ | |
2cbc99d1 | 361 | for (size_t i = 0; i < sd->fields.length; i++) |
b4c522fa IB |
362 | { |
363 | VarDeclaration *v = sd->fields[i]; | |
364 | if (v->storage_class & STCref) | |
365 | continue; | |
366 | if (v->overlapped) | |
367 | continue; | |
368 | Type *tv = v->type->toBasetype(); | |
369 | Type *tvbase = tv->baseElemOf(); | |
370 | if (tvbase->ty == Tstruct) | |
371 | { | |
372 | TypeStruct *ts = (TypeStruct *)tvbase; | |
373 | if (ts->sym->isUnionDeclaration()) | |
374 | continue; | |
375 | if (needOpEquals(ts->sym)) | |
376 | goto Lneed; | |
377 | if (ts->sym->aliasthis) // Bugzilla 14806 | |
378 | goto Lneed; | |
379 | } | |
380 | if (tv->isfloating()) | |
381 | { | |
382 | // This is necessray for: | |
383 | // 1. comparison of +0.0 and -0.0 should be true. | |
384 | // 2. comparison of NANs should be false always. | |
385 | goto Lneed; | |
386 | } | |
387 | if (tv->ty == Tarray) | |
388 | goto Lneed; | |
389 | if (tv->ty == Taarray) | |
390 | goto Lneed; | |
391 | if (tv->ty == Tclass) | |
392 | goto Lneed; | |
393 | } | |
394 | Ldontneed: | |
395 | //printf("\tdontneed\n"); | |
396 | return false; | |
397 | ||
398 | Lneed: | |
399 | //printf("\tneed\n"); | |
400 | return true; | |
401 | } | |
402 | ||
403 | /******************************************* | |
404 | * Check given aggregate actually has an identity opEquals or not. | |
405 | */ | |
406 | FuncDeclaration *hasIdentityOpEquals(AggregateDeclaration *ad, Scope *sc) | |
407 | { | |
408 | Dsymbol *eq = search_function(ad, Id::eq); | |
409 | if (eq) | |
410 | { | |
411 | /* check identity opEquals exists | |
412 | */ | |
413 | UnionExp er; new(&er) NullExp(ad->loc, NULL); // dummy rvalue | |
414 | UnionExp el; new(&el) IdentifierExp(ad->loc, Id::p); // dummy lvalue | |
415 | Expressions a; | |
416 | a.setDim(1); | |
417 | for (size_t i = 0; i < 5; i++) | |
418 | { | |
419 | Type *tthis = NULL; // dead-store to prevent spurious warning | |
420 | switch (i) | |
421 | { | |
422 | case 0: tthis = ad->type; break; | |
423 | case 1: tthis = ad->type->constOf(); break; | |
424 | case 2: tthis = ad->type->immutableOf(); break; | |
425 | case 3: tthis = ad->type->sharedOf(); break; | |
426 | case 4: tthis = ad->type->sharedConstOf(); break; | |
427 | default: assert(0); | |
428 | } | |
429 | FuncDeclaration *f = NULL; | |
430 | ||
431 | unsigned errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. | |
432 | sc = sc->push(); | |
433 | sc->tinst = NULL; | |
434 | sc->minst = NULL; | |
435 | ||
436 | for (size_t j = 0; j < 2; j++) | |
437 | { | |
438 | a[0] = (j == 0 ? er.exp() : el.exp()); | |
439 | a[0]->type = tthis; | |
440 | f = resolveFuncCall(ad->loc, sc, eq, NULL, tthis, &a, 1); | |
441 | if (f) | |
442 | break; | |
443 | } | |
444 | ||
445 | sc = sc->pop(); | |
446 | global.endGagging(errors); | |
447 | ||
448 | if (f) | |
449 | { | |
450 | if (f->errors) | |
451 | return NULL; | |
452 | return f; | |
453 | } | |
454 | } | |
455 | } | |
456 | return NULL; | |
457 | } | |
458 | ||
459 | /****************************************** | |
460 | * Build opEquals for struct. | |
461 | * const bool opEquals(const S s) { ... } | |
462 | * | |
463 | * By fixing bugzilla 3789, opEquals is changed to be never implicitly generated. | |
464 | * Now, struct objects comparison s1 == s2 is translated to: | |
465 | * s1.tupleof == s2.tupleof | |
466 | * to calculate structural equality. See EqualExp::op_overload. | |
467 | */ | |
468 | FuncDeclaration *buildOpEquals(StructDeclaration *sd, Scope *sc) | |
469 | { | |
470 | if (hasIdentityOpEquals(sd, sc)) | |
471 | { | |
472 | sd->hasIdentityEquals = true; | |
473 | } | |
474 | return NULL; | |
475 | } | |
476 | ||
477 | /****************************************** | |
478 | * Build __xopEquals for TypeInfo_Struct | |
479 | * static bool __xopEquals(ref const S p, ref const S q) | |
480 | * { | |
481 | * return p == q; | |
482 | * } | |
483 | * | |
484 | * This is called by TypeInfo.equals(p1, p2). If the struct does not support | |
485 | * const objects comparison, it will throw "not implemented" Error in runtime. | |
486 | */ | |
487 | FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc) | |
488 | { | |
489 | if (!needOpEquals(sd)) | |
490 | return NULL; // bitwise comparison would work | |
491 | ||
492 | //printf("StructDeclaration::buildXopEquals() %s\n", sd->toChars()); | |
493 | if (Dsymbol *eq = search_function(sd, Id::eq)) | |
494 | { | |
495 | if (FuncDeclaration *fd = eq->isFuncDeclaration()) | |
496 | { | |
497 | TypeFunction *tfeqptr; | |
498 | { | |
499 | Scope scx; | |
500 | ||
501 | /* const bool opEquals(ref const S s); | |
502 | */ | |
503 | Parameters *parameters = new Parameters; | |
dddea6d4 | 504 | parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL, NULL)); |
c3a2ba10 | 505 | tfeqptr = new TypeFunction(ParameterList(parameters), Type::tbool, LINKd); |
b4c522fa | 506 | tfeqptr->mod = MODconst; |
a3b38b77 | 507 | tfeqptr = (TypeFunction *)typeSemantic(tfeqptr, Loc(), &scx); |
b4c522fa IB |
508 | } |
509 | fd = fd->overloadExactMatch(tfeqptr); | |
510 | if (fd) | |
511 | return fd; | |
512 | } | |
513 | } | |
514 | ||
515 | if (!sd->xerreq) | |
516 | { | |
517 | // object._xopEquals | |
518 | Identifier *id = Identifier::idPool("_xopEquals"); | |
519 | Expression *e = new IdentifierExp(sd->loc, Id::empty); | |
520 | e = new DotIdExp(sd->loc, e, Id::object); | |
521 | e = new DotIdExp(sd->loc, e, id); | |
a3b38b77 | 522 | e = expressionSemantic(e, sc); |
b4c522fa IB |
523 | Dsymbol *s = getDsymbol(e); |
524 | assert(s); | |
525 | sd->xerreq = s->isFuncDeclaration(); | |
526 | } | |
527 | ||
528 | Loc declLoc = Loc(); // loc is unnecessary so __xopEquals is never called directly | |
529 | Loc loc = Loc(); // loc is unnecessary so errors are gagged | |
530 | ||
531 | Parameters *parameters = new Parameters; | |
dddea6d4 IB |
532 | parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL, NULL)); |
533 | parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL, NULL)); | |
c3a2ba10 | 534 | TypeFunction *tf = new TypeFunction(ParameterList(parameters), Type::tbool, LINKd); |
b4c522fa IB |
535 | |
536 | Identifier *id = Id::xopEquals; | |
537 | FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); | |
538 | fop->generated = true; | |
539 | Expression *e1 = new IdentifierExp(loc, Id::p); | |
540 | Expression *e2 = new IdentifierExp(loc, Id::q); | |
541 | Expression *e = new EqualExp(TOKequal, loc, e1, e2); | |
542 | ||
543 | fop->fbody = new ReturnStatement(loc, e); | |
544 | ||
545 | unsigned errors = global.startGagging(); // Do not report errors | |
546 | Scope *sc2 = sc->push(); | |
547 | sc2->stc = 0; | |
548 | sc2->linkage = LINKd; | |
549 | ||
a3b38b77 IB |
550 | dsymbolSemantic(fop, sc2); |
551 | semantic2(fop, sc2); | |
b4c522fa IB |
552 | |
553 | sc2->pop(); | |
554 | if (global.endGagging(errors)) // if errors happened | |
555 | fop = sd->xerreq; | |
556 | ||
557 | return fop; | |
558 | } | |
559 | ||
560 | /****************************************** | |
561 | * Build __xopCmp for TypeInfo_Struct | |
562 | * static bool __xopCmp(ref const S p, ref const S q) | |
563 | * { | |
564 | * return p.opCmp(q); | |
565 | * } | |
566 | * | |
567 | * This is called by TypeInfo.compare(p1, p2). If the struct does not support | |
568 | * const objects comparison, it will throw "not implemented" Error in runtime. | |
569 | */ | |
570 | FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc) | |
571 | { | |
572 | //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); | |
573 | if (Dsymbol *cmp = search_function(sd, Id::cmp)) | |
574 | { | |
575 | if (FuncDeclaration *fd = cmp->isFuncDeclaration()) | |
576 | { | |
577 | TypeFunction *tfcmpptr; | |
578 | { | |
579 | Scope scx; | |
580 | ||
581 | /* const int opCmp(ref const S s); | |
582 | */ | |
583 | Parameters *parameters = new Parameters; | |
dddea6d4 | 584 | parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL, NULL)); |
c3a2ba10 | 585 | tfcmpptr = new TypeFunction(ParameterList(parameters), Type::tint32, LINKd); |
b4c522fa | 586 | tfcmpptr->mod = MODconst; |
a3b38b77 | 587 | tfcmpptr = (TypeFunction *)typeSemantic(tfcmpptr, Loc(), &scx); |
b4c522fa IB |
588 | } |
589 | fd = fd->overloadExactMatch(tfcmpptr); | |
590 | if (fd) | |
591 | return fd; | |
592 | } | |
593 | } | |
594 | else | |
595 | { | |
596 | // FIXME: doesn't work for recursive alias this | |
597 | return NULL; | |
598 | } | |
599 | ||
600 | if (!sd->xerrcmp) | |
601 | { | |
602 | // object._xopCmp | |
603 | Identifier *id = Identifier::idPool("_xopCmp"); | |
604 | Expression *e = new IdentifierExp(sd->loc, Id::empty); | |
605 | e = new DotIdExp(sd->loc, e, Id::object); | |
606 | e = new DotIdExp(sd->loc, e, id); | |
a3b38b77 | 607 | e = expressionSemantic(e, sc); |
b4c522fa IB |
608 | Dsymbol *s = getDsymbol(e); |
609 | assert(s); | |
610 | sd->xerrcmp = s->isFuncDeclaration(); | |
611 | } | |
612 | ||
613 | Loc declLoc = Loc(); // loc is unnecessary so __xopCmp is never called directly | |
614 | Loc loc = Loc(); // loc is unnecessary so errors are gagged | |
615 | ||
616 | Parameters *parameters = new Parameters; | |
dddea6d4 IB |
617 | parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL, NULL)); |
618 | parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL, NULL)); | |
c3a2ba10 | 619 | TypeFunction *tf = new TypeFunction(ParameterList(parameters), Type::tint32, LINKd); |
b4c522fa IB |
620 | |
621 | Identifier *id = Id::xopCmp; | |
622 | FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); | |
623 | fop->generated = true; | |
624 | Expression *e1 = new IdentifierExp(loc, Id::p); | |
625 | Expression *e2 = new IdentifierExp(loc, Id::q); | |
626 | #ifdef IN_GCC | |
627 | Expression *e = new CallExp(loc, new DotIdExp(loc, e1, Id::cmp), e2); | |
628 | #else | |
629 | Expression *e = new CallExp(loc, new DotIdExp(loc, e2, Id::cmp), e1); | |
630 | #endif | |
631 | ||
632 | fop->fbody = new ReturnStatement(loc, e); | |
633 | ||
634 | unsigned errors = global.startGagging(); // Do not report errors | |
635 | Scope *sc2 = sc->push(); | |
636 | sc2->stc = 0; | |
637 | sc2->linkage = LINKd; | |
638 | ||
a3b38b77 IB |
639 | dsymbolSemantic(fop, sc2); |
640 | semantic2(fop, sc2); | |
b4c522fa IB |
641 | |
642 | sc2->pop(); | |
643 | if (global.endGagging(errors)) // if errors happened | |
644 | fop = sd->xerrcmp; | |
645 | ||
646 | return fop; | |
647 | } | |
648 | ||
649 | /******************************************* | |
650 | * We need a toHash for the struct if | |
651 | * any fields has a toHash. | |
652 | * Generate one if a user-specified one does not exist. | |
653 | */ | |
654 | bool needToHash(StructDeclaration *sd) | |
655 | { | |
656 | //printf("StructDeclaration::needToHash() %s\n", sd->toChars()); | |
657 | if (sd->isUnionDeclaration()) | |
658 | goto Ldontneed; | |
659 | ||
660 | if (sd->xhash) | |
661 | goto Lneed; | |
662 | ||
663 | /* If any of the fields has an opEquals, then we | |
664 | * need it too. | |
665 | */ | |
2cbc99d1 | 666 | for (size_t i = 0; i < sd->fields.length; i++) |
b4c522fa IB |
667 | { |
668 | VarDeclaration *v = sd->fields[i]; | |
669 | if (v->storage_class & STCref) | |
670 | continue; | |
671 | if (v->overlapped) | |
672 | continue; | |
673 | Type *tv = v->type->toBasetype(); | |
674 | Type *tvbase = tv->baseElemOf(); | |
675 | if (tvbase->ty == Tstruct) | |
676 | { | |
677 | TypeStruct *ts = (TypeStruct *)tvbase; | |
678 | if (ts->sym->isUnionDeclaration()) | |
679 | continue; | |
680 | if (needToHash(ts->sym)) | |
681 | goto Lneed; | |
682 | if (ts->sym->aliasthis) // Bugzilla 14948 | |
683 | goto Lneed; | |
684 | } | |
685 | if (tv->isfloating()) | |
686 | { | |
687 | // This is necessray for: | |
688 | // 1. comparison of +0.0 and -0.0 should be true. | |
689 | goto Lneed; | |
690 | } | |
691 | if (tv->ty == Tarray) | |
692 | goto Lneed; | |
693 | if (tv->ty == Taarray) | |
694 | goto Lneed; | |
695 | if (tv->ty == Tclass) | |
696 | goto Lneed; | |
697 | } | |
698 | Ldontneed: | |
699 | //printf("\tdontneed\n"); | |
700 | return false; | |
701 | ||
702 | Lneed: | |
703 | //printf("\tneed\n"); | |
704 | return true; | |
705 | } | |
706 | ||
707 | /****************************************** | |
708 | * Build __xtoHash for non-bitwise hashing | |
709 | * static hash_t xtoHash(ref const S p) nothrow @trusted; | |
710 | */ | |
711 | FuncDeclaration *buildXtoHash(StructDeclaration *sd, Scope *sc) | |
712 | { | |
713 | if (Dsymbol *s = search_function(sd, Id::tohash)) | |
714 | { | |
715 | static TypeFunction *tftohash; | |
716 | if (!tftohash) | |
717 | { | |
c3a2ba10 | 718 | tftohash = new TypeFunction(ParameterList(), Type::thash_t, LINKd); |
b4c522fa IB |
719 | tftohash->mod = MODconst; |
720 | tftohash = (TypeFunction *)tftohash->merge(); | |
721 | } | |
722 | ||
723 | if (FuncDeclaration *fd = s->isFuncDeclaration()) | |
724 | { | |
725 | fd = fd->overloadExactMatch(tftohash); | |
726 | if (fd) | |
727 | return fd; | |
728 | } | |
729 | } | |
730 | ||
731 | if (!needToHash(sd)) | |
732 | return NULL; | |
733 | ||
734 | //printf("StructDeclaration::buildXtoHash() %s\n", sd->toPrettyChars()); | |
735 | Loc declLoc = Loc(); // loc is unnecessary so __xtoHash is never called directly | |
736 | Loc loc = Loc(); // internal code should have no loc to prevent coverage | |
737 | ||
738 | Parameters *parameters = new Parameters(); | |
dddea6d4 | 739 | parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL, NULL)); |
c3a2ba10 IB |
740 | TypeFunction *tf = new TypeFunction(ParameterList(parameters), Type::thash_t, |
741 | LINKd, STCnothrow | STCtrusted); | |
b4c522fa IB |
742 | |
743 | Identifier *id = Id::xtoHash; | |
744 | FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); | |
745 | fop->generated = true; | |
746 | ||
747 | /* Do memberwise hashing. | |
748 | * | |
749 | * If sd is a nested struct, and if it's nested in a class, the calculated | |
750 | * hash value will also contain the result of parent class's toHash(). | |
751 | */ | |
752 | const char *code = | |
753 | "size_t h = 0;" | |
754 | "foreach (i, T; typeof(p.tupleof))" | |
755 | " h += typeid(T).getHash(cast(const void*)&p.tupleof[i]);" | |
756 | "return h;"; | |
757 | fop->fbody = new CompileStatement(loc, new StringExp(loc, const_cast<char *>(code))); | |
758 | ||
759 | Scope *sc2 = sc->push(); | |
760 | sc2->stc = 0; | |
761 | sc2->linkage = LINKd; | |
762 | ||
a3b38b77 IB |
763 | dsymbolSemantic(fop, sc2); |
764 | semantic2(fop, sc2); | |
b4c522fa IB |
765 | |
766 | sc2->pop(); | |
767 | ||
768 | //printf("%s fop = %s %s\n", sd->toChars(), fop->toChars(), fop->type->toChars()); | |
769 | return fop; | |
770 | } | |
771 | ||
772 | /***************************************** | |
773 | * Create inclusive postblit for struct by aggregating | |
774 | * all the postblits in postblits[] with the postblits for | |
775 | * all the members. | |
776 | * Note the close similarity with AggregateDeclaration::buildDtor(), | |
777 | * and the ordering changes (runs forward instead of backwards). | |
778 | */ | |
779 | FuncDeclaration *buildPostBlit(StructDeclaration *sd, Scope *sc) | |
780 | { | |
781 | //printf("StructDeclaration::buildPostBlit() %s\n", sd->toChars()); | |
782 | if (sd->isUnionDeclaration()) | |
783 | return NULL; | |
784 | ||
785 | StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; | |
2cbc99d1 | 786 | Loc declLoc = sd->postblits.length ? sd->postblits[0]->loc : sd->loc; |
b4c522fa IB |
787 | Loc loc = Loc(); // internal code should have no loc to prevent coverage |
788 | ||
2cbc99d1 | 789 | for (size_t i = 0; i < sd->postblits.length; i++) |
b4c522fa IB |
790 | { |
791 | stc |= sd->postblits[i]->storage_class & STCdisable; | |
792 | } | |
793 | ||
794 | Statements *a = new Statements(); | |
2cbc99d1 | 795 | for (size_t i = 0; i < sd->fields.length && !(stc & STCdisable); i++) |
b4c522fa IB |
796 | { |
797 | VarDeclaration *v = sd->fields[i]; | |
798 | if (v->storage_class & STCref) | |
799 | continue; | |
800 | if (v->overlapped) | |
801 | continue; | |
802 | Type *tv = v->type->baseElemOf(); | |
803 | if (tv->ty != Tstruct) | |
804 | continue; | |
805 | StructDeclaration *sdv = ((TypeStruct *)tv)->sym; | |
806 | if (!sdv->postblit) | |
807 | continue; | |
808 | assert(!sdv->isUnionDeclaration()); | |
809 | sdv->postblit->functionSemantic(); | |
810 | ||
811 | stc = mergeFuncAttrs(stc, sdv->postblit); | |
812 | stc = mergeFuncAttrs(stc, sdv->dtor); | |
813 | if (stc & STCdisable) | |
814 | { | |
815 | a->setDim(0); | |
816 | break; | |
817 | } | |
818 | ||
819 | Expression *ex = NULL; | |
820 | tv = v->type->toBasetype(); | |
821 | if (tv->ty == Tstruct) | |
822 | { | |
823 | // this.v.__xpostblit() | |
824 | ||
825 | ex = new ThisExp(loc); | |
826 | ex = new DotVarExp(loc, ex, v); | |
827 | ||
828 | // This is a hack so we can call postblits on const/immutable objects. | |
829 | ex = new AddrExp(loc, ex); | |
830 | ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo()); | |
831 | ex = new PtrExp(loc, ex); | |
832 | if (stc & STCsafe) | |
833 | stc = (stc & ~STCsafe) | STCtrusted; | |
834 | ||
835 | ex = new DotVarExp(loc, ex, sdv->postblit, false); | |
836 | ex = new CallExp(loc, ex); | |
837 | } | |
838 | else | |
839 | { | |
c0aebc60 | 840 | // __ArrayPostblit((cast(S*)this.v.ptr)[0 .. n]) |
b4c522fa | 841 | |
b0a55e66 | 842 | uinteger_t n = tv->numberOfElems(loc); |
b4c522fa IB |
843 | if (n == 0) |
844 | continue; | |
845 | ||
846 | ex = new ThisExp(loc); | |
847 | ex = new DotVarExp(loc, ex, v); | |
848 | ||
849 | // This is a hack so we can call postblits on const/immutable objects. | |
850 | ex = new DotIdExp(loc, ex, Id::ptr); | |
851 | ex = new CastExp(loc, ex, sdv->type->pointerTo()); | |
852 | if (stc & STCsafe) | |
853 | stc = (stc & ~STCsafe) | STCtrusted; | |
854 | ||
855 | ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t), | |
856 | new IntegerExp(loc, n, Type::tsize_t)); | |
857 | // Prevent redundant bounds check | |
858 | ((SliceExp *)ex)->upperIsInBounds = true; | |
859 | ((SliceExp *)ex)->lowerIsLessThanUpper = true; | |
860 | ||
c0aebc60 | 861 | ex = new CallExp(loc, new IdentifierExp(loc, Id::__ArrayPostblit), ex); |
b4c522fa IB |
862 | } |
863 | a->push(new ExpStatement(loc, ex)); // combine in forward order | |
864 | ||
865 | /* Bugzilla 10972: When the following field postblit calls fail, | |
866 | * this field should be destructed for Exception Safety. | |
867 | */ | |
868 | if (!sdv->dtor) | |
869 | continue; | |
870 | sdv->dtor->functionSemantic(); | |
871 | ||
872 | tv = v->type->toBasetype(); | |
873 | if (v->type->toBasetype()->ty == Tstruct) | |
874 | { | |
875 | // this.v.__xdtor() | |
876 | ||
877 | ex = new ThisExp(loc); | |
878 | ex = new DotVarExp(loc, ex, v); | |
879 | ||
880 | // This is a hack so we can call destructors on const/immutable objects. | |
881 | ex = new AddrExp(loc, ex); | |
882 | ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo()); | |
883 | ex = new PtrExp(loc, ex); | |
884 | if (stc & STCsafe) | |
885 | stc = (stc & ~STCsafe) | STCtrusted; | |
886 | ||
887 | ex = new DotVarExp(loc, ex, sdv->dtor, false); | |
888 | ex = new CallExp(loc, ex); | |
889 | } | |
890 | else | |
891 | { | |
c0aebc60 | 892 | // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) |
b4c522fa | 893 | |
b0a55e66 | 894 | uinteger_t n = tv->numberOfElems(loc); |
b4c522fa IB |
895 | //if (n == 0) |
896 | // continue; | |
897 | ||
898 | ex = new ThisExp(loc); | |
899 | ex = new DotVarExp(loc, ex, v); | |
900 | ||
901 | // This is a hack so we can call destructors on const/immutable objects. | |
902 | ex = new DotIdExp(loc, ex, Id::ptr); | |
903 | ex = new CastExp(loc, ex, sdv->type->pointerTo()); | |
904 | if (stc & STCsafe) | |
905 | stc = (stc & ~STCsafe) | STCtrusted; | |
906 | ||
907 | ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t), | |
908 | new IntegerExp(loc, n, Type::tsize_t)); | |
909 | // Prevent redundant bounds check | |
910 | ((SliceExp *)ex)->upperIsInBounds = true; | |
911 | ((SliceExp *)ex)->lowerIsLessThanUpper = true; | |
912 | ||
c0aebc60 | 913 | ex = new CallExp(loc, new IdentifierExp(loc, Id::__ArrayDtor), ex); |
b4c522fa | 914 | } |
72acf751 | 915 | a->push(new ScopeGuardStatement(loc, TOKon_scope_failure, new ExpStatement(loc, ex))); |
b4c522fa IB |
916 | } |
917 | ||
918 | // Build our own "postblit" which executes a, but only if needed. | |
2cbc99d1 | 919 | if (a->length || (stc & STCdisable)) |
b4c522fa IB |
920 | { |
921 | //printf("Building __fieldPostBlit()\n"); | |
922 | PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__fieldPostblit); | |
923 | dd->generated = true; | |
924 | dd->storage_class |= STCinference; | |
925 | dd->fbody = (stc & STCdisable) ? NULL : new CompoundStatement(loc, a); | |
926 | sd->postblits.shift(dd); | |
927 | sd->members->push(dd); | |
a3b38b77 | 928 | dsymbolSemantic(dd, sc); |
b4c522fa IB |
929 | } |
930 | ||
931 | FuncDeclaration *xpostblit = NULL; | |
2cbc99d1 | 932 | switch (sd->postblits.length) |
b4c522fa IB |
933 | { |
934 | case 0: | |
935 | break; | |
936 | ||
937 | case 1: | |
938 | xpostblit = sd->postblits[0]; | |
939 | break; | |
940 | ||
941 | default: | |
942 | Expression *e = NULL; | |
943 | stc = STCsafe | STCnothrow | STCpure | STCnogc; | |
2cbc99d1 | 944 | for (size_t i = 0; i < sd->postblits.length; i++) |
b4c522fa IB |
945 | { |
946 | FuncDeclaration *fd = sd->postblits[i]; | |
947 | stc = mergeFuncAttrs(stc, fd); | |
948 | if (stc & STCdisable) | |
949 | { | |
950 | e = NULL; | |
951 | break; | |
952 | } | |
953 | Expression *ex = new ThisExp(loc); | |
954 | ex = new DotVarExp(loc, ex, fd, false); | |
955 | ex = new CallExp(loc, ex); | |
956 | e = Expression::combine(e, ex); | |
957 | } | |
958 | PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__aggrPostblit); | |
959 | dd->storage_class |= STCinference; | |
960 | dd->fbody = new ExpStatement(loc, e); | |
961 | sd->members->push(dd); | |
a3b38b77 | 962 | dsymbolSemantic(dd, sc); |
b4c522fa IB |
963 | xpostblit = dd; |
964 | break; | |
965 | } | |
966 | // Add an __xpostblit alias to make the inclusive postblit accessible | |
967 | if (xpostblit) | |
968 | { | |
969 | AliasDeclaration *alias = new AliasDeclaration(Loc(), Id::__xpostblit, xpostblit); | |
a3b38b77 | 970 | dsymbolSemantic(alias, sc); |
b4c522fa IB |
971 | sd->members->push(alias); |
972 | alias->addMember(sc, sd); // add to symbol table | |
973 | } | |
974 | return xpostblit; | |
975 | } | |
976 | ||
977 | /***************************************** | |
978 | * Create inclusive destructor for struct/class by aggregating | |
979 | * all the destructors in dtors[] with the destructors for | |
980 | * all the members. | |
981 | * Note the close similarity with StructDeclaration::buildPostBlit(), | |
982 | * and the ordering changes (runs backward instead of forwards). | |
983 | */ | |
984 | FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc) | |
985 | { | |
986 | //printf("AggregateDeclaration::buildDtor() %s\n", ad->toChars()); | |
987 | if (ad->isUnionDeclaration()) | |
988 | return NULL; | |
989 | ||
990 | StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; | |
2cbc99d1 | 991 | Loc declLoc = ad->dtors.length ? ad->dtors[0]->loc : ad->loc; |
b4c522fa IB |
992 | Loc loc = Loc(); // internal code should have no loc to prevent coverage |
993 | ||
994 | Expression *e = NULL; | |
2cbc99d1 | 995 | for (size_t i = 0; i < ad->fields.length; i++) |
b4c522fa IB |
996 | { |
997 | VarDeclaration *v = ad->fields[i]; | |
998 | if (v->storage_class & STCref) | |
999 | continue; | |
1000 | if (v->overlapped) | |
1001 | continue; | |
1002 | Type *tv = v->type->baseElemOf(); | |
1003 | if (tv->ty != Tstruct) | |
1004 | continue; | |
1005 | StructDeclaration *sdv = ((TypeStruct *)tv)->sym; | |
1006 | if (!sdv->dtor) | |
1007 | continue; | |
1008 | sdv->dtor->functionSemantic(); | |
1009 | ||
1010 | stc = mergeFuncAttrs(stc, sdv->dtor); | |
1011 | if (stc & STCdisable) | |
1012 | { | |
1013 | e = NULL; | |
1014 | break; | |
1015 | } | |
1016 | ||
1017 | Expression *ex = NULL; | |
1018 | tv = v->type->toBasetype(); | |
1019 | if (tv->ty == Tstruct) | |
1020 | { | |
1021 | // this.v.__xdtor() | |
1022 | ||
1023 | ex = new ThisExp(loc); | |
1024 | ex = new DotVarExp(loc, ex, v); | |
1025 | ||
1026 | // This is a hack so we can call destructors on const/immutable objects. | |
1027 | ex = new AddrExp(loc, ex); | |
1028 | ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo()); | |
1029 | ex = new PtrExp(loc, ex); | |
1030 | if (stc & STCsafe) | |
1031 | stc = (stc & ~STCsafe) | STCtrusted; | |
1032 | ||
1033 | ex = new DotVarExp(loc, ex, sdv->dtor, false); | |
1034 | ex = new CallExp(loc, ex); | |
1035 | } | |
1036 | else | |
1037 | { | |
c0aebc60 | 1038 | // __ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) |
b4c522fa | 1039 | |
b0a55e66 | 1040 | uinteger_t n = tv->numberOfElems(loc); |
b4c522fa IB |
1041 | if (n == 0) |
1042 | continue; | |
1043 | ||
1044 | ex = new ThisExp(loc); | |
1045 | ex = new DotVarExp(loc, ex, v); | |
1046 | ||
1047 | // This is a hack so we can call destructors on const/immutable objects. | |
1048 | ex = new DotIdExp(loc, ex, Id::ptr); | |
1049 | ex = new CastExp(loc, ex, sdv->type->pointerTo()); | |
1050 | if (stc & STCsafe) | |
1051 | stc = (stc & ~STCsafe) | STCtrusted; | |
1052 | ||
1053 | ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t), | |
1054 | new IntegerExp(loc, n, Type::tsize_t)); | |
1055 | // Prevent redundant bounds check | |
1056 | ((SliceExp *)ex)->upperIsInBounds = true; | |
1057 | ((SliceExp *)ex)->lowerIsLessThanUpper = true; | |
1058 | ||
c0aebc60 | 1059 | ex = new CallExp(loc, new IdentifierExp(loc, Id::__ArrayDtor), ex); |
b4c522fa IB |
1060 | } |
1061 | e = Expression::combine(ex, e); // combine in reverse order | |
1062 | } | |
1063 | ||
1064 | /* Build our own "destructor" which executes e | |
1065 | */ | |
1066 | if (e || (stc & STCdisable)) | |
1067 | { | |
1068 | //printf("Building __fieldDtor()\n"); | |
1069 | DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Id::__fieldDtor); | |
1070 | dd->generated = true; | |
1071 | dd->storage_class |= STCinference; | |
1072 | dd->fbody = new ExpStatement(loc, e); | |
1073 | ad->dtors.shift(dd); | |
1074 | ad->members->push(dd); | |
a3b38b77 | 1075 | dsymbolSemantic(dd, sc); |
b4c522fa IB |
1076 | } |
1077 | ||
1078 | FuncDeclaration *xdtor = NULL; | |
2cbc99d1 | 1079 | switch (ad->dtors.length) |
b4c522fa IB |
1080 | { |
1081 | case 0: | |
1082 | break; | |
1083 | ||
1084 | case 1: | |
1085 | xdtor = ad->dtors[0]; | |
1086 | break; | |
1087 | ||
1088 | default: | |
1089 | e = NULL; | |
1090 | stc = STCsafe | STCnothrow | STCpure | STCnogc; | |
2cbc99d1 | 1091 | for (size_t i = 0; i < ad->dtors.length; i++) |
b4c522fa IB |
1092 | { |
1093 | FuncDeclaration *fd = ad->dtors[i]; | |
1094 | stc = mergeFuncAttrs(stc, fd); | |
1095 | if (stc & STCdisable) | |
1096 | { | |
1097 | e = NULL; | |
1098 | break; | |
1099 | } | |
1100 | Expression *ex = new ThisExp(loc); | |
1101 | ex = new DotVarExp(loc, ex, fd, false); | |
1102 | ex = new CallExp(loc, ex); | |
1103 | e = Expression::combine(ex, e); | |
1104 | } | |
1105 | DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Id::__aggrDtor); | |
1106 | dd->generated = true; | |
1107 | dd->storage_class |= STCinference; | |
1108 | dd->fbody = new ExpStatement(loc, e); | |
1109 | ad->members->push(dd); | |
a3b38b77 | 1110 | dsymbolSemantic(dd, sc); |
b4c522fa IB |
1111 | xdtor = dd; |
1112 | break; | |
1113 | } | |
1114 | // Add an __xdtor alias to make the inclusive dtor accessible | |
1115 | if (xdtor) | |
1116 | { | |
1117 | AliasDeclaration *alias = new AliasDeclaration(Loc(), Id::__xdtor, xdtor); | |
a3b38b77 | 1118 | dsymbolSemantic(alias, sc); |
b4c522fa IB |
1119 | ad->members->push(alias); |
1120 | alias->addMember(sc, ad); // add to symbol table | |
1121 | } | |
1122 | return xdtor; | |
1123 | } | |
1124 | ||
1125 | /****************************************** | |
1126 | * Create inclusive invariant for struct/class by aggregating | |
1127 | * all the invariants in invs[]. | |
1128 | * void __invariant() const [pure nothrow @trusted] | |
1129 | * { | |
1130 | * invs[0](), invs[1](), ...; | |
1131 | * } | |
1132 | */ | |
1133 | FuncDeclaration *buildInv(AggregateDeclaration *ad, Scope *sc) | |
1134 | { | |
1135 | StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; | |
1136 | Loc declLoc = ad->loc; | |
1137 | Loc loc = Loc(); // internal code should have no loc to prevent coverage | |
1138 | ||
2cbc99d1 | 1139 | switch (ad->invs.length) |
b4c522fa IB |
1140 | { |
1141 | case 0: | |
1142 | return NULL; | |
1143 | ||
1144 | case 1: | |
1145 | // Don't return invs[0] so it has uniquely generated name. | |
1146 | /* fall through */ | |
1147 | ||
1148 | default: | |
1149 | Expression *e = NULL; | |
1150 | StorageClass stcx = 0; | |
2cbc99d1 | 1151 | for (size_t i = 0; i < ad->invs.length; i++) |
b4c522fa IB |
1152 | { |
1153 | stc = mergeFuncAttrs(stc, ad->invs[i]); | |
1154 | if (stc & STCdisable) | |
1155 | { | |
1156 | // What should do? | |
1157 | } | |
1158 | StorageClass stcy = (ad->invs[i]->storage_class & STCsynchronized) | | |
1159 | (ad->invs[i]->type->mod & MODshared ? STCshared : 0); | |
1160 | if (i == 0) | |
1161 | stcx = stcy; | |
1162 | else if (stcx ^ stcy) | |
1163 | { | |
1164 | #if 1 // currently rejects | |
1165 | ad->error(ad->invs[i]->loc, "mixing invariants with shared/synchronized differene is not supported"); | |
1166 | e = NULL; | |
1167 | break; | |
1168 | #endif | |
1169 | } | |
1170 | e = Expression::combine(e, new CallExp(loc, new VarExp(loc, ad->invs[i], false))); | |
1171 | } | |
1172 | InvariantDeclaration *inv; | |
1173 | inv = new InvariantDeclaration(declLoc, Loc(), stc | stcx, Id::classInvariant); | |
1174 | inv->fbody = new ExpStatement(loc, e); | |
1175 | ad->members->push(inv); | |
a3b38b77 | 1176 | dsymbolSemantic(inv, sc); |
b4c522fa IB |
1177 | return inv; |
1178 | } | |
1179 | } |