view sema/TypeCheck.d @ 174:20ff3c31f600

Putting symbol on MemberRef -calls.
author Anders Johnsen <skabet@gmail.com>
date Thu, 24 Jul 2008 21:06:42 +0200
parents 7982eb63c0eb
children c8e26556c24d
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);

        Stdout(exp.exp).newline;
        if (auto iden = cast(MemberReference)exp.exp)
        {
            Symbol[] internalVisitMemberRef(MemberReference m)
            {
                Symbol[] visitRef(MemberReference m, Identifier target, Symbol st)
                {
                    auto child = m.child;
                    auto res = st.findMembers(child.get);
                    return res;
                }
                switch(m.target.expType)
                {
                    case ExpType.Identifier:
                        return visitRef(m, cast(Identifier)m.target,
                                (cast(Identifier)m.target).getSymbol);
                    case ExpType.MemberReference:
                        Symbol[] s = internalVisitMemberRef(cast(MemberReference)m.target);
                        if(s.length)
                            return s;
                        return visitRef(m, cast(Identifier)m.target, s[0]);
                }
            }

            Exp[] newArgs;

            Symbol[] methods = internalVisitMemberRef(iden);           

            if (!methods.length)
            {
                messages.report(NoMethodByName, iden.loc);
                return;
            }

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

            if (sel)
            {
                foreach (i, arg; exp.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 = iden.env;
                        newArgs ~= castExp;
                    }
                    else
                        newArgs ~= arg;
                }
                exp.args = newArgs;
                exp.callSym = sel;
            }
            else
            {
                messages.report(NoMachingMethod, exp.loc);
                foreach ( i, s ; methods )
                {
                    messages.report(CandidateNr, 
                            (cast(FuncDecl)s.decl).identifier.loc)
                        .arg(Integer.toString(i+1));
                }
            }
        }
        else if (auto iden = cast(Identifier)exp.exp)
        {
            Exp[] newArgs;

            Symbol[] methods;
            
            foreach (decl ; iden.env.find(iden.get))
                methods ~= decl.sym;

            if (!methods.length)
            {
                messages.report(NoMethodByName, iden.loc);
                return;
            }

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

            if (sel)
            {
                foreach (i, arg; exp.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 = iden.env;
                        newArgs ~= castExp;
                    }
                    else
                        newArgs ~= arg;
                }
                exp.args = newArgs;
                exp.callSym = sel;
            }
            else
            {
                messages.report(NoMachingMethod, exp.loc);
                foreach ( i, s ; methods )
                {
                    messages.report(CandidateNr, 
                            (cast(FuncDecl)s.decl).identifier.loc)
                        .arg(Integer.toString(i+1));
                }
            }
        }
        else
        {
            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.findFunctionMembers("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)
    in
    {
        foreach (a ; available)
            assert(a.type.isFunction, "A non-function found in available-list.");
    }
    body
    {
        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;
            bool work = 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) )
                    {
                        work = false;
                        break;
                    }
                }
            }

            foreach (a ; (cast(FuncDecl)s.decl).funcArgs[arg_list.length..$])
                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;
}