view dmd2/opover.c @ 1083:c1e9f612e2e2

Fix for dual operand form of fistp, also make reg ST(0) explicit and fix lindquists previous code that allowed dual operand form of fstp but dissallowed the single operand form accidently
author Kelly Wilson <wilsonk cpsc.ucalgary.ca>
date Tue, 10 Mar 2009 06:23:26 -0600
parents 356e65836fb5
children 638d16625da2
line wrap: on
line source


// Compiler implementation of the D programming language
// Copyright (c) 1999-2007 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 <stdlib.h>
#include <ctype.h>
#include <assert.h>
#if _MSC_VER
#include <complex>
#else
#endif

#ifdef __APPLE__
#define integer_t dmd_integer_t
#endif

#if IN_GCC || IN_LLVM
#include "mem.h"
#elif POSIX
#include "../root/mem.h"
#elif _WIN32
#include "..\root\mem.h"
#endif

//#include "port.h"
#include "mtype.h"
#include "init.h"
#include "expression.h"
#include "id.h"
#include "declaration.h"
#include "aggregate.h"
#include "template.h"

static void inferApplyArgTypesX(FuncDeclaration *fstart, Arguments *arguments);
static void inferApplyArgTypesZ(TemplateDeclaration *tstart, Arguments *arguments);
static int inferApplyArgTypesY(TypeFunction *tf, Arguments *arguments);
static void templateResolve(Match *m, TemplateDeclaration *td, Scope *sc, Loc loc, Objects *targsi, Expression *ethis, Expressions *arguments);

/******************************** Expression **************************/


/***********************************
 * Determine if operands of binary op can be reversed
 * to fit operator overload.
 */

int Expression::isCommutative()
{
    return FALSE;	// default is no reverse
}

/***********************************
 * Get Identifier for operator overload.
 */

Identifier *Expression::opId()
{
    assert(0);
    return NULL;
}

/***********************************
 * Get Identifier for reverse operator overload,
 * NULL if not supported for this operator.
 */

Identifier *Expression::opId_r()
{
    return NULL;
}

/************************* Operators *****************************/

Identifier *UAddExp::opId()   { return Id::uadd; }

Identifier *NegExp::opId()   { return Id::neg; }

Identifier *ComExp::opId()   { return Id::com; }

Identifier *CastExp::opId()   { return Id::cast; }

Identifier *InExp::opId()     { return Id::opIn; }
Identifier *InExp::opId_r()     { return Id::opIn_r; }

Identifier *PostExp::opId() { return (op == TOKplusplus)
				? Id::postinc
				: Id::postdec; }

int AddExp::isCommutative()  { return TRUE; }
Identifier *AddExp::opId()   { return Id::add; }
Identifier *AddExp::opId_r() { return Id::add_r; }

Identifier *MinExp::opId()   { return Id::sub; }
Identifier *MinExp::opId_r() { return Id::sub_r; }

int MulExp::isCommutative()  { return TRUE; }
Identifier *MulExp::opId()   { return Id::mul; }
Identifier *MulExp::opId_r() { return Id::mul_r; }

Identifier *DivExp::opId()   { return Id::div; }
Identifier *DivExp::opId_r() { return Id::div_r; }

Identifier *ModExp::opId()   { return Id::mod; }
Identifier *ModExp::opId_r() { return Id::mod_r; }

Identifier *ShlExp::opId()   { return Id::shl; }
Identifier *ShlExp::opId_r() { return Id::shl_r; }

Identifier *ShrExp::opId()   { return Id::shr; }
Identifier *ShrExp::opId_r() { return Id::shr_r; }

Identifier *UshrExp::opId()   { return Id::ushr; }
Identifier *UshrExp::opId_r() { return Id::ushr_r; }

int AndExp::isCommutative()  { return TRUE; }
Identifier *AndExp::opId()   { return Id::iand; }
Identifier *AndExp::opId_r() { return Id::iand_r; }

int OrExp::isCommutative()  { return TRUE; }
Identifier *OrExp::opId()   { return Id::ior; }
Identifier *OrExp::opId_r() { return Id::ior_r; }

int XorExp::isCommutative()  { return TRUE; }
Identifier *XorExp::opId()   { return Id::ixor; }
Identifier *XorExp::opId_r() { return Id::ixor_r; }

Identifier *CatExp::opId()   { return Id::cat; }
Identifier *CatExp::opId_r() { return Id::cat_r; }

Identifier *    AssignExp::opId()  { return Id::assign;  }
Identifier * AddAssignExp::opId()  { return Id::addass;  }
Identifier * MinAssignExp::opId()  { return Id::subass;  }
Identifier * MulAssignExp::opId()  { return Id::mulass;  }
Identifier * DivAssignExp::opId()  { return Id::divass;  }
Identifier * ModAssignExp::opId()  { return Id::modass;  }
Identifier * AndAssignExp::opId()  { return Id::andass;  }
Identifier *  OrAssignExp::opId()  { return Id::orass;   }
Identifier * XorAssignExp::opId()  { return Id::xorass;  }
Identifier * ShlAssignExp::opId()  { return Id::shlass;  }
Identifier * ShrAssignExp::opId()  { return Id::shrass;  }
Identifier *UshrAssignExp::opId()  { return Id::ushrass; }
Identifier * CatAssignExp::opId()  { return Id::catass;  }

int EqualExp::isCommutative()  { return TRUE; }
Identifier *EqualExp::opId()   { return Id::eq; }

int CmpExp::isCommutative()  { return TRUE; }
Identifier *CmpExp::opId()   { return Id::cmp; }

Identifier *ArrayExp::opId()	{ return Id::index; }
Identifier *PtrExp::opId()	{ return Id::opStar; }

/************************************
 * Operator overload.
 * Check for operator overload, if so, replace
 * with function call.
 * Return NULL if not an operator overload.
 */

Expression *UnaExp::op_overload(Scope *sc)
{
    AggregateDeclaration *ad;
    Dsymbol *fd;
    Type *t1 = e1->type->toBasetype();

    if (t1->ty == Tclass)
    {
	ad = ((TypeClass *)t1)->sym;
	goto L1;
    }
    else if (t1->ty == Tstruct)
    {
	ad = ((TypeStruct *)t1)->sym;

    L1:
	fd = search_function(ad, opId());
	if (fd)
	{
	    if (op == TOKarray)
	    {
		Expression *e;
		ArrayExp *ae = (ArrayExp *)this;

		e = new DotIdExp(loc, e1, fd->ident);
		e = new CallExp(loc, e, ae->arguments);
		e = e->semantic(sc);
		return e;
	    }
	    else
	    {
		// Rewrite +e1 as e1.add()
		return build_overload(loc, sc, e1, NULL, fd->ident);
	    }
	}
    }
    return NULL;
}


Expression *BinExp::op_overload(Scope *sc)
{
    //printf("BinExp::op_overload() (%s)\n", toChars());

    AggregateDeclaration *ad;
    Type *t1 = e1->type->toBasetype();
    Type *t2 = e2->type->toBasetype();
    Identifier *id = opId();
    Identifier *id_r = opId_r();

    Match m;
    Expressions args1;
    Expressions args2;
    int argsset = 0;

    AggregateDeclaration *ad1;
    if (t1->ty == Tclass)
	ad1 = ((TypeClass *)t1)->sym;
    else if (t1->ty == Tstruct)
	ad1 = ((TypeStruct *)t1)->sym;
    else
	ad1 = NULL;

    AggregateDeclaration *ad2;
    if (t2->ty == Tclass)
	ad2 = ((TypeClass *)t2)->sym;
    else if (t2->ty == Tstruct)
	ad2 = ((TypeStruct *)t2)->sym;
    else
	ad2 = NULL;

    Dsymbol *s = NULL;
    Dsymbol *s_r = NULL;
    FuncDeclaration *fd = NULL;
    TemplateDeclaration *td = NULL;
    if (ad1 && id)
    {
	s = search_function(ad1, id);
    }
    if (ad2 && id_r)
    {
	s_r = search_function(ad2, id_r);
    }

    if (s || s_r)
    {
	/* Try:
	 *	a.opfunc(b)
	 *	b.opfunc_r(a)
	 * and see which is better.
	 */
	Expression *e;
	FuncDeclaration *lastf;

	args1.setDim(1);
	args1.data[0] = (void*) e1;
	args2.setDim(1);
	args2.data[0] = (void*) e2;
	argsset = 1;

	memset(&m, 0, sizeof(m));
	m.last = MATCHnomatch;

	if (s)
	{
	    fd = s->isFuncDeclaration();
	    if (fd)
	    {
		overloadResolveX(&m, fd, NULL, &args2);
	    }
	    else
	    {   td = s->isTemplateDeclaration();
		templateResolve(&m, td, sc, loc, NULL, NULL, &args2);
	    }
	}

	lastf = m.lastf;

	if (s_r)
	{
	    fd = s_r->isFuncDeclaration();
	    if (fd)
	    {
		overloadResolveX(&m, fd, NULL, &args1);
	    }
	    else
	    {   td = s_r->isTemplateDeclaration();
		templateResolve(&m, td, sc, loc, NULL, NULL, &args1);
	    }
	}

	if (m.count > 1)
	{
	    // Error, ambiguous
	    error("overloads %s and %s both match argument list for %s",
		    m.lastf->type->toChars(),
		    m.nextf->type->toChars(),
		    m.lastf->toChars());
	}
	else if (m.last == MATCHnomatch)
	{
	    m.lastf = m.anyf;
	}

	if (op == TOKplusplus || op == TOKminusminus)
	    // Kludge because operator overloading regards e++ and e--
	    // as unary, but it's implemented as a binary.
	    // Rewrite (e1 ++ e2) as e1.postinc()
	    // Rewrite (e1 -- e2) as e1.postdec()
	    e = build_overload(loc, sc, e1, NULL, id);
	else if (lastf && m.lastf == lastf || m.last == MATCHnomatch)
	    // Rewrite (e1 op e2) as e1.opfunc(e2)
	    e = build_overload(loc, sc, e1, e2, id);
	else
	    // Rewrite (e1 op e2) as e2.opfunc_r(e1)
	    e = build_overload(loc, sc, e2, e1, id_r);
	return e;
    }

    if (isCommutative())
    {
	s = NULL;
	s_r = NULL;
	if (ad1 && id_r)
	{
	    s_r = search_function(ad1, id_r);
	}
	if (ad2 && id)
	{
	    s = search_function(ad2, id);
	}

	if (s || s_r)
	{
	    /* Try:
	     *	a.opfunc_r(b)
	     *	b.opfunc(a)
	     * and see which is better.
	     */
	    Expression *e;
	    FuncDeclaration *lastf;

	    if (!argsset)
	    {	args1.setDim(1);
		args1.data[0] = (void*) e1;
		args2.setDim(1);
		args2.data[0] = (void*) e2;
	    }

	    memset(&m, 0, sizeof(m));
	    m.last = MATCHnomatch;

	    if (s_r)
	    {
		fd = s_r->isFuncDeclaration();
		if (fd)
		{
		    overloadResolveX(&m, fd, NULL, &args2);
		}
		else
		{   td = s_r->isTemplateDeclaration();
		    templateResolve(&m, td, sc, loc, NULL, NULL, &args2);
		}
	    }
	    lastf = m.lastf;

	    if (s)
	    {
		fd = s->isFuncDeclaration();
		if (fd)
		{
		    overloadResolveX(&m, fd, NULL, &args1);
		}
		else
		{   td = s->isTemplateDeclaration();
		    templateResolve(&m, td, sc, loc, NULL, NULL, &args1);
		}
	    }

	    if (m.count > 1)
	    {
		// Error, ambiguous
		error("overloads %s and %s both match argument list for %s",
			m.lastf->type->toChars(),
			m.nextf->type->toChars(),
			m.lastf->toChars());
	    }
	    else if (m.last == MATCHnomatch)
	    {
		m.lastf = m.anyf;
	    }

	    if (lastf && m.lastf == lastf ||
		id_r && m.last == MATCHnomatch)
		// Rewrite (e1 op e2) as e1.opfunc_r(e2)
		e = build_overload(loc, sc, e1, e2, id_r);
	    else
		// Rewrite (e1 op e2) as e2.opfunc(e1)
		e = build_overload(loc, sc, e2, e1, id);

	    // When reversing operands of comparison operators,
	    // need to reverse the sense of the op
	    switch (op)
	    {
		case TOKlt:	op = TOKgt;	break;
		case TOKgt:	op = TOKlt;	break;
		case TOKle:	op = TOKge;	break;
		case TOKge:	op = TOKle;	break;

		// Floating point compares
		case TOKule:	op = TOKuge;	 break;
		case TOKul:	op = TOKug;	 break;
		case TOKuge:	op = TOKule;	 break;
		case TOKug:	op = TOKul;	 break;

		// These are symmetric
		case TOKunord:
		case TOKlg:
		case TOKleg:
		case TOKue:
		    break;
	    }

	    return e;
	}
    }

    return NULL;
}

/***********************************
 * Utility to build a function call out of this reference and argument.
 */

Expression *build_overload(Loc loc, Scope *sc, Expression *ethis, Expression *earg, Identifier *id)
{
    Expression *e;

    //printf("build_overload(id = '%s')\n", id->toChars());
    //earg->print();
    //earg->type->print();
    e = new DotIdExp(loc, ethis, id);

    if (earg)
	e = new CallExp(loc, e, earg);
    else
	e = new CallExp(loc, e);

    e = e->semantic(sc);
    return e;
}

/***************************************
 * Search for function funcid in aggregate ad.
 */

Dsymbol *search_function(ScopeDsymbol *ad, Identifier *funcid)
{
    Dsymbol *s;
    FuncDeclaration *fd;
    TemplateDeclaration *td;

    s = ad->search(0, funcid, 0);
    if (s)
    {	Dsymbol *s2;

	//printf("search_function: s = '%s'\n", s->kind());
	s2 = s->toAlias();
	//printf("search_function: s2 = '%s'\n", s2->kind());
	fd = s2->isFuncDeclaration();
	if (fd && fd->type->ty == Tfunction)
	    return fd;

	td = s2->isTemplateDeclaration();
	if (td)
	    return td;
    }
    return NULL;
}


/*****************************************
 * Given array of arguments and an aggregate type,
 * if any of the argument types are missing, attempt to infer
 * them from the aggregate type.
 */

void inferApplyArgTypes(enum TOK op, Arguments *arguments, Expression *aggr)
{
    if (!arguments || !arguments->dim)
	return;

    /* Return if no arguments need types.
     */
    for (size_t u = 0; 1; u++)
    {	if (u == arguments->dim)
	    return;
	Argument *arg = (Argument *)arguments->data[u];
	if (!arg->type)
	    break;
    }

    AggregateDeclaration *ad;

    Argument *arg = (Argument *)arguments->data[0];
    Type *taggr = aggr->type;
    if (!taggr)
	return;
    Type *tab = taggr->toBasetype();
    switch (tab->ty)
    {
	case Tarray:
	case Tsarray:
	case Ttuple:
	    if (arguments->dim == 2)
	    {
		if (!arg->type)
		    arg->type = Type::tsize_t;	// key type
		arg = (Argument *)arguments->data[1];
	    }
	    if (!arg->type && tab->ty != Ttuple)
		arg->type = tab->nextOf();	// value type
	    break;

	case Taarray:
	{   TypeAArray *taa = (TypeAArray *)tab;

	    if (arguments->dim == 2)
	    {
		if (!arg->type)
		    arg->type = taa->index;	// key type
		arg = (Argument *)arguments->data[1];
	    }
	    if (!arg->type)
		arg->type = taa->next;		// value type
	    break;
	}

	case Tclass:
	    ad = ((TypeClass *)tab)->sym;
	    goto Laggr;

	case Tstruct:
	    ad = ((TypeStruct *)tab)->sym;
	    goto Laggr;

	Laggr:
	    if (arguments->dim == 1)
	    {
		if (!arg->type)
		{
		    /* Look for a head() or rear() overload
		     */
		    Identifier *id = (op == TOKforeach) ? Id::Fhead : Id::Ftoe;
		    Dsymbol *s = search_function(ad, id);
		    FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL;
		    if (!fd)
		    {	if (s && s->isTemplateDeclaration())
			    break;
			goto Lapply;
		    }
		    arg->type = fd->type->nextOf();
		}
		break;
	    }

	Lapply:
	{   /* Look for an
	     *	int opApply(int delegate(ref Type [, ...]) dg);
	     * overload
	     */
	    Dsymbol *s = search_function(ad,
			(op == TOKforeach_reverse) ? Id::applyReverse
						   : Id::apply);
	    if (s)
	    {
		FuncDeclaration *fd = s->isFuncDeclaration();
		if (fd)
		{   inferApplyArgTypesX(fd, arguments);
		    break;
		}
#if 0
		TemplateDeclaration *td = s->isTemplateDeclaration();
		if (td)
		{   inferApplyArgTypesZ(td, arguments);
		    break;
		}
#endif
	    }
	    break;
	}

	case Tdelegate:
	{
	    if (0 && aggr->op == TOKdelegate)
	    {	DelegateExp *de = (DelegateExp *)aggr;

		FuncDeclaration *fd = de->func->isFuncDeclaration();
		if (fd)
		    inferApplyArgTypesX(fd, arguments);
	    }
	    else
	    {
		inferApplyArgTypesY((TypeFunction *)tab->nextOf(), arguments);
	    }
	    break;
	}

	default:
	    break;		// ignore error, caught later
    }
}

/********************************
 * Recursive helper function,
 * analogous to func.overloadResolveX().
 */

int fp3(void *param, FuncDeclaration *f)
{
    Arguments *arguments = (Arguments *)param;
    TypeFunction *tf = (TypeFunction *)f->type;
    if (inferApplyArgTypesY(tf, arguments) == 1)
	return 0;
    if (arguments->dim == 0)
	return 1;
    return 0;
}

static void inferApplyArgTypesX(FuncDeclaration *fstart, Arguments *arguments)
{
    overloadApply(fstart, &fp3, arguments);
}

/******************************
 * Infer arguments from type of function.
 * Returns:
 *	0 match for this function
 *	1 no match for this function
 */

static int inferApplyArgTypesY(TypeFunction *tf, Arguments *arguments)
{   size_t nparams;
    Argument *p;

    if (Argument::dim(tf->parameters) != 1)
	goto Lnomatch;
    p = Argument::getNth(tf->parameters, 0);
    if (p->type->ty != Tdelegate)
	goto Lnomatch;
    tf = (TypeFunction *)p->type->nextOf();
    assert(tf->ty == Tfunction);

    /* We now have tf, the type of the delegate. Match it against
     * the arguments, filling in missing argument types.
     */
    nparams = Argument::dim(tf->parameters);
    if (nparams == 0 || tf->varargs)
	goto Lnomatch;		// not enough parameters
    if (arguments->dim != nparams)
	goto Lnomatch;		// not enough parameters

    for (size_t u = 0; u < nparams; u++)
    {
	Argument *arg = (Argument *)arguments->data[u];
	Argument *param = Argument::getNth(tf->parameters, u);
	if (arg->type)
	{   if (!arg->type->equals(param->type))
	    {
		/* Cannot resolve argument types. Indicate an
		 * error by setting the number of arguments to 0.
		 */
		arguments->dim = 0;
		goto Lmatch;
	    }
	    continue;
	}
	arg->type = param->type;
    }
  Lmatch:
    return 0;

  Lnomatch:
    return 1;
}

/*******************************************
 * Infer foreach arg types from a template function opApply which looks like:
 *    int opApply(alias int func(ref uint))() { ... }
 */

#if 0
void inferApplyArgTypesZ(TemplateDeclaration *tstart, Arguments *arguments)
{
    for (TemplateDeclaration *td = tstart; td; td = td->overnext)
    {
        if (!td->scope)
        {
            error("forward reference to template %s", td->toChars());
            return;
        }
        if (!td->onemember || !td->onemember->toAlias()->isFuncDeclaration())
        {
            error("is not a function template");
            return;
        }
	if (!td->parameters || td->parameters->dim != 1)
	    continue;
	TemplateParameter *tp = (TemplateParameter *)td->parameters->data[0];
	TemplateAliasParameter *tap = tp->isTemplateAliasParameter();
	if (!tap || !tap->specType || tap->specType->ty != Tfunction)
	    continue;
	TypeFunction *tf = (TypeFunction *)tap->specType;
	if (inferApplyArgTypesY(tf, arguments) == 0)	// found it
	    return;
    }
}
#endif

/**************************************
 */

static void templateResolve(Match *m, TemplateDeclaration *td, Scope *sc, Loc loc, Objects *targsi, Expression *ethis, Expressions *arguments)
{
    FuncDeclaration *fd;

    assert(td);
    fd = td->deduceFunctionTemplate(sc, loc, targsi, ethis, arguments);
    if (!fd)
	return;
    m->anyf = fd;
    if (m->last >= MATCHexact)
    {
	m->nextf = fd;
	m->count++;
    }
    else
    {
	m->last = MATCHexact;
	m->lastf = fd;
	m->count = 1;
    }
}