"Fossies" - the Fresh Open Source Software Archive

Member "dmd2/src/dmd/dmd/clone.d" (20 Nov 2020, 40454 Bytes) of package /linux/misc/dmd.2.094.2.linux.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) D source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

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