view sema/TypeCheck.d @ 164:ba94fd563548

The symbol for the constructor a "new"-exp is calling is now stored in callSym in NewExp.
author Anders Johnsen <skabet@gmail.com>
date Tue, 22 Jul 2008 16:53:47 +0200
parents 6cb2f4201e2a
children 7606387b2f0a
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 ( exp.c_args.length )
        {
            if ( !methods.length )
            {
                messages.report(NoConstructor, exp.newType.loc);
                return;
            }

            Symbol[] possible;
            Symbol perfect;

            foreach( s ; methods )
            {
                bool per = true;

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

                    if ( i == exp.c_args.length-1 
                            && i == s.type.asFunction.params.length-1)
                        if (per)
                            perfect = s;
                        else
                            possible ~= s;
                }
            }

            Symbol sel;

            if ( perfect )
                sel = perfect;
            else
                if ( possible.length )
                    sel = possible[0];

            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;
            }
        }
    }

    MessageHandler messages;
}