view sema/TypeCheck.d @ 165:7606387b2f0a

Better handling of param checking on method calls.
author Anders Johnsen <skabet@gmail.com>
date Tue, 22 Jul 2008 18:24:15 +0200
parents ba94fd563548
children 7982eb63c0eb
line wrap: on
line source

module sema.TypeCheck;

import sema.Visitor,
       sema.Symbol,
       sema.DType;

import tango.io.Stdout,
       Integer = tango.text.convert.Integer;

import basic.SourceLocation,
       basic.Message;

class TypeCheck : Visitor!(void)
{
    this(MessageHandler messages)
    {
        this.messages = messages;
    }

    override void visitBinaryExp(BinaryExp exp)
    {
        super.visitBinaryExp(exp);

        if(!(exp.left.type is exp.right.type))
        {
            if (!exp.right.type.hasImplicitConversionTo(exp.left.type) &&
                !exp.left.type.hasImplicitConversionTo(exp.right.type))
                messages.report(InvalidImplicitCast, exp.loc)
                    .arg(exp.right.type.toString)
                    .arg(exp.left.type.toString);
            else
            {
                CastExp castExp;
                if(exp.left.type.isReal && exp.right.type.isReal)
                    if(exp.left.type.byteSize > exp.right.type.byteSize)
                        castExp = new CastExp(
                            SLoc.Invalid,
                            new Identifier(exp.left.type.name),
                            exp.right);
                    else
                        castExp = new CastExp(
                            SLoc.Invalid,
                            new Identifier(exp.right.type.name),
                            exp.left);
                else if(exp.left.type.isReal || exp.right.type.isReal)
                    if(exp.left.type.isReal)
                        castExp = new CastExp(
                            SLoc.Invalid,
                            new Identifier(exp.left.type.name),
                            exp.right);
                    else
                        castExp = new CastExp(
                            SLoc.Invalid,
                            new Identifier(exp.right.type.name),
                            exp.left);
                else
                    if(exp.left.type.byteSize > exp.right.type.byteSize)
                        castExp = new CastExp(
                            SLoc.Invalid,
                            new Identifier(exp.left.type.name),
                            exp.right);
                    else if(exp.left.type.byteSize > exp.right.type.byteSize)
                        castExp = new CastExp(
                            SLoc.Invalid,
                            new Identifier(exp.right.type.name),
                            exp.left);
                    else
                        castExp = new CastExp(
                            SLoc.Invalid,
                            new Identifier(exp.left.type.name),
                            exp.right);


                if(castExp)
                {
                    castExp.env = exp.env;
                    if(castExp.exp == exp.right)
                        exp.right = castExp;
                    else
                        exp.left = castExp;

                }
            }
        }
    }

    override void visitCallExp(CallExp exp)
    {
        super.visitCallExp(exp);

        Exp[] newArgs;

        foreach(i, arg; exp.args)
        {
            auto argType = exp.exp.type.asFunction.params[i];
            auto expType = arg.type;
            if(argType.byteSize != expType.byteSize)
            {
                if(!expType.hasImplicitConversionTo(argType))
                   messages.report(InvalidImplicitCast, exp.loc)
                        .arg(expType.toString)
                        .arg(argType.toString);

                auto castExp = new CastExp(
                        SLoc.Invalid,
                        new Identifier(argType.name),
                        arg);
                castExp.env = exp.exp.env;
                newArgs ~= castExp;
            }
            else
                newArgs ~= arg;
        }

        exp.args = newArgs;
    }

    override void visitNewExp(NewExp exp)
    {
        super.visitNewExp(exp);

        Exp[] newArgs;
        
        Symbol[] methods = exp.newType.getSymbol.findMembers("this");

        if (!methods.length)
        {
            messages.report(NoConstructor, exp.newType.loc);
            return;
        }

        Symbol sel = getBestMatch(exp.c_args, methods);

        if (sel)
        {
            foreach (i, arg; exp.c_args)
            {
                auto argType = sel.type.asFunction.params[i];
                auto expType = arg.type;
                if (argType.byteSize != expType.byteSize)
                {
                    if (!expType.hasImplicitConversionTo(argType))
                        messages.report(InvalidImplicitCast, exp.loc)
                            .arg(expType.toString)
                            .arg(argType.toString);

                    auto castExp = new CastExp(
                            SLoc.Invalid,
                            new Identifier(argType.name),
                            arg);
                    castExp.env = exp.newType.env;
                    newArgs ~= castExp;
                }
                else
                    newArgs ~= arg;
            }
            exp.c_args = newArgs;
            exp.callSym = sel;
        }
        else
        {
            messages.report(NoMachingCon, exp.newType.loc);
            foreach ( i, s ; methods )
            {
                messages.report(CandidateNr, 
                        (cast(FuncDecl)s.decl).identifier.loc)
                    .arg(Integer.toString(i+1));
            }
        }
    }

    override void visitAssignExp(AssignExp exp)
    {
        super.visitAssignExp(exp);

        auto identifierType = exp.identifier.type;
        auto expType = exp.exp.type;

        if(identifierType != expType)
        {
            if(!expType.hasImplicitConversionTo(identifierType))
                messages.report(InvalidImplicitCast, exp.loc)
                    .arg(expType.toString)
                    .arg(identifierType.toString);

            auto castExp = new CastExp(
                    SLoc.Invalid,
                    new Identifier(identifierType.name),
                    exp.exp);
            castExp.env = exp.exp.env;
            exp.exp = castExp;
        }
    }

    override void visitReturnStmt(ReturnStmt stmt)
    {
        super.visitReturnStmt(stmt);

        if(stmt.exp)
        {
            auto returnType = stmt.env.parentFunction.type.asFunction.returnType;
            auto expType = stmt.exp.type;
            if(returnType != expType)
            {
                if(!expType.hasImplicitConversionTo(returnType))
                   messages.report(InvalidImplicitCast, stmt.exp.loc)
                        .arg(expType.toString)
                        .arg(returnType.toString);

                auto castExp = new CastExp(
                        SLoc.Invalid,
                        new Identifier(returnType.name),
                        stmt.exp);
                castExp.env = stmt.exp.env;
                stmt.exp = castExp;
            }
        }
    }

    override void visitVarDecl(VarDecl decl)
    {
        super.visitVarDecl(decl);

        if(decl.init)
        {
            auto varType = decl.identifier.type;
            auto expType = decl.init.type;
            if(varType.byteSize != expType.byteSize)
            {
                if(!expType.hasImplicitConversionTo(varType))
                   messages.report(InvalidImplicitCast, decl.init.loc)
                        .arg(expType.toString)
                        .arg(varType.toString);

                auto castExp = new CastExp(
                        SLoc.Invalid,
                        new Identifier(varType.name),
                        decl.init);
                castExp.env = decl.init.env;
                decl.init = castExp;
            }
        }
    }

    private Symbol getBestMatch(Exp[] arg_list , Symbol[] available)
    body
    {
        Symbol[] _a;
        foreach (a ; available)
            if (a.type.isFunction)
                _a ~= a;
        available = _a;

        assert(available.length, "No available methods in symbol-list.");

        Symbol[] possible;
        Symbol perfect;

        foreach (s ; available)
        {
            if (s.type.asFunction.params.length < arg_list.length)
                continue;

            bool per = true;

            foreach (i, arg; arg_list)
            {
                auto argType = s.type.asFunction.params[i];
                auto expType = arg.type;
                if (argType != expType)
                {
                    per = false;
                    if( !expType.hasImplicitConversionTo(argType) )
                        break;
                }

                if (i == arg_list.length-1)
                {
                    bool work = true;
                    foreach (a ; (cast(FuncDecl)s.decl).funcArgs[i+1..$])
                        if (a.init is null)
                            work = false;

                    if (work)
                        if (per)
                            return s;
                        else
                            possible ~= s;
                }
            }
        }

        if (possible.length)
            return possible[0];

        return null;
    }

    MessageHandler messages;
}