view dmd2/clone.c @ 1117:4c20fcc4252b

Fun with parameter attributes: For several of the "synthetic" parameters added to D functions, we can apply noalias and nocapture. They are sret parameters, 'nest' pointers passed to nested functions, and _argptr: Nocapture: - Sret and nest are nocapture because they don't represent D-level variables, and thus the callee can't (validly) obtain a pointer to them, let alone keep it around after it returns. - _argptr is nocapture because although the callee has access to it as a pointer, that pointer is invalidated when it returns. All three are noalias because they're function-local variables - Sret and _argptr are noalias because they're freshly alloca'd memory only used for a single function call that's not allowed to keep an aliasing pointer to it around (since the parameter is nocapture). - 'Nest' is noalias because the callee only ever has access to one such pointer per parent function, and every parent function has a different one. This commit also ensures attributes set on sret, _arguments and _argptr are propagated to calls to such functions. It also adds one exception to the general rule that attributes on function types should propagate to calls: the type of a delegate's function pointer has a 'nest' parameter, but this can either be a true 'nest' (for delegates to nested functions) or a 'this' (for delegates to member functions). Since 'this' is neither noalias nor nocapture, and there's generally no way to tell which one it is, we remove these attributes at the call site if the callee is a delegate.
author Frits van Bommel <fvbommel wxs.nl>
date Sat, 14 Mar 2009 22:15:31 +0100
parents 356e65836fb5
children 638d16625da2
line wrap: on
line source


// Compiler implementation of the D programming language
// Copyright (c) 1999-2008 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.

#include <stdio.h>
#include <assert.h>

#include "root.h"
#include "aggregate.h"
#include "scope.h"
#include "mtype.h"
#include "declaration.h"
#include "module.h"
#include "id.h"
#include "expression.h"
#include "statement.h"
#include "init.h"


/*******************************************
 * We need an opAssign for the struct if
 * it has a destructor or a postblit.
 * We need to generate one if a user-specified one does not exist.
 */

int StructDeclaration::needOpAssign()
{
#define X 0
    if (X) printf("StructDeclaration::needOpAssign() %s\n", toChars());
    if (hasIdentityAssign)
	goto Ldontneed;

    if (dtor || postblit)
	goto Lneed;

    /* If any of the fields need an opAssign, then we
     * need it too.
     */
    for (size_t i = 0; i < fields.dim; i++)
    {
	Dsymbol *s = (Dsymbol *)fields.data[i];
	VarDeclaration *v = s->isVarDeclaration();
	assert(v && v->storage_class & STCfield);
	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 (sd->needOpAssign())
		goto Lneed;
	}
    }
Ldontneed:
    if (X) printf("\tdontneed\n");
    return 0;

Lneed:
    if (X) printf("\tneed\n");
    return 1;
#undef X
}

/******************************************
 * Build opAssign for struct.
 *	S* opAssign(S s) { ... }
 */

FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc)
{
    if (!needOpAssign())
	return NULL;

    //printf("StructDeclaration::buildOpAssign() %s\n", toChars());

    FuncDeclaration *fop = NULL;

    Argument *param = new Argument(STCnodtor, type, Id::p, NULL);
    Arguments *fparams = new Arguments;
    fparams->push(param);
    Type *ftype = new TypeFunction(fparams, handle, FALSE, LINKd);
#if STRUCTTHISREF
    ((TypeFunction *)ftype)->isref = 1;
#endif

    fop = new FuncDeclaration(0, 0, Id::assign, STCundefined, ftype);

    Expression *e = NULL;
    if (postblit)
    {	/* Swap:
	 *    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(0, type, idtmp, new VoidInitializer(0));
	    tmp->noauto = 1;
	    e = new DeclarationExp(0, tmp);
	    ec = new AssignExp(0,
		new VarExp(0, tmp),
#if STRUCTTHISREF
		new ThisExp(0)
#else
		new PtrExp(0, new ThisExp(0))
#endif
		);
	    ec->op = TOKblit;
	    e = Expression::combine(e, ec);
	}
	ec = new AssignExp(0,
#if STRUCTTHISREF
		new ThisExp(0),
#else
		new PtrExp(0, new ThisExp(0)),
#endif
		new IdentifierExp(0, 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 *ec = new DotVarExp(0, new VarExp(0, tmp), dtor, 0);
	    ec = new CallExp(0, ec);
	    e = Expression::combine(e, ec);
	}
    }
    else
    {	/* Do memberwise copy
	 */
	//printf("\tmemberwise copy\n");
	for (size_t i = 0; i < fields.dim; i++)
	{
	    Dsymbol *s = (Dsymbol *)fields.data[i];
	    VarDeclaration *v = s->isVarDeclaration();
	    assert(v && v->storage_class & STCfield);
	    // this.v = s.v;
	    AssignExp *ec = new AssignExp(0,
		new DotVarExp(0, new ThisExp(0), v, 0),
		new DotVarExp(0, new IdentifierExp(0, Id::p), v, 0));
	    ec->op = TOKblit;
	    e = Expression::combine(e, ec);
	}
    }
    Statement *s1 = new ExpStatement(0, e);

    /* Add:
     *   return this;
     */
    e = new ThisExp(0);
    Statement *s2 = new ReturnStatement(0, e);

    fop->fbody = new CompoundStatement(0, s1, s2);

    members->push(fop);
    fop->addMember(sc, this, 1);

    sc = sc->push();
    sc->stc = 0;
    sc->linkage = LINKd;

    fop->semantic(sc);

    sc->pop();

    //printf("-StructDeclaration::buildOpAssign() %s\n", toChars());

    return fop;
}

/*******************************************
 * Build copy constructor for struct.
 * Copy constructors are compiler generated only, and are only
 * callable from the compiler. They are not user accessible.
 * A copy constructor is:
 *    void cpctpr(ref S s)
 *    {
 *	*this = s;
 *	this.postBlit();
 *    }
 * This is done so:
 *	- postBlit() never sees uninitialized data
 *	- memcpy can be much more efficient than memberwise copy
 *	- no fields are overlooked
 */

FuncDeclaration *StructDeclaration::buildCpCtor(Scope *sc)
{
    //printf("StructDeclaration::buildCpCtor() %s\n", toChars());
    FuncDeclaration *fcp = NULL;

    /* Copy constructor is only necessary if there is a postblit function,
     * otherwise the code generator will just do a bit copy.
     */
    if (postblit)
    {
	//printf("generating cpctor\n");

	Argument *param = new Argument(STCref, type, Id::p, NULL);
	Arguments *fparams = new Arguments;
	fparams->push(param);
	Type *ftype = new TypeFunction(fparams, Type::tvoid, FALSE, LINKd);

	fcp = new FuncDeclaration(0, 0, Id::cpctor, STCundefined, ftype);

	// Build *this = p;
	Expression *e = new ThisExp(0);
#if !STRUCTTHISREF
	e = new PtrExp(0, e);
#endif
	AssignExp *ea = new AssignExp(0, e, new IdentifierExp(0, Id::p));
	ea->op = TOKblit;
	Statement *s = new ExpStatement(0, ea);

	// Build postBlit();
	e = new VarExp(0, postblit, 0);
	e = new CallExp(0, e);

	s = new CompoundStatement(0, s, new ExpStatement(0, e));
	fcp->fbody = s;

	members->push(fcp);

	sc = sc->push();
	sc->stc = 0;
	sc->linkage = LINKd;

	fcp->semantic(sc);

	sc->pop();
    }

    return fcp;
}

/*****************************************
 * Create inclusive postblit for struct by aggregating
 * all the postblits in postblits[] with the postblits for
 * all the members.
 * Note the close similarity with AggregateDeclaration::buildDtor(),
 * and the ordering changes (runs forward instead of backwards).
 */

#if DMDV2
FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc)
{
    //printf("StructDeclaration::buildPostBlit() %s\n", toChars());
    Expression *e = NULL;

    for (size_t i = 0; i < fields.dim; i++)
    {
	Dsymbol *s = (Dsymbol *)fields.data[i];
	VarDeclaration *v = s->isVarDeclaration();
	assert(v && v->storage_class & STCfield);
	Type *tv = v->type->toBasetype();
	size_t dim = 1;
	while (tv->ty == Tsarray)
	{   TypeSArray *ta = (TypeSArray *)tv;
	    dim *= ((TypeSArray *)tv)->dim->toInteger();
	    tv = tv->nextOf()->toBasetype();
	}
	if (tv->ty == Tstruct)
	{   TypeStruct *ts = (TypeStruct *)tv;
	    StructDeclaration *sd = ts->sym;
	    if (sd->postblit)
	    {	Expression *ex;

		// this.v
		ex = new ThisExp(0);
		ex = new DotVarExp(0, ex, v, 0);

		if (dim == 1)
		{   // this.v.dtor()
		    ex = new DotVarExp(0, ex, sd->postblit, 0);
		    ex = new CallExp(0, ex);
		}
		else
		{
		    // Typeinfo.postblit(cast(void*)&this.v);
		    Expression *ea = new AddrExp(0, ex);
		    ea = new CastExp(0, ea, Type::tvoid->pointerTo());

		    Expression *et = v->type->getTypeInfo(sc);
		    et = new DotIdExp(0, et, Id::postblit);

		    ex = new CallExp(0, et, ea);
		}
		e = Expression::combine(e, ex);	// combine in forward order
	    }
	}
    }

    /* Build our own "postblit" which executes e
     */
    if (e)
    {	//printf("Building __fieldPostBlit()\n");
	PostBlitDeclaration *dd = new PostBlitDeclaration(0, 0, Lexer::idPool("__fieldPostBlit"));
	dd->fbody = new ExpStatement(0, e);
	dtors.push(dd);
	members->push(dd);
	dd->semantic(sc);
    }

    switch (postblits.dim)
    {
	case 0:
	    return NULL;

	case 1:
	    return (FuncDeclaration *)postblits.data[0];

	default:
	    e = NULL;
	    for (size_t i = 0; i < postblits.dim; i++)
	    {	FuncDeclaration *fd = (FuncDeclaration *)postblits.data[i];
		Expression *ex = new ThisExp(0);
		ex = new DotVarExp(0, ex, fd, 0);
		ex = new CallExp(0, ex);
		e = Expression::combine(e, ex);
	    }
	    PostBlitDeclaration *dd = new PostBlitDeclaration(0, 0, Lexer::idPool("__aggrPostBlit"));
	    dd->fbody = new ExpStatement(0, e);
	    members->push(dd);
	    dd->semantic(sc);
	    return dd;
    }
}

#endif

/*****************************************
 * Create inclusive destructor for struct/class by aggregating
 * all the destructors in dtors[] with the destructors for
 * all the members.
 * Note the close similarity with StructDeclaration::buildPostBlit(),
 * and the ordering changes (runs backward instead of forwards).
 */

FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc)
{
    //printf("AggregateDeclaration::buildDtor() %s\n", toChars());
    Expression *e = NULL;

#if DMDV2
    for (size_t i = 0; i < fields.dim; i++)
    {
	Dsymbol *s = (Dsymbol *)fields.data[i];
	VarDeclaration *v = s->isVarDeclaration();
	assert(v && v->storage_class & STCfield);
	Type *tv = v->type->toBasetype();
	size_t dim = 1;
	while (tv->ty == Tsarray)
	{   TypeSArray *ta = (TypeSArray *)tv;
	    dim *= ((TypeSArray *)tv)->dim->toInteger();
	    tv = tv->nextOf()->toBasetype();
	}
	if (tv->ty == Tstruct)
	{   TypeStruct *ts = (TypeStruct *)tv;
	    StructDeclaration *sd = ts->sym;
	    if (sd->dtor)
	    {	Expression *ex;

		// this.v
		ex = new ThisExp(0);
		ex = new DotVarExp(0, ex, v, 0);

		if (dim == 1)
		{   // this.v.dtor()
		    ex = new DotVarExp(0, ex, sd->dtor, 0);
		    ex = new CallExp(0, ex);
		}
		else
		{
		    // Typeinfo.destroy(cast(void*)&this.v);
		    Expression *ea = new AddrExp(0, ex);
		    ea = new CastExp(0, ea, Type::tvoid->pointerTo());

		    Expression *et = v->type->getTypeInfo(sc);
		    et = new DotIdExp(0, et, Id::destroy);

		    ex = new CallExp(0, et, ea);
		}
		e = Expression::combine(ex, e);	// combine in reverse order
	    }
	}
    }

    /* Build our own "destructor" which executes e
     */
    if (e)
    {	//printf("Building __fieldDtor()\n");
	DtorDeclaration *dd = new DtorDeclaration(0, 0, Lexer::idPool("__fieldDtor"));
	dd->fbody = new ExpStatement(0, e);
	dtors.shift(dd);
	members->push(dd);
	dd->semantic(sc);
    }
#endif

    switch (dtors.dim)
    {
	case 0:
	    return NULL;

	case 1:
	    return (FuncDeclaration *)dtors.data[0];

	default:
	    e = NULL;
	    for (size_t i = 0; i < dtors.dim; i++)
	    {	FuncDeclaration *fd = (FuncDeclaration *)dtors.data[i];
		Expression *ex = new ThisExp(0);
		ex = new DotVarExp(0, ex, fd, 0);
		ex = new CallExp(0, ex);
		e = Expression::combine(ex, e);
	    }
	    DtorDeclaration *dd = new DtorDeclaration(0, 0, Lexer::idPool("__aggrDtor"));
	    dd->fbody = new ExpStatement(0, e);
	    members->push(dd);
	    dd->semantic(sc);
	    return dd;
    }
}