diff --git a/modcc/expression.cpp b/modcc/expression.cpp
index 9ba6c58ea1c1a3716080163456688f8792db1981..51e8217b5147109af2dfff553a71889f25b11ef4 100644
--- a/modcc/expression.cpp
+++ b/modcc/expression.cpp
@@ -866,8 +866,7 @@ void IfExpression::semantic(scope_ptr scp) {
 
     condition_->semantic(scp);
 
-    auto cond = condition_->is_conditional();
-    if(!cond) {
+    if(!condition_->is_conditional()) {
         error("not a valid conditional expression");
     }
 
@@ -878,6 +877,10 @@ void IfExpression::semantic(scope_ptr scp) {
     }
 }
 
+void IfExpression::replace_condition(expression_ptr&& other) {
+    std::swap(condition_, other);
+}
+
 expression_ptr IfExpression::clone() const {
     return make_expression<IfExpression>(
             location_,
diff --git a/modcc/expression.hpp b/modcc/expression.hpp
index 1da38c35247e7cb3fc0f2cf5f2c49cfa714e7baa..4d131b44c81c5f2aeddcb2ddab4abe8f8d4ae74f 100644
--- a/modcc/expression.hpp
+++ b/modcc/expression.hpp
@@ -797,6 +797,8 @@ public:
     expression_ptr clone() const override;
 
     std::string to_string() const override;
+
+    void replace_condition(expression_ptr&& other);
     void semantic(scope_ptr scp) override;
 
     void accept(Visitor* v) override;
@@ -1142,6 +1144,16 @@ public:
     BlockExpression* body() {
         return body_->is_block();
     }
+    void body(expression_ptr&& new_body) {
+        if(!new_body->is_block()) {
+            Location loc = new_body? new_body->location(): Location{};
+            throw compiler_exception(
+                    " attempt to set FunctionExpression body with non-block expression, i.e.\n"
+                    + new_body->to_string(),
+                    loc);
+        }
+        body_ = std::move(new_body);
+    }
 
     FunctionExpression* is_function() override {return this;}
     void semantic(scope_type::symbol_map&) override;
diff --git a/modcc/functionexpander.cpp b/modcc/functionexpander.cpp
index a6b443306f53fc411d0b952a54ac67b586e151b8..a17329d9dc6e9a63ce0ddc7152526486779a0a8d 100644
--- a/modcc/functionexpander.cpp
+++ b/modcc/functionexpander.cpp
@@ -12,77 +12,112 @@ expression_ptr insert_unique_local_assignment(expr_list_type& stmts, Expression*
     return std::move(exprs.id);
 }
 
-///////////////////////////////////////////////////////////////////////////////
-//  function call site lowering
-///////////////////////////////////////////////////////////////////////////////
 
-expr_list_type lower_function_calls(Expression* e)
-{
-    auto v = std::make_unique<FunctionCallLowerer>(e->scope());
-
-    if(auto a=e->is_assignment()) {
-#ifdef LOGGING
-        std::cout << "lower_function_calls inspect expression " << e->to_string() << "\n";
-#endif
-        // recursively inspect and replace function calls with identifiers
-        a->rhs()->accept(v.get());
-
-    }
-
-    // return the list of statements that assign function call return values
-    // to identifiers, e.g.
-    //      LOCAL ll1_
-    //      ll1_ = mInf(v)
-    return v->move_calls();
-}
-
-void FunctionCallLowerer::visit(Expression *e) {
-    throw compiler_exception(
-        "function lowering for expressions of the type " + e->to_string()
-        + " has not been defined", e->location()
-    );
+/////////////////////////////////////////////////////////////////////
+// lower function call sites so that all function calls are of
+// the form : variable = call(<args>)
+// then lower function arguments that are not identifiers or literals
+// e.g.
+//      a = 2 + foo(2+x, y, 1)
+// becomes
+//      ll0_ = foo(2+x, y, 1)
+//      a = 2 + ll0_
+// becomes
+//       ll1_ = 2+x
+//       ll0_ = foo(ll1_, y, 1)
+//       a = 2 + ll0_
+/////////////////////////////////////////////////////////////////////
+expression_ptr lower_functions(BlockExpression* block) {
+    auto v = std::make_unique<FunctionCallLowerer>();
+    block->accept(v.get());
+    return v->as_block(false);
 }
 
+// We only need to lower function arguments when visiting a Call expression
+// Function arguments are checked for other Call expressions, which recurse.
+// When all Call arguments are handled, other arguments are checked, and
+// lowered if needed
+// e.g. foo(bar(x + 2), y - 1)
+// First, the visitor recurses for bar(x + 2) which gets its arguments lowered:
+//      ll0_ = x + 2;
+//      bar(ll0_);
+// Then, bar(x + 2) gets expanded into
+//      ll1_ = bar(ll0_);
+//      foo(ll1_, y - 1);
+// Finally, foo(ll1_, y - 1) gets its arguments lowered into
+//      ll2_ = y - 1;
+//      foo(ll1_, ll2_);
+// which turns:
+//      foo(bar(x + 2), y - 1)
+// into:
+//      ll0_ = x + 2;
+//      ll1_ = bar(ll0_);
+//      ll2_ = y - 1;
+//      foo(ll1_, ll2_);
 void FunctionCallLowerer::visit(CallExpression *e) {
+    // Lower function calls
     for(auto& arg : e->args()) {
         if(auto func = arg->is_function_call()) {
+            // Recurse on the Call Expression
             func->accept(this);
-#ifdef LOGGING
-            std::cout << "  lowering : " << func->to_string() << "\n";
-#endif
-            expand_call(
-                func, [&arg](expression_ptr&& p){arg = std::move(p);}
-            );
-            arg->semantic(scope_);
+            expand_call(func, [&arg](expression_ptr&& p){arg = std::move(p);});
+            arg->semantic(block_scope_);
         }
         else {
             arg->accept(this);
         }
     }
-}
+    // Lower function arguments
+    for(auto& arg : e->args()) {
+        if(arg->is_number() || arg->is_identifier()) {
+            continue;
+        }
+        auto id = insert_unique_local_assignment(statements_, arg.get());
+        std::swap(arg, id);
+    }
 
-void FunctionCallLowerer::visit(UnaryExpression *e) {
-    if(auto func = e->expression()->is_function_call()) {
-        func->accept(this);
-#ifdef LOGGING
-        std::cout << "  lowering : " << func->to_string() << "\n";
-#endif
-        expand_call(func, [&e](expression_ptr&& p){e->replace_expression(std::move(p));});
-        e->semantic(scope_);
+    // Procedure Expressions need to be printed stand-alone
+    // Function Expressions are always part of a bigger expression
+    if (e->is_procedure_call()) {
+        statements_.push_back(e->clone());
     }
-    else {
-        e->expression()->accept(this);
+}
+
+void FunctionCallLowerer::visit(AssignmentExpression *e) {
+    e->rhs()->accept(this);
+    if (auto func = e->rhs()->is_function_call()) {
+        for (auto& arg: func->args()) {
+            if (auto id = arg->is_identifier()) {
+                if (id->name() == e->lhs()->is_identifier()->name()) {
+                    expand_call(func, [&e](expression_ptr&& p){e->replace_rhs(std::move(p));});
+                    e->semantic(block_scope_);
+                    break;
+                }
+            }
+        }
     }
+    statements_.push_back(e->clone());
 }
 
+void FunctionCallLowerer::visit(ConserveExpression *e) {
+    statements_.push_back(e->clone());
+}
+
+void FunctionCallLowerer::visit(CompartmentExpression *e) {
+    statements_.push_back(e->clone());
+}
+
+void FunctionCallLowerer::visit(LinearExpression *e) {
+    statements_.push_back(e->clone());
+}
+
+// Binary Expressions need to handle function calls if they contain them
+// Functions calls have to be visited and expanded out of the expression
 void FunctionCallLowerer::visit(BinaryExpression *e) {
     if(auto func = e->lhs()->is_function_call()) {
         func->accept(this);
-#ifdef LOGGING
-        std::cout << "  lowering : " << func->to_string() << "\n";
-#endif
         expand_call(func, [&e](expression_ptr&& p){e->replace_lhs(std::move(p));});
-        e->semantic(scope_);
+        e->semantic(block_scope_);
     }
     else {
         e->lhs()->accept(this);
@@ -90,50 +125,67 @@ void FunctionCallLowerer::visit(BinaryExpression *e) {
 
     if(auto func = e->rhs()->is_function_call()) {
         func->accept(this);
-#ifdef LOGGING
-        std::cout << "  lowering : " << func->to_string() << "\n";
-#endif
         expand_call(func, [&e](expression_ptr&& p){e->replace_rhs(std::move(p));});
-        e->semantic(scope_);
+        e->semantic(block_scope_);
     }
     else {
         e->rhs()->accept(this);
     }
 }
 
-///////////////////////////////////////////////////////////////////////////////
-//  function argument lowering
-///////////////////////////////////////////////////////////////////////////////
-
-expr_list_type
-lower_function_arguments(std::vector<expression_ptr>& args)
-{
-    expr_list_type new_statements;
-    for(auto it=args.begin(); it!=args.end(); ++it) {
-        // get reference to the unique_ptr with the expression
-        auto& e = *it;
-#ifdef LOGGING
-        std::cout << "inspecting argument @ " << e->location() << " : " << e->to_string() << std::endl;
-#endif
-
-        if(e->is_number() || e->is_identifier()) {
-            // do nothing, because identifiers and literals are in the correct form
-            // for lowering
-            continue;
-        }
+// Unary Expressions need to handle function calls if they contain them
+// Functions calls have to be visited and expanded out of the expression
+void FunctionCallLowerer::visit(UnaryExpression *e) {
+    if(auto func = e->expression()->is_function_call()) {
+        func->accept(this);
+        expand_call(func, [&e](expression_ptr&& p){e->replace_expression(std::move(p));});
+        e->semantic(block_scope_);
+    }
+    else {
+        e->expression()->accept(this);
+    }
+}
+
+// If expressions need to handle the condition before the true and false branches
+// The condition should be handled by the Binary Expression visitor which will
+// expand any contained function calls and lower their arguments
+void FunctionCallLowerer::visit(IfExpression *e) {
+    expr_list_type outer;
+
+    e->condition()->accept(this);
+
+    if(auto func = e->condition()->is_function_call()) {
+        expand_call(func, [&e](expression_ptr&& p){
+            auto zero_exp = make_expression<NumberExpression>(Location{}, 0.);
+            p = make_expression<ConditionalExpression>(p->location(), tok::ne, p->clone(), std::move(zero_exp));
+            e->replace_condition(std::move(p));
+        });
+        e->semantic(block_scope_);
+    }
 
-        auto id = insert_unique_local_assignment(new_statements, e.get());
-#ifdef LOGGING
-        std::cout << "  lowering to " << new_statements.back()->to_string() << "\n";
-#endif
-        // replace the function call in the original expression with the local
-        // variable which holds the pre-computed value
-        std::swap(e, id);
+    std::swap(outer, statements_);
+
+    e->true_branch()->accept(this);
+    auto true_branch = make_expression<BlockExpression>(
+            e->true_branch()->location(),
+            std::move(statements_),
+            true);
+
+    statements_.clear();
+    expression_ptr false_branch;
+    if (e->false_branch()) {
+        e->false_branch()->accept(this);
+        false_branch = make_expression<BlockExpression>(
+                e->false_branch()->location(),
+                std::move(statements_),
+                true);
     }
-#ifdef LOGGING
-    std::cout << "\n";
-#endif
 
-    return new_statements;
+    statements_ = std::move(outer);
+    statements_.push_back(make_expression<IfExpression>(
+            e->location(),
+            e->condition()->clone(),
+            std::move(true_branch),
+            std::move(false_branch)));
 }
 
diff --git a/modcc/functionexpander.hpp b/modcc/functionexpander.hpp
index b1ea95a2f3777d7c45869728406883074af02862..cfb04b7f4f47866af0072aac6a58a0ea446c8eda 100644
--- a/modcc/functionexpander.hpp
+++ b/modcc/functionexpander.hpp
@@ -11,83 +11,34 @@
 // Return the new unique local identifier.
 expression_ptr insert_unique_local_assignment(expr_list_type& stmts, Expression* e);
 
-// prototype for lowering function calls
-expr_list_type lower_function_calls(Expression* e);
+// prototype for lowering function calls and arguments
+expression_ptr lower_functions(BlockExpression* block);
 
-///////////////////////////////////////////////////////////////////////////////
-// visitor that takes function call sites and lowers them to inline assignments
-//
-// e.g. if called on the following statement
-//
-// a = 3 + foo(x, y)
-//
-// the calls_ member will be
-//
-// LOCAL ll0_
-// ll0_ = foo(x,y)
-//
-// and the original statment is modified to be
-//
-// a = 3 + ll0_
-//
-// If the calls_ data is spliced directly before the original statement
-// the function call will have been fully lowered
-///////////////////////////////////////////////////////////////////////////////
-class FunctionCallLowerer : public Visitor {
+class FunctionCallLowerer : public BlockRewriterBase {
 public:
-    FunctionCallLowerer(scope_ptr s)
-    :   scope_(s)
-    {}
-
-    void visit(CallExpression *e)       override;
-    void visit(Expression *e)           override;
-    void visit(UnaryExpression *e)      override;
-    void visit(BinaryExpression *e)     override;
-    void visit(NumberExpression *e)     override {};
-    void visit(IdentifierExpression *e) override {};
-
-    expr_list_type& calls() {
-        return calls_;
-    }
-
-    expr_list_type move_calls() {
-        return std::move(calls_);
-    }
-
-    ~FunctionCallLowerer() {}
+    using BlockRewriterBase::visit;
+
+    FunctionCallLowerer(): BlockRewriterBase() {}
+    FunctionCallLowerer(scope_ptr s): BlockRewriterBase(s) {}
+
+    virtual void visit(CallExpression *e)        override;
+    virtual void visit(ConserveExpression *e)    override;
+    virtual void visit(CompartmentExpression *e) override;
+    virtual void visit(LinearExpression *e)      override;
+    virtual void visit(AssignmentExpression *e)  override;
+    virtual void visit(BinaryExpression *e)      override;
+    virtual void visit(UnaryExpression *e)       override;
+    virtual void visit(IfExpression *e)          override;
+    virtual void visit(NumberExpression *e)      override {};
+    virtual void visit(IdentifierExpression *e)  override {};
 
 private:
     template< typename F>
     void expand_call(CallExpression* func, F replacer) {
-        auto id = insert_unique_local_assignment(calls_, func);
+        auto id = insert_unique_local_assignment(statements_, func);
         // replace the function call in the original expression with the local
         // variable which holds the pre-computed value
         replacer(std::move(id));
     }
-
-    expr_list_type calls_;
-    scope_ptr scope_;
 };
 
-///////////////////////////////////////////////////////////////////////////////
-// visitor that takes function arguments that are not literals of identifiers
-// and lowers them to inline assignments
-//
-// e.g. if called on the following statement
-//
-// a = foo(2+x, y)
-//
-// the calls_ member will be
-//
-// LOCAL ll0_
-// ll0_ = 2+x
-//
-// and the original statment is modified to be
-//
-// a = foo(ll0_, y)
-//
-// If the calls_ data is spliced directly before the original statement
-// the function arguments will have been fully lowered
-///////////////////////////////////////////////////////////////////////////////
-expr_list_type lower_function_arguments(std::vector<expression_ptr>& args);
-
diff --git a/modcc/functioninliner.cpp b/modcc/functioninliner.cpp
index f238a684529c53ae9fcbe2e1d0da86d17ac9f0a6..8a9e63113f48eb1878d1fabbbbf98733b8511e6c 100644
--- a/modcc/functioninliner.cpp
+++ b/modcc/functioninliner.cpp
@@ -4,128 +4,200 @@
 #include "error.hpp"
 #include "functioninliner.hpp"
 #include "errorvisitor.hpp"
+#include "symdiff.hpp"
 
-expression_ptr inline_function_call(const expression_ptr& e)
-{
-    auto assign_to_func = e->is_assignment();
-    auto ret_identifier = assign_to_func->lhs()->is_identifier();
+expression_ptr inline_function_calls(std::string calling_func, BlockExpression* block) {
+    auto inline_block = block->clone();
 
-    if(auto f = assign_to_func->rhs()->is_function_call()) {
-        auto body = f->function()->body()->clone();
+    // The function inliner will inline one function at a time
+    // Once all functions in a block have been inlined, the
+    // while loop will be broken
+    while(true) {
+        inline_block->semantic(block->scope());
 
-        for (auto&s: body->is_block()->statements()) {
-            s->semantic(e->scope());
-        }
+        auto func_inliner = std::make_unique<FunctionInliner>(calling_func);
+        inline_block->accept(func_inliner.get());
 
-        FunctionInliner func_inliner(f->name(), ret_identifier, f->function()->args(), f->args(), e->scope());
+        if (!func_inliner->return_val_set()) {
+            throw compiler_exception("return variable of function not set", block->location());
+        }
 
-        body->accept(&func_inliner);
-        if (!func_inliner.return_val_set()) {
-            throw compiler_exception(pprintf("return variable of function % not set", f->name()), e->location());
+        if (func_inliner->finished_inlining()) {
+            return func_inliner->as_block(false);
         }
-        return body;
+
+        inline_block = func_inliner->as_block(false);
     }
-    return {};
 }
+
 ///////////////////////////////////////////////////////////////////////////////
 //  function inliner
 ///////////////////////////////////////////////////////////////////////////////
 
-// Takes a Binary or Unary Expression and replaces its variables that match any
-// function argument in fargs_ with the corresponding call argument in cargs_
-void FunctionInliner::replace_args(Expression* e) {
-    for(auto i=0u; i<fargs_.size(); ++i) {
-        if(auto id = cargs_[i]->is_identifier()) {
-            VariableReplacer v(fargs_[i], id->spelling());
-            e->accept(&v);
-        }
-        else if(auto value = cargs_[i]->is_number()) {
-            ValueInliner v(fargs_[i], value->value());
-            e->accept(&v);
-        }
-        else {
-            throw compiler_exception("can't inline functions with expressions as arguments", e->location());
-        }
-    }
-    e->semantic(scope_);
-
-    ErrorVisitor v("");
-    e->accept(&v);
-    if(v.num_errors()) {
-        throw compiler_exception("something went wrong with inlined function call ", e->location());
-    }
-}
-
+// The Function inliner works on inlining one function at a time.
+// If no function is being inlined when an expression is being visited,
+// the expression remains the same.
 void FunctionInliner::visit(Expression* e) {
+    if (!inlining_in_progress_) {
+        statements_.push_back(e->clone());
+        return;
+    }
     throw compiler_exception(
             "I don't know how to do function inlining for this statement : "
             + e->to_string(), e->location());
 }
 
+// Only in procedures, always stays the same
+void FunctionInliner::visit(ConserveExpression *e) {
+    statements_.push_back(e->clone());
+}
+
+// Only in procedures, always stays the same
+void FunctionInliner::visit(CompartmentExpression *e) {
+    statements_.push_back(e->clone());
+}
+
+// Only in procedures, always stays the same
+void FunctionInliner::visit(LinearExpression *e) {
+    statements_.push_back(e->clone());
+}
+
 void FunctionInliner::visit(LocalDeclaration* e) {
-    auto loc = e->location();
+    if (!inlining_in_progress_) {
+        statements_.push_back(e->clone());
+        return;
+    }
 
     std::map<std::string, Token> new_vars;
     for (auto& var: e->variables()) {
-        auto unique_decl = make_unique_local_decl(scope_, loc, "r_");
+        auto unique_decl = make_unique_local_decl(scope_, e->location(), "r_");
         auto unique_name = unique_decl.id->is_identifier()->spelling();
 
         // Local variables must be renamed to avoid collisions with the calling function.
-        // They are considered part of the function arguments `fargs_` and the renamed
-        // variable is considered part of the call arguments `cargs_`
-        fargs_.push_back(var.first);
-        cargs_.push_back(unique_decl.id->clone());
+        // The mappings are stored in local_arg_map
+        local_arg_map_.emplace(std::make_pair(var.first, std::move(unique_decl.id)));
 
         auto e_tok = var.second;
         e_tok.spelling = unique_name;
         new_vars[unique_name] =  e_tok;
     }
     e->variables().swap(new_vars);
-}
+    statements_.push_back(e->clone());
 
-void FunctionInliner::visit(BlockExpression* e) {
-    for (auto& expr: e->statements()) {
-        expr->accept(this);
-    }
 }
 
 void FunctionInliner::visit(UnaryExpression* e) {
-    replace_args(e);
+    if (!inlining_in_progress_) {
+        return;
+    }
+
+    auto sub = substitute(e->expression(), local_arg_map_);
+    sub = substitute(sub, call_arg_map_);
+    e->replace_expression(std::move(sub));
+
+    e->semantic(scope_);
+
+    ErrorVisitor v("");
+    e->accept(&v);
+    if(v.num_errors()) {
+        throw compiler_exception("something went wrong with inlined function call ", e->location());
+    }
 }
 
 void FunctionInliner::visit(BinaryExpression* e) {
-    replace_args(e);
+    if (!inlining_in_progress_) {
+        return;
+    }
+    auto sub_lhs = substitute(e->lhs(), local_arg_map_);
+    sub_lhs = substitute(sub_lhs, call_arg_map_);
+
+    auto sub_rhs = substitute(e->rhs(), local_arg_map_);
+    sub_rhs = substitute(sub_rhs, call_arg_map_);
+
+    e->replace_lhs(std::move(sub_lhs));
+    e->replace_rhs(std::move(sub_rhs));
+
+    e->semantic(scope_);
+
+    ErrorVisitor v("");
+    e->accept(&v);
+    if(v.num_errors()) {
+        throw compiler_exception("something went wrong with inlined function call ", e->location());
+    }
 }
 
 void FunctionInliner::visit(AssignmentExpression* e) {
+    // At this point, after function lowering, all function calls should be on the rhs of
+    // an Assignment Expression.
+    // If we find a new function to inline, we can do so, provided we aren't already inlining
+    // another function and we haven't inlined a function already.
+    if (!inlining_in_progress_ && !inlining_executed_ && e->rhs()->is_function_call()) {
+        auto f = e->rhs()->is_function_call();
+        auto& fargs = f->function()->args();
+        auto& cargs = f->args();
+
+        inlining_in_progress_ = true;
+        inlining_func_ = f->name();
+        lhs_ = e->lhs()->is_identifier()->clone();
+        return_set_ = false;
+        scope_ = e->scope();
+
+        for (unsigned i = 0; i < fargs.size(); ++i) {
+            call_arg_map_.emplace(std::make_pair(fargs[i]->is_argument()->spelling(), cargs[i]->clone()));
+        }
+
+        auto body = f->function()->body()->clone();
+        for (auto&s: body->is_block()->statements()) {
+            s->semantic(e->scope());
+        }
+
+        body->accept(this);
+        inlining_in_progress_ = false;
+        inlining_executed_ = true;
+        return;
+    }
+
+    // If we're not inlining a function call, don't change anything in the expression
+    if (!inlining_in_progress_) {
+        statements_.push_back(e->clone());
+        return;
+    }
+
+    // If we're inlining a function call, take care of variable renaming
     if (auto lhs = e->lhs()->is_identifier()) {
-        if (lhs->spelling() == func_name_) {
+        std::string iden_name = lhs->spelling();
+
+        // if the identifier name matches the function name, then we are setting the return value
+        if (iden_name == inlining_func_) {
             e->replace_lhs(lhs_->clone());
             return_set_ = true;
         } else {
-            for (unsigned i = 0;  i < fargs_.size(); i++) {
-                if (fargs_[i] == lhs->spelling()) {
-                    e->replace_lhs(cargs_[i]->clone());
-                    break;
-                }
+            if (local_arg_map_.count(iden_name)) {
+                e->replace_lhs(local_arg_map_.at(iden_name)->clone());
             }
         }
     }
 
     if (auto rhs = e->rhs()->is_identifier()) {
-        for (unsigned i = 0;  i < fargs_.size(); i++) {
-            if (fargs_[i] == rhs->spelling()) {
-                e->replace_rhs(cargs_[i]->clone());
-                break;
-            }
+        if (local_arg_map_.count(rhs->spelling())) {
+            e->replace_rhs(local_arg_map_.at(rhs->spelling())->clone());
+        }
+        if (call_arg_map_.count(rhs->spelling())) {
+            e->replace_rhs(call_arg_map_.at(rhs->spelling())->clone());
         }
     }
     else {
         e->rhs()->accept(this);
     }
+    statements_.push_back(e->clone());
 }
 
 void FunctionInliner::visit(IfExpression* e) {
+    expr_list_type outer;
+    std::swap(outer, statements_);
+
+    // Make sure if Expressions set the return value if not already set
+    // return_set_ will always be true unless we are inlining a function
     bool if_ret;
     bool save_ret = return_set_;
 
@@ -133,117 +205,65 @@ void FunctionInliner::visit(IfExpression* e) {
 
     e->condition()->accept(this);
     e->true_branch()->accept(this);
+    auto true_branch = make_expression<BlockExpression>(
+            e->true_branch()->location(),
+            std::move(statements_),
+            true);
 
+    statements_.clear();
     if_ret = return_set_;
     return_set_ = false;
 
+    expression_ptr false_branch;
     if (e->false_branch()) {
         e->false_branch()->accept(this);
+        false_branch = make_expression<BlockExpression>(
+                e->false_branch()->location(),
+                std::move(statements_),
+                true);
     }
 
+    statements_.clear();
     if_ret &= return_set_;
-
     return_set_ = save_ret? save_ret: if_ret;
+
+    statements_ = std::move(outer);
+    statements_.push_back(make_expression<IfExpression>(
+            e->location(),
+            e->condition()->clone(),
+            std::move(true_branch),
+            std::move(false_branch)));
 }
 
 void FunctionInliner::visit(CallExpression* e) {
-    for (auto& a: e->is_function_call()->args()) {
-        for (unsigned i = 0;  i < fargs_.size(); i++) {
-            if (auto id = a->is_identifier()) {
-                if (fargs_[i] == id->spelling()) {
-                    a = cargs_[i]->clone();
-                }
-            } else {
-                a->accept(this);
-            }
+    if (!inlining_in_progress_) {
+        if (e->is_procedure_call()) {
+            statements_.push_back(e->clone());
         }
+        return;
     }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-//  variable replacer
-///////////////////////////////////////////////////////////////////////////////
-
-void VariableReplacer::visit(Expression *e) {
-    throw compiler_exception(
-            "I don't know how to variable inlining for this statement : "
-            + e->to_string(), e->location());
-}
-
-void VariableReplacer::visit(UnaryExpression *e) {
-    auto exp = e->expression()->is_identifier();
-    if(exp && exp->spelling()==source_) {
-        e->replace_expression(
-            make_expression<IdentifierExpression>(exp->location(), target_)
-        );
-    }
-    else if(!exp) {
-        e->expression()->accept(this);
-    }
-}
-
-void VariableReplacer::visit(BinaryExpression *e) {
-    auto lhs = e->lhs()->is_identifier();
-    if(lhs && lhs->spelling()==source_) {
-        e->replace_lhs(
-            make_expression<IdentifierExpression>(lhs->location(), target_)
-        );
-    }
-    else if(!lhs){ // only inspect subexpressions that are not themselves identifiers
-        e->lhs()->accept(this);
-    }
-
-    auto rhs = e->rhs()->is_identifier();
-    if(rhs && rhs->spelling()==source_) {
-        e->replace_rhs(
-            make_expression<IdentifierExpression>(rhs->location(), target_)
-        );
-    }
-    else if(!rhs){ // only inspect subexpressions that are not themselves identifiers
-        e->rhs()->accept(this);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-//  value inliner
-///////////////////////////////////////////////////////////////////////////////
 
-void ValueInliner::visit(Expression *e) {
-    throw compiler_exception(
-            "I don't know how to value inlining for this statement : "
-            + e->to_string(), e->location());
-}
-
-void ValueInliner::visit(UnaryExpression *e) {
-    auto exp = e->expression()->is_identifier();
-    if(exp && exp->spelling()==source_) {
-        e->replace_expression(
-            make_expression<NumberExpression>(exp->location(), value_)
-        );
+    if (e->is_function_call()->name() == inlining_func_ || e->is_function_call()->name() == calling_func_) {
+        throw compiler_exception("recursive functions not allowed", e->location());
     }
-    else if(!exp){
-        e->expression()->accept(this);
-    }
-}
 
-void ValueInliner::visit(BinaryExpression *e) {
-    auto lhs = e->lhs()->is_identifier();
-    if(lhs && lhs->spelling()==source_) {
-        e->replace_lhs(
-            make_expression<NumberExpression>(lhs->location(), value_)
-        );
-    }
-    else if(!lhs) {
-        e->lhs()->accept(this);
-    }
+    auto& args = e->is_function_call() ? e->is_function_call()->args() : e->is_procedure_call()->args();
 
-    auto rhs = e->rhs()->is_identifier();
-    if(rhs && rhs->spelling()==source_) {
-        e->replace_rhs(
-            make_expression<NumberExpression>(rhs->location(), value_)
-        );
+    for (auto& a: args) {
+        if (auto id = a->is_identifier()) {
+            std::string iden_name = id->spelling();
+            if (local_arg_map_.count(iden_name)) {
+                a = local_arg_map_.at(iden_name)->clone();
+            }
+            if (call_arg_map_.count(iden_name)) {
+                a = call_arg_map_.at(iden_name)->clone();
+            }
+        } else {
+            a->accept(this);
+        }
     }
-    else if(!rhs){
-        e->rhs()->accept(this);
+    if (e->is_procedure_call()) {
+        statements_.push_back(e->clone());
+        return;
     }
 }
diff --git a/modcc/functioninliner.hpp b/modcc/functioninliner.hpp
index c302e36f2be73b71f2e9b1fe29b8d27bcb78c54a..34e37a5c038dea86f8e92a7356bc3a4002f32810 100644
--- a/modcc/functioninliner.hpp
+++ b/modcc/functioninliner.hpp
@@ -5,94 +5,61 @@
 #include "scope.hpp"
 #include "visitor.hpp"
 
-// Takes an assignment to a function call, returns an inlined
-// version without modifying the original expression's contents
-expression_ptr inline_function_call(const expression_ptr& e);
-
-class FunctionInliner : public Visitor {
+expression_ptr inline_function_calls(std::string calling_func, BlockExpression* block);
 
+class FunctionInliner : public BlockRewriterBase {
 public:
-
-    FunctionInliner(std::string func_name,
-                    Expression* lhs,
-                    const std::vector<expression_ptr>& fargs,
-                    const std::vector<expression_ptr>& cargs,
-                    const scope_ptr& scope) :
-                    func_name_(func_name), lhs_(lhs->clone()), scope_(scope) {
-        for (auto& f: fargs) {
-            fargs_.push_back(f->is_argument()->spelling());
-        }
-        for (auto& c: cargs) {
-            cargs_.push_back(c->clone());
-        }
-    }
-
-    void visit(Expression* e)            override;
-    void visit(UnaryExpression* e)       override;
-    void visit(BinaryExpression* e)      override;
-    void visit(BlockExpression *e)       override;
-    void visit(AssignmentExpression* e)  override;
-    void visit(IfExpression* e)          override;
-    void visit(LocalDeclaration* e)      override;
-    void visit(CallExpression* e)        override;
-    void visit(NumberExpression* e)      override {};
+    using BlockRewriterBase::visit;
+
+    FunctionInliner(std::string calling_func) : BlockRewriterBase(), calling_func_(calling_func) {};
+    FunctionInliner(scope_ptr s): BlockRewriterBase(s) {}
+
+    virtual void visit(Expression *e)            override;
+    virtual void visit(CallExpression *e)        override;
+    virtual void visit(ConserveExpression *e)    override;
+    virtual void visit(CompartmentExpression *e) override;
+    virtual void visit(LinearExpression *e)      override;
+    virtual void visit(AssignmentExpression* e)  override;
+    virtual void visit(BinaryExpression* e)      override;
+    virtual void visit(UnaryExpression* e)       override;
+    virtual void visit(IfExpression* e)          override;
+    virtual void visit(LocalDeclaration* e)      override;
+    virtual void visit(NumberExpression* e)      override {};
+    virtual void visit(IdentifierExpression* e)  override {};
 
     bool return_val_set() {return return_set_;};
+    bool finished_inlining() {return !inlining_executed_;};
 
     ~FunctionInliner() {}
 
 private:
-    std::string func_name_;
+    std::string inlining_func_, calling_func_;
     expression_ptr lhs_;
-    std::vector<std::string> fargs_;
-    std::vector<expression_ptr> cargs_;
+    std::map<std::string, expression_ptr> call_arg_map_;
+    std::map<std::string, expression_ptr> local_arg_map_;
     scope_ptr scope_;
-    bool return_set_ = false;
-
-    void replace_args(Expression* e);
-
-};
-
-class VariableReplacer : public Visitor {
-
-public:
-
-    VariableReplacer(std::string const& source, std::string const& target)
-    :   source_(source),
-        target_(target)
-    {}
 
-    void visit(Expression *e)           override;
-    void visit(UnaryExpression *e)      override;
-    void visit(BinaryExpression *e)     override;
-    void visit(NumberExpression *e)     override {};
+    // Tracks whether the return value of a function has been set
+    bool return_set_ = true;
 
-    ~VariableReplacer() {}
+    // Tracks whether a function is being inlined
+    bool inlining_in_progress_ = false;
 
-private:
-
-    std::string source_;
-    std::string target_;
-};
-
-class ValueInliner : public Visitor {
-
-public:
-
-    ValueInliner(std::string const& source, long double value)
-    :   source_(source),
-        value_(value)
-    {}
+    // Tracks whether a function has been inlined
+    bool inlining_executed_ = false;
 
-    void visit(Expression *e)           override;
-    void visit(UnaryExpression *e)      override;
-    void visit(BinaryExpression *e)     override;
-    void visit(NumberExpression *e)     override {};
-
-    ~ValueInliner() {}
-
-private:
+    void replace_args(Expression* e);
 
-    std::string source_;
-    long double value_;
-};
+protected:
+    virtual void reset() override {
+        inlining_func_.clear();
+        lhs_ = nullptr;
+        call_arg_map_.clear();
+        local_arg_map_.clear();
+        scope_.reset();
+        return_set_ = true;
+        inlining_in_progress_ = false;
+        inlining_executed_ = false;
+        BlockRewriterBase::reset();
+    }
+};
\ No newline at end of file
diff --git a/modcc/identifier.hpp b/modcc/identifier.hpp
index 6cf466361032b5eaad20d8099321fd65b4d169fb..ae1db14dfa38aeadd54e00882e1540279b6bf6e6 100644
--- a/modcc/identifier.hpp
+++ b/modcc/identifier.hpp
@@ -16,7 +16,8 @@ enum class moduleKind {
 enum class accessKind {
     read,
     write,
-    readwrite
+    readwrite,
+    noaccess
 };
 
 /// describes the scope of a variable
diff --git a/modcc/module.cpp b/modcc/module.cpp
index 279fc357452e905693f940cd8c2f3b112ebf8869..9938a8636448262e0d2dfc16669ea27362f3fcd2 100644
--- a/modcc/module.cpp
+++ b/modcc/module.cpp
@@ -48,20 +48,40 @@ class NrnCurrentRewriter: public BlockRewriterBase {
     }
 
     bool has_current_update_ = false;
-    std::set<std::string> ion_current_vars_;
+    std::set<std::string> current_vars_;
+    std::set<expression_ptr> conductivity_exps_;
 
 public:
     using BlockRewriterBase::visit;
 
     virtual void finalize() override {
         if (has_current_update_) {
-            // Initialize conductivity_ as first statement.
-            statements_.push_front(make_expression<AssignmentExpression>(loc_,
-                    id("conductivity_"),
-                    make_expression<NumberExpression>(loc_, 0.0)));
-            statements_.push_front(make_expression<AssignmentExpression>(loc_,
-                    id("current_"),
-                    make_expression<NumberExpression>(loc_, 0.0)));
+            expression_ptr current_sum, conductivity_sum;
+            for (auto& curr: current_vars_) {
+                auto curr_id = make_expression<IdentifierExpression>(Location{}, curr);
+                if (!current_sum) {
+                    current_sum = std::move(curr_id);
+                } else {
+                    current_sum = make_expression<AddBinaryExpression>(
+                            Location{}, std::move(current_sum), std::move(curr_id));
+                }
+            }
+            for (auto& cond: conductivity_exps_) {
+                if (!conductivity_sum) {
+                    conductivity_sum = cond->clone();
+                } else {
+                    conductivity_sum = make_expression<AddBinaryExpression>(
+                            Location{}, std::move(conductivity_sum), cond->clone());
+                }
+            }
+            if (current_sum) {
+                statements_.push_back(make_expression<AssignmentExpression>(loc_,
+                        id("current_"), std::move(current_sum)));
+            }
+            if (conductivity_sum) {
+                statements_.push_back(make_expression<AssignmentExpression>(loc_,
+                        id("conductivity_"), std::move(conductivity_sum)));
+            }
         }
     }
 
@@ -69,22 +89,13 @@ public:
     virtual void visit(ConductanceExpression *e) override {}
     virtual void visit(AssignmentExpression *e) override {
         statements_.push_back(e->clone());
-        auto loc = e->location();
 
         sourceKind current_source = current_update(e);
         if (current_source != sourceKind::no_source) {
             has_current_update_ = true;
 
-            if (current_source==sourceKind::ion_current_density || current_source==sourceKind::ion_current) {
-                ion_current_vars_.insert(e->lhs()->is_identifier()->name());
-            }
-            else {
-                // A 'nonspecific' current contribution.
-                // Remove data source; currents accumulated into `current_` instead.
-
-                e->lhs()->is_identifier()->symbol()->is_local_variable()
-                    ->external_variable()->data_source(sourceKind::no_source);
-            }
+            auto visited_current = current_vars_.count(e->lhs()->is_identifier()->name());
+            current_vars_.insert(e->lhs()->is_identifier()->name());
 
             linear_test_result L = linear_test(e->rhs(), {"v"});
             if (!L.is_linear) {
@@ -93,17 +104,8 @@ public:
                 return;
             }
             else {
-                statements_.push_back(make_expression<AssignmentExpression>(loc,
-                    id("current_", loc),
-                    make_expression<AddBinaryExpression>(loc,
-                        id("current_", loc),
-                        e->lhs()->clone())));
-                if (L.coef.count("v")) {
-                    statements_.push_back(make_expression<AssignmentExpression>(loc,
-                        id("conductivity_", loc),
-                        make_expression<AddBinaryExpression>(loc,
-                            id("conductivity_", loc),
-                            L.coef.at("v")->clone())));
+                if (L.coef.count("v") && !visited_current) {
+                    conductivity_exps_.insert(L.coef.at("v")->clone());
                 }
             }
         }
@@ -280,6 +282,9 @@ bool Module::semantic() {
 
     auto& init_body = api_init->body()->statements();
 
+    api_init->semantic(symbols_);
+    scope_ptr nrn_init_scope = api_init->scope();
+
     for(auto& e : *proc_init->body()) {
         auto solve_expression = e->is_solve_statement();
         if (solve_expression) {
@@ -292,11 +297,17 @@ bool Module::semantic() {
 
             if (solve_proc->kind() == procedureKind::linear) {
                 solver = std::make_unique<LinearSolverVisitor>(state_vars);
-                linear_rewrite(solve_proc->body(), state_vars)->accept(solver.get());
+                auto rewrite_body = linear_rewrite(solve_proc->body(), state_vars);
+
+                rewrite_body->semantic(nrn_init_scope);
+                rewrite_body->accept(solver.get());
             } else if (solve_proc->kind() == procedureKind::kinetic &&
                        solve_expression->variant() == solverVariant::steadystate) {
                 solver = std::make_unique<SparseSolverVisitor>(solverVariant::steadystate);
-                kinetic_rewrite(solve_proc->body())->accept(solver.get());
+                auto rewrite_body = kinetic_rewrite(solve_proc->body());
+
+                rewrite_body->semantic(nrn_init_scope);
+                rewrite_body->accept(solver.get());
             } else {
                 error("A SOLVE expression in an INITIAL block can only be used to solve a "
                       "LINEAR block or a KINETIC block at steadystate and " +
@@ -352,6 +363,10 @@ bool Module::semantic() {
 
         for(auto& e: (breakpoint->body()->statements())) {
             SolveExpression* solve_expression = e->is_solve_statement();
+            LocalDeclaration* local_expression = e->is_local_declaration();
+            if(local_expression) {
+                continue;
+            }
             if(!solve_expression) {
                 found_non_solve = true;
                 continue;
@@ -398,11 +413,15 @@ bool Module::semantic() {
                     solver = std::make_unique<SparseNonlinearSolverVisitor>();
                 }
 
+                rewrite_body->semantic(nrn_state_scope);
                 rewrite_body->accept(solver.get());
             }
             else if (deriv->kind()==procedureKind::linear) {
                 solver = std::make_unique<LinearSolverVisitor>(state_vars);
-                linear_rewrite(deriv->body(), state_vars)->accept(solver.get());
+                auto rewrite_body = linear_rewrite(deriv->body(), state_vars);
+
+                rewrite_body->semantic(nrn_state_scope);
+                rewrite_body->accept(solver.get());
             }
             else {
                 deriv->body()->accept(solver.get());
@@ -667,12 +686,11 @@ void Module::add_variables_to_symbols() {
     // Nonspecific current variables are represented by an indexed variable
     // with a 'current' data source. Assignments in the NrnCurrent block will
     // later be rewritten so that these contributions are accumulated in `current_`
-    // (potentially saving some weight multiplications); at that point the
-    // data source for the nonspecific current variable will be reset to 'no_source'.
+    // (potentially saving some weight multiplications);
 
     if( neuron_block_.has_nonspecific_current() ) {
         auto const& i = neuron_block_.nonspecific_current;
-        create_indexed_variable(i.spelling, sourceKind::current, accessKind::write, "", i.location);
+        create_indexed_variable(i.spelling, current_kind, accessKind::noaccess, "", i.location);
     }
 
     for(auto const& ion : neuron_block_.ions) {
@@ -732,9 +750,6 @@ void Module::add_variables_to_symbols() {
 }
 
 int Module::semantic_func_proc() {
-    bool keep_inlining = true;
-    int errors = 0;
-
     ////////////////////////////////////////////////////////////////////////////
     // now iterate over the functions and procedures and perform semantic
     // analysis on each. This includes
@@ -742,128 +757,90 @@ int Module::semantic_func_proc() {
     //  -   generate local variable table for each function/procedure
     //  -   inlining function calls
     ////////////////////////////////////////////////////////////////////////////
-
-    while (keep_inlining) {
-    #ifdef LOGGING
-        std::cout << white("===================================\n");
+#ifdef LOGGING
+    std::cout << white("===================================\n");
         std::cout << cyan("        Function Inlining\n");
         std::cout << white("===================================\n");
-    #endif
-        keep_inlining = false;
-
-        for (auto& e: symbols_) {
-            auto& s = e.second;
-            if(s->kind() == symbolKind::procedure || s->kind() == symbolKind::function) {
-
-    #ifdef LOGGING
-                std::cout << "\nfunction inlining for " << s->location() << "\n"
-                          << s->to_string() << "\n"
-                          << green("\n-call site lowering-\n\n");
-    #endif
-
-                // perform semantic analysis
-                s->semantic(symbols_);
-
-                // then use an error visitor to print out all the semantic errors
-                ErrorVisitor v(source_name());
-                s->accept(&v);
-                errors += v.num_errors();
-
-                // inline function calls
-                // this requires that the symbol table has already been built
-                if (v.num_errors() == 0) {
-                    auto &b = s->kind() == symbolKind::function ?
-                              s->is_function()->body()->statements() :
-                              s->is_procedure()->body()->statements();
-
-                    // lower function call sites so that all function calls are of
-                    // the form : variable = call(<args>)
-                    // e.g.
-                    //      a = 2 + foo(2+x, y, 1)
-                    // becomes
-                    //      ll0_ = foo(2+x, y, 1)
-                    //      a = 2 + ll0_
-                    for (auto e = b.begin(); e != b.end(); ++e) {
-                        b.splice(e, lower_function_calls((*e).get()));
-                    }
-    #ifdef LOGGING
-                    std::cout << "body after call site lowering\n";
-                        for(auto& l : b) std::cout << "  " << l->to_string() << " @ " << l->location() << "\n";
-                        std::cout << green("\n-argument lowering-\n\n");
-    #endif
-                    // lower function arguments that are not identifiers or literals
-                    // e.g.
-                    //      ll0_ = foo(2+x, y, 1)
-                    //      a = 2 + ll0_
-                    // becomes
-                    //      ll1_ = 2+x
-                    //      ll0_ = foo(ll1_, y, 1)
-                    //      a = 2 + ll0_
-                    for (auto e = b.begin(); e != b.end(); ++e) {
-                        if (auto be = (*e)->is_binary()) {
-                            // only apply to assignment expressions where rhs is a
-                            // function call because the function call lowering step
-                            // above ensures that all function calls are of this form
-                            if (auto rhs = be->rhs()->is_function_call()) {
-                                b.splice(e, lower_function_arguments(rhs->args()));
-                            }
-                        }
-                    }
-
-    #ifdef LOGGING
-                    std::cout << "body after argument lowering\n";
-                        for(auto& l : b) std::cout << "  " << l->to_string() << " @ " << l->location() << "\n";
-                        std::cout << green("\n-inlining-\n\n");
-    #endif
-                }
+#endif
+    for (auto& e: symbols_) {
+        auto& s = e.second;
+        if(s->kind() == symbolKind::procedure || s->kind() == symbolKind::function) {
+            // perform semantic analysis
+            s->semantic(symbols_);
+#ifdef LOGGING
+            std::cout << "\nfunction lowering for " << s->location() << "\n"
+                      << s->to_string() << "\n\n";
+#endif
+
+            if (s->kind() == symbolKind::function) {
+                auto rewritten = lower_functions(s->is_function()->body());
+                s->is_function()->body(std::move(rewritten));
+            } else {
+                auto rewritten = lower_functions(s->is_procedure()->body());
+                s->is_procedure()->body(std::move(rewritten));
             }
+#ifdef LOGGING
+            std::cout << "body after function lowering\n"
+                      << s->to_string() << "\n\n";
+#endif
         }
+    }
 
-        for(auto& e : symbols_) {
-            auto& s = e.second;
-
-            if(s->kind() == symbolKind::procedure)
-            {
-                if(errors==0) {
-                    auto &b = s->kind()==symbolKind::function ?
-                        s->is_function()->body()->statements() :
-                        s->is_procedure()->body()->statements();
-
-                    // Do the inlining: supports multiline functions and if/else statements
-                    // e.g. if the function foo in the examples above is defined as follows
-                    //
-                    //  function foo(a, b, c) {
-                    //      Local t = b + c
-                    //      foo = a*t
-                    //  }
-                    //
-                    // the full inlined example is
-                    //      ll1_ = 2+x
-                    //      r_0_ = y+1
-                    //      ll0_ = ll1_*r_0_
-                    //      a = 2 + ll0_
-
-                    for (auto &e: b) {
-                        if (auto ass = e->is_assignment()) {
-                            if (ass->rhs()->is_function_call()) {
-                                e = inline_function_call(e);
-                                keep_inlining = true;
-                            }
-                        }
-                    }
+    auto inline_and_simplify = [&](auto&& caller) {
+        auto rewritten = inline_function_calls(caller->name(), caller->body());
+        caller->body(std::move(rewritten));
+        caller->body(constant_simplify(caller->body()));
+    };
 
+    // First, inline all function calls inside the bodies of each function
+    // This catches recursions
+    for(auto& e : symbols_) {
+        auto& s = e.second;
+
+        if (s->kind() == symbolKind::function) {
+            // perform semantic analysis
+            s->semantic(symbols_);
+#ifdef LOGGING
+            std::cout << "function inlining for " << s->location() << "\n"
+                      << s->to_string() << "\n\n";
+#endif
+            inline_and_simplify(s->is_function());
+            s->semantic(symbols_);
+#ifdef LOGGING
+            std::cout << "body after inlining\n"
+                      << s->to_string() << "\n\n";
+#endif
+        }
+    }
 
-    #ifdef LOGGING
-                    std::cout << "body after inlining\n";
-                    for(auto& l : b) std::cout << "  " << l->to_string() << " @ " << l->location() << "\n";
-    #endif
-                    // Finally, run a constant simplification pass.
-                    if (auto proc = s->is_procedure()) {
-                        proc->body(constant_simplify(proc->body()));
-                        s->semantic(symbols_);
-                    }
-                }
-            }
+    // Once all functions are inlined internally; we can inline
+    // function calls in the bodies of procedures
+    for(auto& e : symbols_) {
+        auto& s = e.second;
+
+        if(s->kind() == symbolKind::procedure) {
+            // perform semantic analysis
+            s->semantic(symbols_);
+#ifdef LOGGING
+            std::cout << "function inlining for " << s->location() << "\n"
+                      << s->to_string() << "\n\n";
+#endif
+            inline_and_simplify(s->is_procedure());
+            s->semantic(symbols_);
+#ifdef LOGGING
+            std::cout << "body after inlining\n"
+                      << s->to_string() << "\n\n";
+#endif
+        }
+    }
+
+    int errors = 0;
+    for(auto& e : symbols_) {
+        auto& s = e.second;
+        if(s->kind() == symbolKind::procedure) {
+            ErrorVisitor v(source_name());
+            s->accept(&v);
+            errors += v.num_errors();
         }
     }
     return errors;
@@ -907,5 +884,3 @@ void Module::check_revpot_mechanism() {
 
     kind_ = moduleKind::revpot;
 }
-
-
diff --git a/modcc/parser.cpp b/modcc/parser.cpp
index bb59db9ebcd1fb3b01801cc56d96c56a076ab0bb..ed776735c544ecd1092b1575ff0a7ce75d550509 100644
--- a/modcc/parser.cpp
+++ b/modcc/parser.cpp
@@ -1643,7 +1643,10 @@ expression_ptr Parser::parse_if() {
 
         // handle 'else if {}' case recursively
         if(token_.type == tok::if_stmt) {
-            false_branch = parse_if();
+            expr_list_type if_block;
+            auto exp = parse_if();
+            if_block.push_back(std::move(exp));
+            false_branch = make_expression<BlockExpression>(Location(), std::move(if_block), true);
         }
         // we have a closing 'else {}'
         else if(token_.type == tok::lbrace) {
diff --git a/modcc/solvers.cpp b/modcc/solvers.cpp
index cd792ab092812af40ca33b2961d398611f61c302..a636c9eef246b61c2b9d2cb900a28458048048d7 100644
--- a/modcc/solvers.cpp
+++ b/modcc/solvers.cpp
@@ -876,7 +876,9 @@ public:
     virtual void visit(IfExpression* e) override {
         e->condition()->accept(this);
         e->true_branch()->accept(this);
-        e->false_branch()->accept(this);
+        if (e->false_branch()) {
+            e->false_branch()->accept(this);
+        }
     }
 
     virtual void visit(IdentifierExpression* e) override {
diff --git a/test/unit-modcc/mod_files/test6.mod b/test/unit-modcc/mod_files/test6.mod
index 70b8431997f8fc9c8c3d9deb6a5d24ec022e1c50..e94889985683a998076a3c577026d52002a44927 100644
--- a/test/unit-modcc/mod_files/test6.mod
+++ b/test/unit-modcc/mod_files/test6.mod
@@ -25,13 +25,16 @@ BREAKPOINT {
     rates(delta)
     s0 = s1 * s2
 }
+FUNCTION iden(x) {
+    iden = x
+}
 
 FUNCTION foo(x, y) {
     LOCAL temp
     if (x == 3) {
         foo = 2 * y
     } else if (x == 4) {
-        foo = y
+        foo = y * iden(6 + x)
     } else {
         temp = exp(y)
         foo = temp * x
diff --git a/test/unit-modcc/test_parser.cpp b/test/unit-modcc/test_parser.cpp
index c2c68a61ab5bef75341bbd856e0818a11d51e3b7..4e86bf3c6e60f742d7d2d2ff33e2001fcd665959 100644
--- a/test/unit-modcc/test_parser.cpp
+++ b/test/unit-modcc/test_parser.cpp
@@ -265,8 +265,10 @@ TEST(Parser, parse_if) {
         EXPECT_NE(s->condition()->is_unary(), nullptr);
         EXPECT_NE(s->true_branch()->is_block(), nullptr);
         ASSERT_NE(s->false_branch(), nullptr);
-        ASSERT_NE(s->false_branch()->is_if(), nullptr);
-        EXPECT_EQ(s->false_branch()->is_if()->false_branch(), nullptr);
+        ASSERT_NE(s->false_branch()->is_block(), nullptr);
+
+        auto false_if_branch = s->false_branch()->is_block()->statements().front()->clone();
+        EXPECT_EQ(false_if_branch->is_if()->false_branch(), nullptr);
     }
 }
 
diff --git a/test/unit-modcc/test_printers.cpp b/test/unit-modcc/test_printers.cpp
index bee760b7e01cc4868e52367ae7bf2cedebcbfc5b..de50a5f3423cff7f374111e14b951b498d06118c 100644
--- a/test/unit-modcc/test_printers.cpp
+++ b/test/unit-modcc/test_printers.cpp
@@ -200,37 +200,44 @@ TEST(CPrinter, proc_body_const) {
 
 TEST(CPrinter, proc_body_inlined) {
     const char* expected =
-        "    r_0_ = s2[i_]/ 3;\n"
-        "    r_3_ = s1[i_]+ 2;\n"
-        "    if (s1[i_]== 3) {\n"
-        "        r_2_ =  2*r_3_;\n"
-        "    }\n"
-        "    else if (s1[i_]== 4) {\n"
-        "        r_2_ = r_3_;\n"
-        "    }\n"
-        "    else {\n"
-        "        r_5_ = exp(r_3_);\n"
-        "        r_2_ = r_5_*s1[i_];\n"
-        "    }\n"
-        "\n"
-        "    r_7_ = r_0_/s2[i_];\n"
-        "    r_8_ = log(r_7_);\n"
-        "    r_6_ =  42*r_8_;\n"
-        "    r_1_ = r_0_*r_6_;\n"
-        "    t0 = r_2_*r_1_;\n"
-        "    t1 = exprelr(t0);\n"
-        "    ll0_ = t1+ 2;\n"
-        "    if (ll0_== 3) {\n"
-        "        t2 =  10;\n"
-        "    }\n"
-        "    else if (ll0_== 4) {\n"
-        "        t2 =  5;\n"
-        "    }\n"
-        "    else {\n"
-        "        r_4_ =  148.4131591025766;\n"
-        "        t2 = r_4_*ll0_;\n"
-        "    }\n"
-        "    s2[i_] = t2+ 4;";
+        "r_9_=s2[i_]/3;\n"
+        "r_8_=s1[i_]+2;\n"
+        "if(s1[i_]==3){\n"
+        "   r_7_=2*r_8_;\n"
+        "}\n"
+        "else{\n"
+        "   if(s1[i_]==4){\n"
+        "       r_12_=6+s1[i_];\n"
+        "       r_11_=r_12_;\n"
+        "       r_7_=r_8_*r_11_;\n"
+        "   }\n"
+        "   else{\n"
+        "       r_10_=exp(r_8_);\n"
+        "       r_7_=r_10_*s1[i_];\n"
+        "   }\n"
+        "}\n"
+        "r_14_=r_9_/s2[i_];\n"
+        "r_15_=log(r_14_);\n"
+        "r_13_=42*r_15_;\n"
+        "r_6_=r_9_*r_13_;\n"
+        "t0=r_7_*r_6_;\n"
+        "t1=exprelr(t0);\n"
+        "ll0_=t1+2;\n"
+        "if(ll0_==3){\n"
+        "   t2=10;\n"
+        "}\n"
+        "else{\n"
+        "   if(ll0_==4){\n"
+        "       r_18_=6+ll0_;\n"
+        "       r_17_=r_18_;\n"
+        "       t2=5*r_17_;\n"
+        "   }\n"
+        "   else{\n"
+        "       r_16_=148.4131591025766;\n"
+        "       t2=r_16_*ll0_;\n"
+        "   }\n"
+        "}\n"
+        "s2[i_]=t2+4;\n";
 
     Module m(io::read_all(DATADIR "/mod_files/test6.mod"), "test6.mod");
     Parser p(m, false);