public IBoundExpr CreateCall(IBoundExpr arg)
        {
            //### opt: unions with no value don't need to be references
            //         could just put the value in place
            var loadCase = new LoadExpr(arg, Decl.Int, 0);

            return(Intrinsic.EqualInt(loadCase, new IntExpr(mCase.Index)));
        }
        bool IBoundExprVisitor <bool> .Visit(LoadExpr expr)
        {
            expr.Struct.Accept(this);

            Write(OpCode.Load, (byte)expr.Index);

            return(true);
        }
        IBoundExpr IUnboundExprVisitor<IBoundExpr>.Visit(DefineExpr expr)
        {
            var stores = new List<IBoundExpr>();

            foreach (var define in expr.Definitions)
            {
                foreach (var name in define.Names)
                {
                    if (Scope.Contains(name)) throw new CompileException(define.Position,
                        "A local variable named \"" + name + "\" is already defined in this scope.");
                }

                var value = define.Value.Accept(this);

                if (define.Names.Count > 1)
                {

                    // splitting a tuple
                    var tupleType = value.Type as BoundTupleType;

                    if (tupleType == null) throw new CompileException(define.Position,
                        "Cannot define multiple names if the value is not a tuple.");

                    if (tupleType.Fields.Count < define.Names.Count) throw new CompileException(define.Position,
                        "Cannot bind more names in a define than the tuple has fields.");

                    // define a temporary for the entire tuple expression
                    var temp = mContext.NameGenerator.Generate();
                    Scope.Define(temp, value.Type, false);

                    // split out the fields
                    int field = 0;
                    foreach (var name in define.Names)
                    {
                        Scope.Define(name, tupleType.Fields[field], expr.IsMutable);

                        // assign it
                        var fieldValue = new LoadExpr(value, tupleType.Fields[field], field);
                        stores.Add(new StoreExpr(new LocalsExpr(), Scope[name], fieldValue));

                        field++;
                    }
                }
                else
                {
                    // just a single variable

                    // add it to the scope
                    Scope.Define(define.Names[0], value.Type, expr.IsMutable);

                    // assign it
                    stores.Add(new StoreExpr(new LocalsExpr(), Scope[define.Names[0]], value));
                }
            }

            return new BoundBlockExpr(stores);
        }
        /// <summary>
        /// Resolves and binds a reference to a name.
        /// </summary>
        /// <param name="function">The function being compiled.</param>
        /// <param name="scope">The scope in which the name is being bound.</param>
        /// <param name="name">The name being resolved. May or may not be fully-qualified.</param>
        /// <param name="typeArgs">The type arguments being applied to the name. For
        /// example, resolving "foo'(int, bool)" would pass in {int, bool} here.</param>
        /// <param name="arg">The argument being applied to the name.</param>
        /// <returns></returns>
        public IBoundExpr ResolveName(Function function,
                                      Scope scope, Position position, string name,
                                      IList <IUnboundDecl> typeArgs, IBoundExpr arg)
        {
            IBoundDecl argType = null;

            if (arg != null)
            {
                argType = arg.Type;
            }

            IBoundExpr resolved = null;

            // see if it's an argument
            if (function.ParamNames.Contains(name))
            {
                // load the argument
                resolved = new LoadExpr(new LocalsExpr(), function.ParameterType, 0);

                if (function.ParamNames.Count > 1)
                {
                    // function takes multiple parameters, so load it from the tuple
                    var paramTuple = (BoundTupleType)function.ParameterType;

                    var argIndex = (byte)function.ParamNames.IndexOf(name);
                    resolved = new LoadExpr(resolved, paramTuple.Fields[argIndex], argIndex);
                }
            }

            // see if it's a local
            if (scope.Contains(name))
            {
                var local = scope[name];

                // just load the value
                resolved = new LoadExpr(new LocalsExpr(), scope[name]);
            }

            // if we resolved to a local name, handle it
            if (resolved != null)
            {
                if (typeArgs.Count > 0)
                {
                    throw new CompileException(position, "Cannot apply type arguments to a local variable or function argument.");
                }

                // if the local or argument is holding a function reference and we're passed args, call it
                if (argType != null)
                {
                    var funcType = resolved.Type as FuncType;

                    if (funcType != null)
                    {
                        // check that args match
                        if (!DeclComparer.TypesMatch(funcType.Parameter.Bound, argType))
                        {
                            throw new CompileException(position, "Argument types passed to local function reference do not match function's parameter types.");
                        }

                        // call it
                        resolved = new BoundCallExpr(resolved, arg);
                    }
                    else
                    {
                        // not calling a function, so try to desugar to a __Call
                        var callArg = new BoundTupleExpr(new IBoundExpr[] { resolved, arg });

                        resolved = ResolveFunction(function, position,
                                                   "__Call", new IUnboundDecl[0], callArg);

                        if (resolved == null)
                        {
                            throw new CompileException(position, "Cannot call a local variable or argument that is not a function reference, and could not find a matching __Call.");
                        }
                    }
                }

                return(resolved);
            }

            // implicitly apply () as the argument if no other argument was provided.
            // note that we do this *after* checking for locals because locals aren't
            // implicitly applied. since most locals aren't functions anyway, it won't
            // matter in most cases, and in cases where a local holds a function, the
            // user will mostly likely want to treat that function like a value: return
            // it, pass it around, etc.
            if (arg == null)
            {
                arg     = new UnitExpr(Position.None);
                argType = arg.Type;
            }

            return(ResolveFunction(function, position, name, typeArgs, arg));
        }