view sema/TypeCheck.d @ 191:e799db8d9cb0

Fixed a small bug. Passes two more tests now.
author Anders Johnsen <skabet@gmail.com>
date Fri, 25 Jul 2008 14:44:50 +0200
parents 86a2ede00e9a
children 658178183018
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;

                }
            }
        }
        if (exp.op >= BinaryExp.Operator.LeftShift &&
            exp.op <= BinaryExp.Operator.UnsignedRightShift)
        {}  // FIXME: When we have const-system we need to check for 
            //        right site being larger then the bitsize of the 
            //        left
    }

    override void visitDerefExp(DerefExp exp)
    {
        if (!exp.exp.type.isPointer)
        {
            messages.report(CanOnlyDerefPointers,
                    [exp.exp.sourceRange][], 
                    [exp.loc])
                .arg(exp.exp.type.toString);

            exp._type = DType.Int;
        }
    }

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

        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;

            DFunction function_type;
            if (iden.type.isFunction())
            {
                Symbol[] methods = internalVisitMemberRef(iden);           

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

                Symbol sel = getBestMatch(exp.args, methods);
                exp.callSym = sel;
                if (sel)
                    function_type = sel.type.asFunction();
                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 (iden.type.isCallable)
                function_type = iden.type.asCallable();
            else assert(0, "Should not happen");

            foreach (i, arg; exp.args)
            {
                auto argType = function_type.params[i];
                auto expType = arg.type;
                if (argType.isSame(expType))
                {
                    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;
        }
        else if (auto iden = cast(Identifier)exp.exp)
        {
            Exp[] newArgs;

            DFunction function_type;
            if (iden.type.isFunction())
            {
                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);
                exp.callSym = sel;
                if (sel)
                    function_type = sel.type.asFunction();
                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 (iden.type.isCallable)
                function_type = iden.type.asCallable();
            else assert(0, "Should not happen");

            foreach (i, arg; exp.args)
            {
                auto argType = function_type.params[i];
                auto expType = arg.type;
                if (!argType.isSame(expType))
                {
                    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;
        }
        else
        {
            Exp[] newArgs;

            foreach(i, arg; exp.args)
            {
                auto argType = exp.exp.type.asFunction.params[i];
                auto expType = arg.type;
                if(!argType.isSame(expType))
                {
                    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.isSame(expType))
                {
                    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.isSame(expType))
        {
            if(!expType.hasImplicitConversionTo(identifierType))
                messages.report(InvalidImplicitCast, 
                        [exp.identifier.sourceRange, exp.exp.sourceRange][], 
                        [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;
        }

        if (expType.isStaticArray)
            messages.report(CannotReassignSArray, 
                    [exp.identifier.sourceRange, exp.exp.sourceRange][], 
                    [exp.loc]);
    }

    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.isSame(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.isSame(expType))
            {
                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.isSame(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;
}