Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/AST/Convert.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -567,4 +567,10 @@
 	}
 
+    const ast::WhenClause * visit( const ast::WhenClause * node ) override final {
+		// There is no old-AST WhenClause, so this should never be called.
+		assert( !node );
+		return nullptr;
+	}
+
 	const ast::Stmt * visit( const ast::WaitForStmt * node ) override final {
 		if ( inCache( node ) ) return nullptr;
@@ -573,9 +579,9 @@
 		for ( auto clause : node->clauses ) {
 			stmt->clauses.push_back({{
-					get<Expression>().accept1( clause->target_func ),
+					get<Expression>().accept1( clause->target ),
 					get<Expression>().acceptL( clause->target_args ),
 				},
 				get<Statement>().accept1( clause->stmt ),
-				get<Expression>().accept1( clause->cond ),
+				get<Expression>().accept1( clause->when_cond ),
 			});
 		}
@@ -594,4 +600,10 @@
 	const ast::WaitForClause * visit( const ast::WaitForClause * node ) override final {
 		// There is no old-AST WaitForClause, so this should never be called.
+		assert( !node );
+		return nullptr;
+	}
+
+    const ast::Stmt * visit( const ast::WaitUntilStmt * node ) override final {
+        // There is no old-AST WaitUntilStmt, so this should never be called.
 		assert( !node );
 		return nullptr;
@@ -2158,8 +2170,8 @@
 			auto clause = new ast::WaitForClause( old->location );
 
-			clause->target_func = GET_ACCEPT_1(clauses[i].target.function, Expr);
+			clause->target = GET_ACCEPT_1(clauses[i].target.function, Expr);
 			clause->target_args = GET_ACCEPT_V(clauses[i].target.arguments, Expr);
 			clause->stmt = GET_ACCEPT_1(clauses[i].statement, Stmt);
-			clause->cond = GET_ACCEPT_1(clauses[i].condition, Expr);
+			clause->when_cond = GET_ACCEPT_1(clauses[i].condition, Expr);
 
 			stmt->clauses.push_back( clause );
Index: src/AST/Fwd.hpp
===================================================================
--- src/AST/Fwd.hpp	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/AST/Fwd.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -58,6 +58,8 @@
 class FinallyClause;
 class SuspendStmt;
+class WhenClause;
 class WaitForStmt;
 class WaitForClause;
+class WaitUntilStmt;
 class WithStmt;
 class DeclStmt;
Index: src/AST/Node.cpp
===================================================================
--- src/AST/Node.cpp	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/AST/Node.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -174,8 +174,12 @@
 template class ast::ptr_base< ast::FinallyClause, ast::Node::ref_type::weak >;
 template class ast::ptr_base< ast::FinallyClause, ast::Node::ref_type::strong >;
+template class ast::ptr_base< ast::WhenClause, ast::Node::ref_type::weak >;
+template class ast::ptr_base< ast::WhenClause, ast::Node::ref_type::strong >;
 template class ast::ptr_base< ast::WaitForStmt, ast::Node::ref_type::weak >;
 template class ast::ptr_base< ast::WaitForStmt, ast::Node::ref_type::strong >;
 template class ast::ptr_base< ast::WaitForClause, ast::Node::ref_type::weak >;
 template class ast::ptr_base< ast::WaitForClause, ast::Node::ref_type::strong >;
+template class ast::ptr_base< ast::WaitUntilStmt, ast::Node::ref_type::weak >;
+template class ast::ptr_base< ast::WaitUntilStmt, ast::Node::ref_type::strong >;
 template class ast::ptr_base< ast::WithStmt, ast::Node::ref_type::weak >;
 template class ast::ptr_base< ast::WithStmt, ast::Node::ref_type::strong >;
Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/AST/Pass.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -162,6 +162,8 @@
 	const ast::FinallyClause *    visit( const ast::FinallyClause        * ) override final;
 	const ast::Stmt *             visit( const ast::SuspendStmt          * ) override final;
+    const ast::WhenClause *       visit( const ast::WhenClause           * ) override final;
 	const ast::Stmt *             visit( const ast::WaitForStmt          * ) override final;
 	const ast::WaitForClause *    visit( const ast::WaitForClause        * ) override final;
+    const ast::Stmt *             visit( const ast::WaitUntilStmt        * ) override final;
 	const ast::Decl *             visit( const ast::WithStmt             * ) override final;
 	const ast::NullStmt *         visit( const ast::NullStmt             * ) override final;
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/AST/Pass.impl.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -1066,4 +1066,19 @@
 
 //--------------------------------------------------------------------------
+// WhenClause
+template< typename core_t >
+const ast::WhenClause * ast::Pass< core_t >::visit( const ast::WhenClause * node ) {
+	VISIT_START( node );
+
+	if ( __visit_children() ) {
+		maybe_accept( node, &WhenClause::target );
+		maybe_accept( node, &WhenClause::stmt );
+		maybe_accept( node, &WhenClause::when_cond );
+	}
+
+	VISIT_END( WhenClause, node );
+}
+
+//--------------------------------------------------------------------------
 // WaitForStmt
 template< typename core_t >
@@ -1090,11 +1105,29 @@
 
 	if ( __visit_children() ) {
-		maybe_accept( node, &WaitForClause::target_func );
+		maybe_accept( node, &WaitForClause::target );
 		maybe_accept( node, &WaitForClause::target_args );
 		maybe_accept( node, &WaitForClause::stmt );
-		maybe_accept( node, &WaitForClause::cond );
+		maybe_accept( node, &WaitForClause::when_cond );
 	}
 
 	VISIT_END( WaitForClause, node );
+}
+
+//--------------------------------------------------------------------------
+// WaitUntilStmt
+template< typename core_t >
+const ast::Stmt * ast::Pass< core_t >::visit( const ast::WaitUntilStmt * node ) {
+	VISIT_START( node );
+
+	if ( __visit_children() ) {
+		maybe_accept( node, &WaitUntilStmt::clauses );
+		maybe_accept( node, &WaitUntilStmt::timeout_time );
+		maybe_accept( node, &WaitUntilStmt::timeout_stmt );
+		maybe_accept( node, &WaitUntilStmt::timeout_cond );
+		maybe_accept( node, &WaitUntilStmt::else_stmt );
+		maybe_accept( node, &WaitUntilStmt::else_cond );
+	}
+
+	VISIT_END( Stmt, node );
 }
 
Index: src/AST/Print.cpp
===================================================================
--- src/AST/Print.cpp	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/AST/Print.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -208,4 +208,31 @@
 	}
 
+    void print( const ast::WaitStmt * node ) {
+		if ( node->timeout_time ) {
+			os << indent-1 << "timeout of:" << endl;
+			node->timeout_time->accept( *this );
+
+			if ( node->timeout_stmt ) {
+				os << indent-1 << "... with statment:" << endl;
+				node->timeout_stmt->accept( *this );
+			}
+
+			if ( node->timeout_cond ) {
+				os << indent-1 << "... with condition:" << endl;
+				node->timeout_cond->accept( *this );
+			}
+		}
+
+		if ( node->else_stmt ) {
+			os << indent-1 << "else:" << endl;
+			node->else_stmt->accept( *this );
+
+			if ( node->else_cond ) {
+				os << indent-1 << "... with condition:" << endl;
+				node->else_cond->accept( *this );
+			}
+		}
+	}
+
 	void preprint( const ast::NamedTypeDecl * node ) {
 		if ( ! node->name.empty() ) {
@@ -756,4 +783,21 @@
 	}
 
+	virtual const ast::WhenClause * visit( const ast::WhenClause * node ) override final {
+		os << indent-1 << "target: ";
+		safe_print( node->target );
+
+		if ( node->stmt ) {
+			os << indent-1 << "... with statment:" << endl;
+			node->stmt->accept( *this );
+		}
+
+		if ( node->when_cond ) {
+			os << indent-1 << "... with when condition:" << endl;
+			node->when_cond->accept( *this );
+		}
+
+		return node;
+	}
+
 	virtual const ast::Stmt * visit( const ast::WaitForStmt * node ) override final {
 		os << "Waitfor Statement" << endl;
@@ -793,5 +837,5 @@
 	virtual const ast::WaitForClause * visit( const ast::WaitForClause * node ) override final {
 		os << indent-1 << "target function: ";
-		safe_print( node->target_func );
+		safe_print( node->target );
 
 		if ( !node->target_args.empty() ) {
@@ -807,9 +851,19 @@
 		}
 
-		if ( node->cond ) {
+		if ( node->when_cond ) {
 			os << indent-1 << "... with condition:" << endl;
-			node->cond->accept( *this );
-		}
-
+			node->when_cond->accept( *this );
+		}
+
+		return node;
+	}
+
+    virtual const ast::Stmt * visit( const ast::WaitUntilStmt * node ) override final {
+		os << "Waituntil Statement" << endl;
+		indent += 2;
+		for( const auto & clause : node->clauses ) {
+			clause->accept( *this );
+		}
+        print(node);    // calls print( const ast::WaitStmt * node )
 		return node;
 	}
Index: src/AST/Stmt.hpp
===================================================================
--- src/AST/Stmt.hpp	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/AST/Stmt.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -375,9 +375,9 @@
 };
 
-// Waitfor statement: when (...) waitfor (... , ...) ... timeout(...) ... else ...
-class WaitForStmt final : public Stmt {
-  public:
-	std::vector<ptr<WaitForClause>> clauses;
-	ptr<Expr> timeout_time;
+// Base class of WaitFor/WaitUntil statements
+// form: KEYWORD(...) ... timeout(...) ... else ...
+class WaitStmt : public Stmt { 
+  public:
+    ptr<Expr> timeout_time;
 	ptr<Stmt> timeout_stmt;
 	ptr<Expr> timeout_cond;
@@ -385,6 +385,36 @@
 	ptr<Expr> else_cond;
 
+    WaitStmt( const CodeLocation & loc, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)) {}
+
+  private:
+    WaitStmt * clone() const override = 0;
+	MUTATE_FRIEND
+};
+
+// Base class for WaitFor/WaitUntil clauses
+// form: when( when_cond ) KEYWORD( target ) stmt
+class WhenClause : public StmtClause {
+  public:
+	ptr<Expr> target;
+	ptr<Stmt> stmt;
+	ptr<Expr> when_cond;
+
+	WhenClause( const CodeLocation & loc )
+		: StmtClause( loc ) {}
+
+	const WhenClause * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
+	WhenClause * clone() const override { return new WhenClause{ *this }; }
+	MUTATE_FRIEND
+};
+
+// Waitfor statement: when (...) waitfor (... , ...) ... timeout(...) ... else ...
+class WaitForStmt final : public WaitStmt {
+  public:
+	std::vector<ptr<WaitForClause>> clauses;
+
 	WaitForStmt( const CodeLocation & loc, const std::vector<Label> && labels = {} )
-		: Stmt(loc, std::move(labels)) {}
+		: WaitStmt(loc, std::move(labels)) {}
 
 	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
@@ -394,17 +424,60 @@
 };
 
-class WaitForClause final : public StmtClause {
-  public:
-	ptr<Expr> target_func;
+class WaitForClause final : public WhenClause {
+  public:
 	std::vector<ptr<Expr>> target_args;
-	ptr<Stmt> stmt;
-	ptr<Expr> cond;
 
 	WaitForClause( const CodeLocation & loc )
-		: StmtClause( loc ) {}
+		: WhenClause( loc ) {}
 
 	const WaitForClause * accept( Visitor & v ) const override { return v.visit( this ); }
   private:
 	WaitForClause * clone() const override { return new WaitForClause{ *this }; }
+	MUTATE_FRIEND
+};
+
+// waituntil statement: when (...) waituntil (...) ... timeout(...) ... else ...
+class WaitUntilStmt final : public WaitStmt {
+  public:
+    // Non-ast node used during compilation to store data needed to generate predicates
+    //    and set initial status values for clauses
+    // Used to create a tree corresponding to the structure of the clauses in a WaitUntil
+    struct ClauseNode { 
+        enum Op { AND, OR, LEFT_OR, LEAF, ELSE, TIMEOUT } op; // operation/type tag
+        // LEFT_OR used with TIMEOUT/ELSE to indicate that we ignore right hand side after parsing
+
+        ClauseNode * left;
+        ClauseNode * right;
+        WhenClause * leaf;  // only set if this node is a leaf (points into vector of clauses)
+
+        bool ambiguousWhen; // used to paint nodes of predicate tree based on when() clauses
+        bool whenState;     // used to track if when_cond is toggled on or off for generating init values
+        bool childOfAnd;      // true on leaf nodes that are children of AND, false otherwise
+
+        ClauseNode( Op op, ClauseNode * left, ClauseNode * right )
+            : op(op), left(left), right(right), leaf(nullptr), 
+            ambiguousWhen(false), whenState(true), childOfAnd(false) {}
+        ClauseNode( Op op, WhenClause * leaf )
+            : op(op), left(nullptr), right(nullptr), leaf(leaf),
+            ambiguousWhen(false), whenState(true), childOfAnd(false) {}
+        ClauseNode( WhenClause * leaf ) : ClauseNode(LEAF, leaf) {}
+        
+        ~ClauseNode() {
+            if ( left ) delete left;
+            if ( right ) delete right;
+        }
+    };
+
+	std::vector<ptr<WhenClause>> clauses;
+    ClauseNode * predicateTree;
+
+	WaitUntilStmt( const CodeLocation & loc, const std::vector<Label> && labels = {} )
+		: WaitStmt(loc, std::move(labels)) {}
+
+    ~WaitUntilStmt() { delete predicateTree; }
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
+	WaitUntilStmt * clone() const override { return new WaitUntilStmt{ *this }; }
 	MUTATE_FRIEND
 };
Index: src/AST/Visitor.hpp
===================================================================
--- src/AST/Visitor.hpp	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/AST/Visitor.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -50,6 +50,8 @@
     virtual const ast::FinallyClause *    visit( const ast::FinallyClause        * ) = 0;
     virtual const ast::Stmt *             visit( const ast::SuspendStmt          * ) = 0;
+    virtual const ast::WhenClause *       visit( const ast::WhenClause           * ) = 0;
     virtual const ast::Stmt *             visit( const ast::WaitForStmt          * ) = 0;
     virtual const ast::WaitForClause *    visit( const ast::WaitForClause        * ) = 0;
+    virtual const ast::Stmt *             visit( const ast::WaitUntilStmt        * ) = 0;
     virtual const ast::Decl *             visit( const ast::WithStmt             * ) = 0;
     virtual const ast::NullStmt *         visit( const ast::NullStmt             * ) = 0;
Index: src/Common/CodeLocationTools.cpp
===================================================================
--- src/Common/CodeLocationTools.cpp	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/Common/CodeLocationTools.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -128,6 +128,8 @@
     macro(FinallyClause, FinallyClause) \
     macro(SuspendStmt, Stmt) \
+    macro(WhenClause, WhenClause) \
     macro(WaitForStmt, Stmt) \
     macro(WaitForClause, WaitForClause) \
+    macro(WaitUntilStmt, Stmt) \
     macro(WithStmt, Decl) \
     macro(NullStmt, NullStmt) \
Index: src/Concurrency/WaitforNew.cpp
===================================================================
--- src/Concurrency/WaitforNew.cpp	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/Concurrency/WaitforNew.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -305,10 +305,10 @@
 
 	const ast::VariableExpr * variableExpr =
-		clause->target_func.as<ast::VariableExpr>();
+		clause->target.as<ast::VariableExpr>();
 	ast::Expr * castExpr = new ast::CastExpr(
 		location,
 		new ast::CastExpr(
 			location,
-			clause->target_func,
+			clause->target,
 			ast::deepCopy( variableExpr->result.get() ),
 			ast::GeneratedCast ),
@@ -325,7 +325,7 @@
 
 	ResolveContext context{ symtab, transUnit().global };
-	out->push_back( maybeCond( location, clause->cond.get(), {
+	out->push_back( maybeCond( location, clause->when_cond.get(), {
 		makeAccStmt( location, acceptables, index, "is_dtor",
-			detectIsDtor( location, clause->target_func ), context ),
+			detectIsDtor( location, clause->target ), context ),
 		makeAccStmt( location, acceptables, index, "func",
 			funcExpr, context ),
Index: src/Concurrency/Waituntil.cpp
===================================================================
--- src/Concurrency/Waituntil.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/Concurrency/Waituntil.cpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -0,0 +1,1413 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// WaitforNew.cpp -- Expand waitfor clauses into code.
+//
+// Author           : Andrew Beach
+// Created On       : Fri May 27 10:31:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Jun 13 13:30:00 2022
+// Update Count     : 0
+//
+
+#include <string>
+
+#include "Waituntil.hpp"
+#include "AST/Expr.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Print.hpp"
+#include "AST/Stmt.hpp"
+#include "AST/Type.hpp"
+#include "Common/UniqueName.h"
+
+using namespace ast;
+using namespace std;
+
+/* So this is what this pass dones:
+{
+    when ( condA ) waituntil( A ){ doA(); } 
+    or when ( condB ) waituntil( B ){ doB(); } 
+    and when ( condC ) waituntil( C ) { doC(); }
+}
+		 ||
+		 ||
+		\||/
+		 \/
+
+Generates these two routines:
+static inline bool is_full_sat_1( int * clause_statuses ) {
+    return clause_statuses[0] 
+        || clause_statuses[1]
+        && clause_statuses[2];
+}
+
+static inline bool is_done_sat_1( int * clause_statuses ) {
+    return has_run(clause_statuses[0])
+        || has_run(clause_statuses[1])
+        && has_run(clause_statuses[2]);
+}
+
+Replaces the waituntil statement above with the following code:
+{
+    // used with atomic_dec/inc to get binary semaphore behaviour
+    int park_counter = 0;
+
+    // status (one for each clause)
+    int clause_statuses[3] = { 0 };
+
+    bool whenA = condA;
+    bool whenB = condB;
+    bool whenC = condC;
+
+    if ( !whenB ) clause_statuses[1] = __SELECT_RUN;
+    if ( !whenC ) clause_statuses[2] = __SELECT_RUN;
+
+    // some other conditional settors for clause_statuses are set here, see genSubtreeAssign and related routines
+
+    // three blocks
+    // for each block, create, setup, then register select_node
+    select_node clause1;
+    select_node clause2;
+    select_node clause3;
+
+    try {
+        if ( whenA ) { register_select(A, clause1); setup_clause( clause1, &clause_statuses[0], &park_counter ); }
+        ... repeat ^ for B and C ... 
+
+        // if else clause is defined a separate branch can occur here to set initial values, see genWhenStateConditions
+
+        // loop & park until done
+        while( !is_full_sat_1( clause_statuses ) ) {
+            
+            // binary sem P();
+            if ( __atomic_sub_fetch( &park_counter, 1, __ATOMIC_SEQ_CST) < 0 )
+                park();
+            
+            // execute any blocks available with status set to 0
+            for ( int i = 0; i < 3; i++ ) {
+                if (clause_statuses[i] == __SELECT_SAT) {
+                    switch (i) {
+                        case 0:
+                            try {
+                                if (on_selected( A, clause1 ))
+                                    doA();
+                            }
+                            finally { clause_statuses[i] = __SELECT_RUN; unregister_select(A, clause1); }
+                            break;
+                        case 1:
+                            ... same gen as A but for B and clause2 ...
+                            break;
+                        case 2:
+                            ... same gen as A but for C and clause3 ...
+                            break;
+                    }
+                }
+            }
+        }
+
+        // ensure that the blocks that triggered is_full_sat_1 are run
+        // by running every un-run block that is SAT from the start until
+        // the predicate is SAT when considering RUN status = true
+        for ( int i = 0; i < 3; i++ ) {
+            if (is_done_sat_1( clause_statuses )) break;
+            if (clause_statuses[i] == __SELECT_SAT)
+                ... Same if body here as in loop above ...
+        }
+    } finally {
+        // the unregister and on_selected calls are needed to support primitives where the acquire has side effects
+        // so the corresponding block MUST be run for those primitives to not lose state (example is channels)
+        if ( ! has_run(clause_statuses[0]) && whenA && unregister_select(A, clause1) && on_selected( A, clause1 ) )
+            doA(); 
+        ... repeat if above for B and C ...
+    }
+}
+
+*/
+
+namespace Concurrency {
+
+class GenerateWaitUntilCore final {
+    vector<FunctionDecl *> & satFns;
+	UniqueName namer_sat = "__is_full_sat_"s;
+    UniqueName namer_run = "__is_run_sat_"s;
+	UniqueName namer_park = "__park_counter_"s;
+	UniqueName namer_status = "__clause_statuses_"s;
+	UniqueName namer_node = "__clause_"s;
+    UniqueName namer_target = "__clause_target_"s;
+    UniqueName namer_when = "__when_cond_"s;
+
+    string idxName = "__CFA_clause_idx_";
+
+    struct ClauseData {
+        string nodeName;
+        string targetName;
+        string whenName;
+        int index;
+        string & statusName;
+        ClauseData( int index, string & statusName ) : index(index), statusName(statusName) {}
+    };
+
+    const StructDecl * selectNodeDecl = nullptr;
+
+    // This first set of routines are all used to do the complicated job of 
+    //    dealing with how to set predicate statuses with certain when_conds T/F
+    //    so that the when_cond == F effectively makes that clause "disappear"
+    void updateAmbiguousWhen( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool andBelow, bool orBelow );
+    void paintWhenTree( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool & andBelow, bool & orBelow );
+    bool paintWhenTree( WaitUntilStmt::ClauseNode * currNode );
+    void collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs, int & index, bool parentAmbig, bool parentAnd );
+    void collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs );
+    void updateWhenState( WaitUntilStmt::ClauseNode * currNode );
+    void genSubtreeAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, bool status, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData );
+    void genStatusAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData );
+    CompoundStmt * getStatusAssignment( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData );
+    Stmt * genWhenStateConditions( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigClauses, vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type ambigIdx );
+
+    // These routines are just code-gen helpers
+    void addPredicates( const WaitUntilStmt * stmt, string & satName, string & runName );
+    void setUpClause( const WhenClause * clause, ClauseData * data, string & pCountName, CompoundStmt * body );
+    ForStmt * genStatusCheckFor( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, string & predName );
+    Expr * genSelectTraitCall( const WhenClause * clause, const ClauseData * data, string fnName );
+    CompoundStmt * genStmtBlock( const WhenClause * clause, const ClauseData * data );
+    Stmt * genElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, vector<ClauseData *> & clauseData );
+    Stmt * genNoElseClauseBranch( const WaitUntilStmt * stmt, string & satName, string & runName, string & arrName, string & pCountName, vector<ClauseData *> & clauseData );
+    void genClauseInits( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, CompoundStmt * body, string & statusName, string & elseWhenName );
+    Stmt * recursiveOrIfGen( const WaitUntilStmt * stmt, vector<ClauseData *> & data, vector<ClauseData*>::size_type idx, string & elseWhenName );
+    Stmt * buildOrCaseSwitch( const WaitUntilStmt * stmt, string & statusName, vector<ClauseData *> & data );
+    Stmt * genAllOr( const WaitUntilStmt * stmt );
+
+  public:
+    void previsit( const StructDecl * decl );
+	Stmt * postvisit( const WaitUntilStmt * stmt );
+    GenerateWaitUntilCore( vector<FunctionDecl *> & satFns ): satFns(satFns) {}
+};
+
+// Finds select_node decl
+void GenerateWaitUntilCore::previsit( const StructDecl * decl ) {
+    if ( !decl->body ) {
+		return;
+	} else if ( "select_node" == decl->name ) {
+		assert( !selectNodeDecl );
+		selectNodeDecl = decl;
+	}
+}
+
+void GenerateWaitUntilCore::updateAmbiguousWhen( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool andBelow, bool orBelow ) {
+    // all children when-ambiguous
+    if ( currNode->left->ambiguousWhen && currNode->right->ambiguousWhen )
+        // true iff an ancestor/descendant has a different operation
+        currNode->ambiguousWhen = (orAbove || orBelow) && (andBelow || andAbove);
+    // ambiguousWhen is initially false so theres no need to set it here
+}
+
+// Traverses ClauseNode tree and paints each AND/OR node as when-ambiguous true or false
+// This tree painting is needed to generate the if statements that set the initial state
+//    of the clause statuses when some clauses are turned off via when_cond
+// An internal AND/OR node is when-ambiguous if it satisfies all of the following:
+// - It has an ancestor or descendant that is a different operation, i.e. (AND has an OR ancestor or vice versa)
+// - All of its descendent clauses are optional, i.e. they have a when_cond defined on the WhenClause
+void GenerateWaitUntilCore::paintWhenTree( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool & andBelow, bool & orBelow ) {
+    bool aBelow = false; // updated by child nodes
+    bool oBelow = false; // updated by child nodes
+    switch (currNode->op) {
+        case WaitUntilStmt::ClauseNode::AND:
+            paintWhenTree( currNode->left, true, orAbove, aBelow, oBelow );
+            paintWhenTree( currNode->right, true, orAbove, aBelow, oBelow );
+
+            // update currNode's when flag based on conditions listed in fn signature comment above
+            updateAmbiguousWhen(currNode, true, orAbove, aBelow, oBelow );
+
+            // set return flags to tell parents which decendant ops have been seen
+            andBelow = true;
+            orBelow = oBelow;
+            return;
+        case WaitUntilStmt::ClauseNode::OR:
+            paintWhenTree( currNode->left, andAbove, true, aBelow, oBelow );
+            paintWhenTree( currNode->right, andAbove, true, aBelow, oBelow );
+
+            // update currNode's when flag based on conditions listed in fn signature comment above
+            updateAmbiguousWhen(currNode, andAbove, true, aBelow, oBelow );
+
+            // set return flags to tell parents which decendant ops have been seen
+            andBelow = aBelow;
+            orBelow = true;
+            return;
+        case WaitUntilStmt::ClauseNode::LEAF:
+            if ( currNode->leaf->when_cond )
+                currNode->ambiguousWhen = true;
+            return;
+        default:
+            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
+    }
+}
+
+// overloaded wrapper for paintWhenTree that sets initial values
+// returns true if entire tree is OR's (special case)
+bool GenerateWaitUntilCore::paintWhenTree( WaitUntilStmt::ClauseNode * currNode ) {
+    bool aBelow = false, oBelow = false; // unused by initial call
+    paintWhenTree( currNode, false, false, aBelow, oBelow );
+    return !aBelow;
+}
+
+// Helper: returns Expr that represents arrName[index]
+Expr * genArrAccessExpr( const CodeLocation & loc, int index, string arrName ) {
+    return new UntypedExpr ( loc, 
+        new NameExpr( loc, "?[?]" ),
+        {
+            new NameExpr( loc, arrName ),
+            ConstantExpr::from_int( loc, index )
+        }
+    );
+}
+
+// After the ClauseNode AND/OR nodes are painted this routine is called to traverses the tree and does the following:
+// - collects a set of indices in the clause arr that refer whenclauses that can have ambiguous status assignments (ambigIdxs)
+// - collects a set of indices in the clause arr that refer whenclauses that have a when() defined and an AND node as a parent (andIdxs)
+// - updates LEAF nodes to be when-ambiguous if their direct parent is when-ambiguous.
+void GenerateWaitUntilCore::collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs, int & index, bool parentAmbig, bool parentAnd ) {
+    switch (currNode->op) {
+        case WaitUntilStmt::ClauseNode::AND:
+            collectWhens( currNode->left, ambigIdxs, andIdxs, index, currNode->ambiguousWhen, true );
+            collectWhens( currNode->right,  ambigIdxs, andIdxs, index, currNode->ambiguousWhen, true );
+            return;
+        case WaitUntilStmt::ClauseNode::OR:
+            collectWhens( currNode->left,  ambigIdxs, andIdxs, index, currNode->ambiguousWhen, false );
+            collectWhens( currNode->right,  ambigIdxs, andIdxs, index, currNode->ambiguousWhen, false );
+            return;
+        case WaitUntilStmt::ClauseNode::LEAF:
+            if ( parentAmbig ) {
+                ambigIdxs.push_back(make_pair(index, currNode));
+            }
+            if ( parentAnd && currNode->leaf->when_cond ) {
+                currNode->childOfAnd = true;
+                andIdxs.push_back(index);
+            }
+            index++;
+            return;
+        default:
+            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
+    }
+}
+
+// overloaded wrapper for collectWhens that sets initial values
+void GenerateWaitUntilCore::collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs ) {
+    int idx = 0;
+    collectWhens( currNode, ambigIdxs, andIdxs, idx, false, false );
+}
+
+// recursively updates ClauseNode whenState on internal nodes so that next pass can see which 
+//    subtrees are "turned off"
+// sets whenState = false iff both children have whenState == false.
+// similar to paintWhenTree except since paintWhenTree also filtered out clauses we don't need to consider based on op
+// since the ambiguous clauses were filtered in paintWhenTree we don't need to worry about that here
+void GenerateWaitUntilCore::updateWhenState( WaitUntilStmt::ClauseNode * currNode ) {
+    if ( currNode->op == WaitUntilStmt::ClauseNode::LEAF ) return;
+    updateWhenState( currNode->left );
+    updateWhenState( currNode->right );
+    if ( !currNode->left->whenState && !currNode->right->whenState )
+        currNode->whenState = false;
+    else 
+        currNode->whenState = true;
+}
+
+// generates the minimal set of status assignments to ensure predicate subtree passed as currNode evaluates to status
+// assumes that this will only be called on subtrees that are entirely whenState == false
+void GenerateWaitUntilCore::genSubtreeAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, bool status, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData ) {
+    if ( ( currNode->op == WaitUntilStmt::ClauseNode::AND && status )
+        || ( currNode->op == WaitUntilStmt::ClauseNode::OR && !status ) ) {
+        // need to recurse on both subtrees if && subtree needs to be true or || subtree needs to be false
+        genSubtreeAssign( stmt, currNode->left, status, idx, retStmt, clauseData );
+        genSubtreeAssign( stmt, currNode->right, status, idx, retStmt, clauseData );
+    } else if ( ( currNode->op == WaitUntilStmt::ClauseNode::OR && status )
+        || ( currNode->op == WaitUntilStmt::ClauseNode::AND && !status ) ) {
+        // only one subtree needs to evaluate to status if && subtree needs to be true or || subtree needs to be false
+        CompoundStmt * leftStmt = new CompoundStmt( stmt->location );
+        CompoundStmt * rightStmt = new CompoundStmt( stmt->location );
+
+        // only one side needs to evaluate to status so we recurse on both subtrees
+        //    but only keep the statements from the subtree with minimal statements
+        genSubtreeAssign( stmt, currNode->left, status, idx, leftStmt, clauseData );
+        genSubtreeAssign( stmt, currNode->right, status, idx, rightStmt, clauseData );
+        
+        // append minimal statements to retStmt
+        if ( leftStmt->kids.size() < rightStmt->kids.size() ) {
+            retStmt->kids.splice( retStmt->kids.end(), leftStmt->kids );
+        } else {
+            retStmt->kids.splice( retStmt->kids.end(), rightStmt->kids );
+        }
+        
+        delete leftStmt;
+        delete rightStmt;
+    } else if ( currNode->op == WaitUntilStmt::ClauseNode::LEAF ) {
+        const CodeLocation & loc = stmt->location;
+        if ( status && !currNode->childOfAnd ) {
+            retStmt->push_back(
+                new ExprStmt( loc, 
+                    UntypedExpr::createAssign( loc,
+                        genArrAccessExpr( loc, idx, clauseData.at(idx)->statusName ),
+                        new NameExpr( loc, "__SELECT_RUN" )
+                    )
+                )
+            );
+        } else if ( !status && currNode->childOfAnd ) {
+            retStmt->push_back(
+                new ExprStmt( loc, 
+                    UntypedExpr::createAssign( loc,
+                        genArrAccessExpr( loc, idx, clauseData.at(idx)->statusName ),
+                        new NameExpr( loc, "__SELECT_UNSAT" )
+                    )
+                )
+            );
+        }
+
+        // No need to generate statements for the following cases since childOfAnd are always set to true
+        //    and !childOfAnd are always false
+        // - status && currNode->childOfAnd
+        // - !status && !currNode->childOfAnd
+        idx++;
+    }
+}
+
+void GenerateWaitUntilCore::genStatusAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData ) {
+    switch (currNode->op) {
+        case WaitUntilStmt::ClauseNode::AND:
+            // check which subtrees have all whenState == false (disabled)
+            if (!currNode->left->whenState && !currNode->right->whenState) {
+                // this case can only occur when whole tree is disabled since otherwise 
+                //    genStatusAssign( ... ) isn't called on nodes with whenState == false
+                assert( !currNode->whenState ); // paranoidWWW
+                // whole tree disabled so pass true so that select is SAT vacuously
+                genSubtreeAssign( stmt, currNode, true, idx, retStmt, clauseData );
+            } else if ( !currNode->left->whenState ) {
+                // pass true since x && true === x
+                genSubtreeAssign( stmt, currNode->left, true, idx, retStmt, clauseData );
+                genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData );
+            } else if ( !currNode->right->whenState ) {
+                genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData );
+                genSubtreeAssign( stmt, currNode->right, true, idx, retStmt, clauseData );
+            } else { 
+                // if no children with whenState == false recurse normally via break
+                break;
+            }
+            return;
+        case WaitUntilStmt::ClauseNode::OR:
+            if (!currNode->left->whenState && !currNode->right->whenState) {
+                assert( !currNode->whenState ); // paranoid
+                genSubtreeAssign( stmt, currNode, true, idx, retStmt, clauseData );
+            } else if ( !currNode->left->whenState ) {
+                // pass false since x || false === x
+                genSubtreeAssign( stmt, currNode->left, false, idx, retStmt, clauseData );
+                genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData );
+            } else if ( !currNode->right->whenState ) {
+                genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData );
+                genSubtreeAssign( stmt, currNode->right, false, idx, retStmt, clauseData );
+            } else { 
+                break;
+            }
+            return;
+        case WaitUntilStmt::ClauseNode::LEAF:
+            idx++;
+            return;
+        default:
+            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
+    }
+    genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData );
+    genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData );
+}
+
+// generates a minimal set of assignments for status arr based on which whens are toggled on/off
+CompoundStmt * GenerateWaitUntilCore::getStatusAssignment( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData ) {
+    updateWhenState( stmt->predicateTree );
+    CompoundStmt * retval = new CompoundStmt( stmt->location );
+    int idx = 0;
+    genStatusAssign( stmt, stmt->predicateTree, idx, retval, clauseData );
+    return retval;
+}
+
+// generates nested if/elses for all possible assignments of ambiguous when_conds
+// exponential size of code gen but linear runtime O(n), where n is number of ambiguous whens()
+Stmt * GenerateWaitUntilCore::genWhenStateConditions( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, 
+    vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigClauses, vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type ambigIdx ) {
+    // I hate C++ sometimes, using vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type for size() comparison seems silly.
+    //    Why is size_type parameterized on the type stored in the vector?????
+
+    const CodeLocation & loc = stmt->location;
+    int clauseIdx = ambigClauses.at(ambigIdx).first;
+    WaitUntilStmt::ClauseNode * currNode = ambigClauses.at(ambigIdx).second;
+    Stmt * thenStmt;
+    Stmt * elseStmt;
+    
+    if ( ambigIdx == ambigClauses.size() - 1 ) { // base case
+        currNode->whenState = true;
+        thenStmt = getStatusAssignment( stmt, clauseData );
+        currNode->whenState = false;
+        elseStmt = getStatusAssignment( stmt, clauseData );
+    } else {
+        // recurse both with when enabled and disabled to generate all possible cases
+        currNode->whenState = true;
+        thenStmt = genWhenStateConditions( stmt, clauseData, ambigClauses, ambigIdx + 1 );
+        currNode->whenState = false;
+        elseStmt = genWhenStateConditions( stmt, clauseData, ambigClauses, ambigIdx + 1 );
+    }
+
+    // insert first recursion result in if ( __when_cond_ ) { ... }
+    // insert second recursion result in else { ... }
+    return new CompoundStmt ( loc,
+        {
+            new IfStmt( loc,
+                new NameExpr( loc, clauseData.at(clauseIdx)->whenName ),
+                thenStmt,
+                elseStmt
+            )
+        }
+    );
+}
+
+// typedef a fn ptr so that we can reuse genPredExpr
+// genLeafExpr is used to refer to one of the following two routines
+typedef Expr * (*GenLeafExpr)( const CodeLocation & loc, int & index );
+
+// return Expr that represents clause_statuses[index]
+// mutates index to be index + 1
+Expr * genSatExpr( const CodeLocation & loc, int & index ) {
+    return genArrAccessExpr( loc, index++, "clause_statuses" );
+}
+
+// return Expr that represents has_run(clause_statuses[index])
+Expr * genRunExpr( const CodeLocation & loc, int & index ) {
+    return new UntypedExpr ( loc, 
+        new NameExpr( loc, "__CFA_has_clause_run" ),
+        { genSatExpr( loc, index ) }
+    );
+}
+
+// Takes in the ClauseNode tree and recursively generates
+// the predicate expr used inside the predicate functions
+Expr * genPredExpr( const CodeLocation & loc, WaitUntilStmt::ClauseNode * currNode, int & idx, GenLeafExpr genLeaf ) {
+    switch (currNode->op) {
+        case WaitUntilStmt::ClauseNode::AND:
+            return new LogicalExpr( loc, 
+                new CastExpr( loc, genPredExpr( loc, currNode->left, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ),
+                new CastExpr( loc, genPredExpr( loc, currNode->right, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ), 
+                LogicalFlag::AndExpr 
+            );
+        case WaitUntilStmt::ClauseNode::OR:
+            return new LogicalExpr( loc,
+                new CastExpr( loc, genPredExpr( loc, currNode->left, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ),
+                new CastExpr( loc, genPredExpr( loc, currNode->right, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ), 
+                LogicalFlag::OrExpr );
+        case WaitUntilStmt::ClauseNode::LEAF:
+            return genLeaf( loc, idx );
+        default:
+            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
+    }
+}
+
+
+// Builds the predicate functions used to check the status of the waituntil statement
+/* Ex:
+{
+    waituntil( A ){ doA(); } 
+    or waituntil( B ){ doB(); } 
+    and waituntil( C ) { doC(); }
+}
+generates =>
+static inline bool is_full_sat_1( int * clause_statuses ) {
+    return clause_statuses[0] 
+        || clause_statuses[1]
+        && clause_statuses[2];
+}
+
+static inline bool is_done_sat_1( int * clause_statuses ) {
+    return has_run(clause_statuses[0])
+        || has_run(clause_statuses[1])
+        && has_run(clause_statuses[2]);
+}
+*/
+// Returns a predicate function decl
+// predName and genLeaf determine if this generates an is_done or an is_full predicate
+FunctionDecl * buildPredicate( const WaitUntilStmt * stmt, GenLeafExpr genLeaf, string & predName ) {
+    int arrIdx = 0;
+    const CodeLocation & loc = stmt->location;
+    CompoundStmt * body = new CompoundStmt( loc );
+    body->push_back( new ReturnStmt( loc, genPredExpr( loc,  stmt->predicateTree, arrIdx, genLeaf ) ) );
+
+    return new FunctionDecl( loc,
+        predName,
+        {},                     // forall
+        {
+            new ObjectDecl( loc,
+                "clause_statuses",
+                new PointerType( new BasicType( BasicType::Kind::LongUnsignedInt ) )
+            )
+        },
+        { 
+            new ObjectDecl( loc,
+                "sat_ret",
+                new BasicType( BasicType::Kind::Bool )
+            )
+        },
+        body,               // body
+        { Storage::Static },    // storage
+        Linkage::Cforall,       // linkage
+        {},                     // attributes
+        { Function::Inline }
+    );
+}
+
+// Creates is_done and is_full predicates
+void GenerateWaitUntilCore::addPredicates( const WaitUntilStmt * stmt, string & satName, string & runName ) {
+    if ( !stmt->else_stmt || stmt->else_cond ) // don't need SAT predicate when else variation with no else_cond
+        satFns.push_back( Concurrency::buildPredicate( stmt, genSatExpr, satName ) ); 
+    satFns.push_back( Concurrency::buildPredicate( stmt, genRunExpr, runName ) );
+}
+
+// Adds the following to body:
+// if ( when_cond ) { // this if is omitted if no when() condition
+//      setup_clause( clause1, &clause_statuses[0], &park_counter );
+//      register_select(A, clause1);
+// }
+void GenerateWaitUntilCore::setUpClause( const WhenClause * clause, ClauseData * data, string & pCountName, CompoundStmt * body ) {    
+    CompoundStmt * currBody = body;
+    const CodeLocation & loc = clause->location;
+
+    // If we have a when_cond make the initialization conditional
+    if ( clause->when_cond )
+        currBody = new CompoundStmt( loc );
+
+    // Generates: setup_clause( clause1, &clause_statuses[0], &park_counter );
+    currBody->push_back( new ExprStmt( loc,
+        new UntypedExpr ( loc,
+            new NameExpr( loc, "setup_clause" ),
+            {
+                new NameExpr( loc, data->nodeName ),
+                new AddressExpr( loc, genArrAccessExpr( loc, data->index, data->statusName ) ),
+                new AddressExpr( loc, new NameExpr( loc, pCountName ) )
+            }
+        )
+    ));
+
+    // Generates: register_select(A, clause1);
+    currBody->push_back( new ExprStmt( loc, genSelectTraitCall( clause, data, "register_select" ) ) );
+
+    // generates: if ( when_cond ) { ... currBody ... }
+    if ( clause->when_cond )
+        body->push_back( 
+            new IfStmt( loc,
+                new NameExpr( loc, data->whenName ),
+                currBody
+            )
+        );
+}
+
+// Used to generate a call to one of the select trait routines
+Expr * GenerateWaitUntilCore::genSelectTraitCall( const WhenClause * clause, const ClauseData * data, string fnName ) {
+    const CodeLocation & loc = clause->location;
+    return new UntypedExpr ( loc,
+        new NameExpr( loc, fnName ),
+        {
+            new NameExpr( loc, data->targetName ),
+            new NameExpr( loc, data->nodeName )
+        }
+    );
+}
+
+// Generates:
+/* if ( on_selected( target_1, node_1 )) ... corresponding body of target_1 ... 
+*/
+CompoundStmt * GenerateWaitUntilCore::genStmtBlock( const WhenClause * clause, const ClauseData * data ) {
+    const CodeLocation & cLoc = clause->location;
+    return new CompoundStmt( cLoc,
+        {
+            new IfStmt( cLoc,
+                genSelectTraitCall( clause, data, "on_selected" ),
+                new CompoundStmt( cLoc,
+                    {
+                        ast::deepCopy( clause->stmt )
+                    }
+                )
+            )
+        }
+    );
+}
+
+// this routine generates and returns the following
+/*for ( int i = 0; i < numClauses; i++ ) {
+    if ( predName(clause_statuses) ) break;
+    if (clause_statuses[i] == __SELECT_SAT) {
+        switch (i) {
+            case 0:
+                try {
+                    if (on_selected( target1, clause1 ))
+                        dotarget1stmt();
+                }
+                finally { clause_statuses[i] = __SELECT_RUN; unregister_select(target1, clause1); }
+                break;
+            ...
+            case N:
+                ...
+                break;
+        }
+    }
+}*/
+ForStmt * GenerateWaitUntilCore::genStatusCheckFor( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, string & predName ) {
+    CompoundStmt * ifBody = new CompoundStmt( stmt->location );
+    const CodeLocation & loc = stmt->location;
+
+    /* generates:
+    switch (i) {
+        case 0:
+            try {
+                if (on_selected( target1, clause1 ))
+                    dotarget1stmt();
+            }
+            finally { clause_statuses[i] = __SELECT_RUN; unregister_select(target1, clause1); }
+            break;
+            ...
+        case N:
+            ...
+            break;
+    }*/
+    std::vector<ptr<CaseClause>> switchCases;
+    int idx = 0;
+    for ( const auto & clause: stmt->clauses ) {
+        const CodeLocation & cLoc = clause->location;
+        switchCases.push_back(
+            new CaseClause( cLoc,
+                ConstantExpr::from_int( cLoc, idx ),
+                {
+                    new CompoundStmt( cLoc,
+                        {
+                            new ast::TryStmt( cLoc,
+                                genStmtBlock( clause, clauseData.at(idx) ),
+                                {},
+                                new ast::FinallyClause( cLoc, 
+                                    new CompoundStmt( cLoc,
+                                        {
+                                            new ExprStmt( loc,
+                                                new UntypedExpr ( loc,
+                                                    new NameExpr( loc, "?=?" ),
+                                                    {
+                                                        new UntypedExpr ( loc, 
+                                                            new NameExpr( loc, "?[?]" ),
+                                                            {
+                                                                new NameExpr( loc, clauseData.at(0)->statusName ),
+                                                                new NameExpr( loc, idxName )
+                                                            }
+                                                        ),
+                                                        new NameExpr( loc, "__SELECT_RUN" )
+                                                    }
+                                                )
+                                            ),
+                                            new ExprStmt( loc, genSelectTraitCall( clause, clauseData.at(idx), "unregister_select" ) )
+                                        }
+                                    )
+                                )
+                            ),
+                            new BranchStmt( cLoc, BranchStmt::Kind::Break, Label( cLoc ) )
+                        }
+                    )
+                }
+            )
+        );
+        idx++;
+    }
+
+    ifBody->push_back(
+        new SwitchStmt( loc,
+            new NameExpr( loc, idxName ),
+            std::move( switchCases )
+        )
+    );
+
+    // gens:
+    // if (clause_statuses[i] == __SELECT_SAT) {
+    //      ... ifBody  ...
+    // }
+    IfStmt * ifSwitch = new IfStmt( loc,
+        new UntypedExpr ( loc,
+            new NameExpr( loc, "?==?" ),
+            {
+                new UntypedExpr ( loc, 
+                    new NameExpr( loc, "?[?]" ),
+                    {
+                        new NameExpr( loc, clauseData.at(0)->statusName ),
+                        new NameExpr( loc, idxName )
+                    }
+                ),
+                new NameExpr( loc, "__SELECT_SAT" )
+            }
+        ),      // condition
+        ifBody  // body
+    );
+
+    return new ForStmt( loc,
+        {
+            new DeclStmt( loc,
+                new ObjectDecl( loc,
+                    idxName,
+                    new BasicType( BasicType::Kind::SignedInt ),
+                    new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
+                )
+            )
+        },  // inits
+        new UntypedExpr ( loc,
+            new NameExpr( loc, "?<?" ),
+            {
+                new NameExpr( loc, idxName ),
+                ConstantExpr::from_int( loc, stmt->clauses.size() )
+            }
+        ),  // cond
+        new UntypedExpr ( loc,
+            new NameExpr( loc, "?++" ),
+            { new NameExpr( loc, idxName ) }
+        ),  // inc
+        new CompoundStmt( loc,
+            {
+                new IfStmt( loc,
+                    new UntypedExpr ( loc,
+                        new NameExpr( loc, predName ),
+                        { new NameExpr( loc, clauseData.at(0)->statusName ) }
+                    ),
+                    new BranchStmt( loc, BranchStmt::Kind::Break, Label( loc ) )
+                ),
+                ifSwitch
+            }
+        )   // body
+    );
+}
+
+// Generates: !is_full_sat_n() / !is_run_sat_n()
+Expr * genNotSatExpr( const WaitUntilStmt * stmt, string & satName, string & arrName ) {
+    const CodeLocation & loc = stmt->location;
+    return new UntypedExpr ( loc,
+        new NameExpr( loc, "!?" ),
+        {
+            new UntypedExpr ( loc,
+                new NameExpr( loc, satName ),
+                { new NameExpr( loc, arrName ) }
+            )
+        }
+    );
+}
+
+// Generates the code needed for waituntils with an else ( ... )
+// Checks clauses once after registering for completion and runs them if completes
+// If not enough have run to satisfy predicate after one pass then the else is run
+Stmt * GenerateWaitUntilCore::genElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, vector<ClauseData *> & clauseData ) {
+    return new CompoundStmt( stmt->else_stmt->location,
+        {
+            genStatusCheckFor( stmt, clauseData, runName ),
+            new IfStmt( stmt->else_stmt->location,
+                genNotSatExpr( stmt, runName, arrName ),
+                ast::deepCopy( stmt->else_stmt )
+            )
+        }
+    );
+}
+
+Stmt * GenerateWaitUntilCore::genNoElseClauseBranch( const WaitUntilStmt * stmt, string & satName, string & runName, string & arrName, string & pCountName, vector<ClauseData *> & clauseData ) {
+    CompoundStmt * whileBody = new CompoundStmt( stmt->location );
+    const CodeLocation & loc = stmt->location;
+
+    // generates: __CFA_maybe_park( &park_counter );
+    whileBody->push_back(
+        new ExprStmt( loc,
+            new UntypedExpr ( loc,
+                new NameExpr( loc, "__CFA_maybe_park" ),
+                { new AddressExpr( loc, new NameExpr( loc, pCountName ) ) }
+            )
+        )
+    );
+
+    whileBody->push_back( genStatusCheckFor( stmt, clauseData, satName ) );
+
+    return new CompoundStmt( loc,
+        {
+            new WhileDoStmt( loc,
+                genNotSatExpr( stmt, satName, arrName ),
+                whileBody,  // body
+                {}          // no inits
+            ),
+            genStatusCheckFor( stmt, clauseData, runName )
+        }
+    );
+}
+
+// generates the following decls for each clause to ensure the target expr and when_cond is only evaluated once
+// typeof(target) & __clause_target_0 = target;
+// bool __when_cond_0 = when_cond; // only generated if when_cond defined
+// select_node clause1;
+void GenerateWaitUntilCore::genClauseInits( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, CompoundStmt * body, string & statusName, string & elseWhenName ) {
+    ClauseData * currClause;
+    for ( vector<ClauseData*>::size_type i = 0; i < stmt->clauses.size(); i++ ) {
+        currClause = new ClauseData( i, statusName );
+        currClause->nodeName = namer_node.newName();
+        currClause->targetName = namer_target.newName();
+        currClause->whenName = namer_when.newName();
+        clauseData.push_back(currClause);
+        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
+
+        // typeof(target) & __clause_target_0 = target;
+        body->push_back(
+            new DeclStmt( cLoc,
+                new ObjectDecl( cLoc,
+                    currClause->targetName,
+                    new ReferenceType( new TypeofType( ast::deepCopy( stmt->clauses.at(i)->target ) ) ),
+                    new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->target ) )
+                )
+            )
+        );
+
+        // bool __when_cond_0 = when_cond; // only generated if when_cond defined
+        if ( stmt->clauses.at(i)->when_cond )
+            body->push_back(
+                new DeclStmt( cLoc,
+                    new ObjectDecl( cLoc,
+                        currClause->whenName,
+                        new BasicType( BasicType::Kind::Bool ),
+                        new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->when_cond ) )
+                    )
+                )
+            );
+        
+        // select_node clause1;
+        body->push_back(
+            new DeclStmt( cLoc,
+                new ObjectDecl( cLoc,
+                    currClause->nodeName,
+                    new StructInstType( selectNodeDecl )
+                )
+            )
+        );
+    }
+
+    if ( stmt->else_stmt && stmt->else_cond ) {
+        body->push_back(
+            new DeclStmt( stmt->else_cond->location,
+                new ObjectDecl( stmt->else_cond->location,
+                    elseWhenName,
+                    new BasicType( BasicType::Kind::Bool ),
+                    new SingleInit( stmt->else_cond->location, ast::deepCopy( stmt->else_cond ) )
+                )
+            )
+        );
+    }
+}
+
+/*
+if ( clause_status == &clause1 ) ... clause 1 body ...
+...
+elif ( clause_status == &clausen ) ... clause n body ...
+*/
+Stmt * GenerateWaitUntilCore::buildOrCaseSwitch( const WaitUntilStmt * stmt, string & statusName, vector<ClauseData *> & data ) {
+    const CodeLocation & loc = stmt->location;
+
+    IfStmt * outerIf = nullptr;
+	IfStmt * lastIf = nullptr;
+
+	//adds an if/elif clause for each select clause address to run the corresponding clause stmt
+	for ( long unsigned int i = 0; i < data.size(); i++ ) {
+        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
+
+		IfStmt * currIf = new IfStmt( cLoc,
+			new UntypedExpr( cLoc, 
+                new NameExpr( cLoc, "?==?" ), 
+                {
+                    new NameExpr( cLoc, statusName ),
+                    new CastExpr( cLoc, 
+                        new AddressExpr( cLoc, new NameExpr( cLoc, data.at(i)->nodeName ) ),
+                        new BasicType( BasicType::Kind::LongUnsignedInt ), GeneratedFlag::ExplicitCast 
+                    )
+                }
+            ),
+            genStmtBlock( stmt->clauses.at(i), data.at(i) )
+		);
+		
+		if ( i == 0 ) {
+			outerIf = currIf;
+		} else {
+			// add ifstmt to else of previous stmt
+			lastIf->else_ = currIf;
+		}
+
+		lastIf = currIf;
+	}
+
+    // C_TODO: will remove this commented code later. Currently it isn't needed but may switch to a modified version of this later if it has better performance
+    // std::vector<ptr<CaseClause>> switchCases;
+
+    // int idx = 0;
+    // for ( const auto & clause: stmt->clauses ) {
+    //     const CodeLocation & cLoc = clause->location;
+    //     switchCases.push_back(
+    //         new CaseClause( cLoc,
+    //             new CastExpr( cLoc, 
+    //                 new AddressExpr( cLoc, new NameExpr( cLoc, data.at(idx)->targetName ) ),
+    //                 new BasicType( BasicType::Kind::LongUnsignedInt ), GeneratedFlag::ExplicitCast 
+    //             ),
+    //             {
+    //                 new CompoundStmt( cLoc,
+    //                     {
+    //                         ast::deepCopy( clause->stmt ),
+    //                         new BranchStmt( cLoc, BranchStmt::Kind::Break, Label( cLoc ) )
+    //                     }
+    //                 )
+    //             }
+    //         )
+    //     );
+    //     idx++;
+    // }
+
+    return new CompoundStmt( loc,
+        {
+            new ExprStmt( loc, new UntypedExpr( loc, new NameExpr( loc, "park" ) ) ),
+            outerIf
+            // new SwitchStmt( loc,
+            //     new NameExpr( loc, statusName ),
+            //     std::move( switchCases )
+            // )
+        }
+    );
+}
+
+Stmt * GenerateWaitUntilCore::recursiveOrIfGen( const WaitUntilStmt * stmt, vector<ClauseData *> & data, vector<ClauseData*>::size_type idx, string & elseWhenName ) {
+    if ( idx == data.size() ) {   // base case, gen last else
+        const CodeLocation & cLoc = stmt->else_stmt->location;
+        if ( !stmt->else_stmt ) // normal non-else gen
+            return buildOrCaseSwitch( stmt, data.at(0)->statusName, data );
+
+        Expr * raceFnCall = new UntypedExpr( stmt->location,
+            new NameExpr( stmt->location, "__select_node_else_race" ),
+            { new NameExpr( stmt->location, data.at(0)->nodeName ) }
+        );
+
+        if ( stmt->else_stmt && stmt->else_cond ) { // return else conditional on both when and race
+            return new IfStmt( cLoc,
+                new LogicalExpr( cLoc,
+                    new CastExpr( cLoc,
+                        new NameExpr( cLoc, elseWhenName ),
+                        new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                    ),
+                    new CastExpr( cLoc,
+                        raceFnCall,
+                        new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                    ),
+                    LogicalFlag::AndExpr
+                ),
+                ast::deepCopy( stmt->else_stmt ),
+                buildOrCaseSwitch( stmt, data.at(0)->statusName, data )
+            );
+        }
+
+        // return else conditional on race
+        return new IfStmt( stmt->else_stmt->location,
+            raceFnCall,
+            ast::deepCopy( stmt->else_stmt ),
+            buildOrCaseSwitch( stmt, data.at(0)->statusName, data )
+        );
+    }
+    const CodeLocation & cLoc = stmt->clauses.at(idx)->location;
+
+    Expr * ifCond;
+
+    // If we have a when_cond make the register call conditional on it
+    if ( stmt->clauses.at(idx)->when_cond ) {
+        ifCond = new LogicalExpr( cLoc,
+            new CastExpr( cLoc,
+                new NameExpr( cLoc, data.at(idx)->whenName ), 
+                new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+            ),
+            new CastExpr( cLoc,
+                genSelectTraitCall( stmt->clauses.at(idx), data.at(idx), "register_select" ),
+                new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+            ),
+            LogicalFlag::AndExpr
+        );
+    } else ifCond = genSelectTraitCall( stmt->clauses.at(idx), data.at(idx), "register_select" );
+
+    return new CompoundStmt( cLoc,
+        {   // gens: setup_clause( clause1, &status, 0p );
+            new ExprStmt( cLoc,
+                new UntypedExpr ( cLoc,
+                    new NameExpr( cLoc, "setup_clause" ),
+                    {
+                        new NameExpr( cLoc, data.at(idx)->nodeName ),
+                        new AddressExpr( cLoc, new NameExpr( cLoc, data.at(idx)->statusName ) ),
+                        ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicType::Kind::SignedInt ) ) )
+                    }
+                )
+            ),
+            // gens: if (__when_cond && register_select()) { clause body } else { ... recursiveOrIfGen ... }
+            new IfStmt( cLoc,
+                ifCond,
+                genStmtBlock( stmt->clauses.at(idx), data.at(idx) ),
+                // ast::deepCopy( stmt->clauses.at(idx)->stmt ),
+                recursiveOrIfGen( stmt, data, idx + 1, elseWhenName )
+            )
+        }
+    );
+}
+
+// This gens the special case of an all OR waituntil:
+/* 
+int status = 0;
+
+typeof(target) & __clause_target_0 = target;
+bool __when_cond_0 = when_cond; // only generated if when_cond defined
+select_node clause1;
+... generate above for rest of clauses ...
+
+try {
+    setup_clause( clause1, &status, 0p );
+    if ( __when_cond_0 && register_select( 1 ) ) {
+        ... clause 1 body ...
+    } else {
+        ... recursively gen for each of n clauses ...
+        setup_clause( clausen, &status, 0p );
+        if ( __when_cond_n-1 && register_select( n ) ) {
+            ... clause n body ...
+        } else {
+            if ( else_when ) ... else clause body ...
+            else {
+                park();
+
+                // after winning the race and before unpark() clause_status is set to be the winning clause index + 1 
+                if ( clause_status == &clause1) ... clause 1 body ...
+                ...
+                elif ( clause_status == &clausen ) ... clause n body ...
+            }
+        }
+    }
+}
+finally { 
+    if ( __when_cond_1 && clause1.status != 0p) unregister_select( 1 ); // if registered unregister
+    ...
+    if ( __when_cond_n && clausen.status != 0p) unregister_select( n );
+}
+*/
+Stmt * GenerateWaitUntilCore::genAllOr( const WaitUntilStmt * stmt ) {
+    const CodeLocation & loc = stmt->location;
+    string statusName = namer_status.newName();
+    string elseWhenName = namer_when.newName();
+    int numClauses = stmt->clauses.size();
+    CompoundStmt * body = new CompoundStmt( stmt->location );
+
+    // Generates: unsigned long int status = 0;
+    body->push_back( new DeclStmt( loc,
+        new ObjectDecl( loc,
+            statusName,
+            new BasicType( BasicType::Kind::LongUnsignedInt ),
+            new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
+        )
+    ));
+
+    vector<ClauseData *> clauseData;
+    genClauseInits( stmt, clauseData, body, statusName, elseWhenName );
+
+    vector<int> whenIndices; // track which clauses have whens
+
+    CompoundStmt * unregisters = new CompoundStmt( loc );
+    Expr * ifCond;
+    for ( int i = 0; i < numClauses; i++ ) {
+        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
+        // Gens: node.status != 0p
+        UntypedExpr * statusPtrCheck = new UntypedExpr( cLoc, 
+            new NameExpr( cLoc, "?!=?" ), 
+            {
+                ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicType::Kind::LongUnsignedInt ) ) ),
+                new UntypedExpr( cLoc, 
+                    new NameExpr( cLoc, "__get_clause_status" ), 
+                    { new NameExpr( cLoc, clauseData.at(i)->nodeName ) } 
+                ) 
+            }
+        );
+
+        // If we have a when_cond make the unregister call conditional on it
+        if ( stmt->clauses.at(i)->when_cond ) {
+            whenIndices.push_back(i);
+            ifCond = new LogicalExpr( cLoc,
+                new CastExpr( cLoc,
+                    new NameExpr( cLoc, clauseData.at(i)->whenName ), 
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                new CastExpr( cLoc,
+                    statusPtrCheck,
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                LogicalFlag::AndExpr
+            );
+        } else ifCond = statusPtrCheck;
+        
+        unregisters->push_back(
+            new IfStmt( cLoc,
+                ifCond,
+                new ExprStmt( cLoc, genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ) ) 
+            )
+        );
+    }
+
+    if ( whenIndices.empty() || whenIndices.size() != stmt->clauses.size() ) {
+        body->push_back(
+                new ast::TryStmt( loc,
+                new CompoundStmt( loc, { recursiveOrIfGen( stmt, clauseData, 0, elseWhenName ) } ),
+                {},
+                new ast::FinallyClause( loc, unregisters )
+            )
+        );
+    } else { // If all clauses have whens, we need to skip the waituntil if they are all false
+        Expr * outerIfCond = new NameExpr( loc, clauseData.at( whenIndices.at(0) )->whenName );
+        Expr * lastExpr = outerIfCond;
+
+        for ( vector<int>::size_type i = 1; i < whenIndices.size(); i++ ) {
+            outerIfCond = new LogicalExpr( loc,
+                new CastExpr( loc,
+                    new NameExpr( loc, clauseData.at( whenIndices.at(i) )->whenName ), 
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                new CastExpr( loc,
+                    lastExpr,
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                LogicalFlag::OrExpr
+            );
+            lastExpr = outerIfCond;
+        }
+
+        body->push_back(
+                new ast::TryStmt( loc,
+                new CompoundStmt( loc, 
+                    {
+                        new IfStmt( loc,
+                            outerIfCond,
+                            recursiveOrIfGen( stmt, clauseData, 0, elseWhenName )
+                        )
+                    }
+                ),
+                {},
+                new ast::FinallyClause( loc, unregisters )
+            )
+        );
+    }
+
+    for ( ClauseData * datum : clauseData )
+        delete datum;
+
+    return body;
+}
+
+Stmt * GenerateWaitUntilCore::postvisit( const WaitUntilStmt * stmt ) {
+    if ( !selectNodeDecl )
+        SemanticError( stmt, "waituntil statement requires #include <waituntil.hfa>" );
+
+    // Prep clause tree to figure out how to set initial statuses
+    // setTreeSizes( stmt->predicateTree );
+    if ( paintWhenTree( stmt->predicateTree ) ) // if this returns true we can special case since tree is all OR's
+        return genAllOr( stmt );
+
+    CompoundStmt * tryBody = new CompoundStmt( stmt->location );
+    CompoundStmt * body = new CompoundStmt( stmt->location );
+    string statusArrName = namer_status.newName();
+    string pCountName = namer_park.newName();
+    string satName = namer_sat.newName();
+    string runName = namer_run.newName();
+    string elseWhenName = namer_when.newName();
+    int numClauses = stmt->clauses.size();
+    addPredicates( stmt, satName, runName );
+
+    const CodeLocation & loc = stmt->location;
+
+    // Generates: int park_counter = 0;
+    body->push_back( new DeclStmt( loc,
+        new ObjectDecl( loc,
+            pCountName,
+            new BasicType( BasicType::Kind::SignedInt ),
+            new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
+        )
+    ));
+
+    // Generates: int clause_statuses[3] = { 0 };
+    body->push_back( new DeclStmt( loc,
+        new ObjectDecl( loc,
+            statusArrName,
+            new ArrayType( new BasicType( BasicType::Kind::LongUnsignedInt ), ConstantExpr::from_int( loc, numClauses ), LengthFlag::FixedLen, DimensionFlag::DynamicDim ),
+            new ListInit( loc,
+                {
+                    new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
+                }
+            )
+        )
+    ));
+
+    vector<ClauseData *> clauseData;
+    genClauseInits( stmt, clauseData, body, statusArrName, elseWhenName );
+
+    vector<pair<int, WaitUntilStmt::ClauseNode *>> ambiguousClauses;       // list of ambiguous clauses
+    vector<int> andWhenClauses;    // list of clauses that have an AND op as a direct parent and when_cond defined
+
+    collectWhens( stmt->predicateTree, ambiguousClauses, andWhenClauses );
+
+    // This is only needed for clauses that have AND as a parent and a when_cond defined
+    // generates: if ( ! when_cond_0 ) clause_statuses_0 = __SELECT_RUN;
+    for ( int idx : andWhenClauses ) {
+        const CodeLocation & cLoc = stmt->clauses.at(idx)->location;
+        body->push_back( 
+            new IfStmt( cLoc,
+                new UntypedExpr ( cLoc,
+                    new NameExpr( cLoc, "!?" ),
+                    { new NameExpr( cLoc, clauseData.at(idx)->whenName ) }
+                ),  // IfStmt cond
+                new ExprStmt( cLoc,
+                    new UntypedExpr ( cLoc,
+                        new NameExpr( cLoc, "?=?" ),
+                        {
+                            new UntypedExpr ( cLoc, 
+                                new NameExpr( cLoc, "?[?]" ),
+                                {
+                                    new NameExpr( cLoc, statusArrName ),
+                                    ConstantExpr::from_int( cLoc, idx )
+                                }
+                            ),
+                            new NameExpr( cLoc, "__SELECT_RUN" )
+                        }
+                    )
+                )  // IfStmt then
+            )
+        );
+    }
+
+    // Only need to generate conditional initial state setting for ambiguous when clauses
+    if ( !ambiguousClauses.empty() ) {
+        body->push_back( genWhenStateConditions( stmt, clauseData, ambiguousClauses, 0 ) );
+    }
+
+    // generates the following for each clause:
+    // setup_clause( clause1, &clause_statuses[0], &park_counter );
+    // register_select(A, clause1);
+    for ( int i = 0; i < numClauses; i++ ) {
+        setUpClause( stmt->clauses.at(i), clauseData.at(i), pCountName, tryBody );
+    }
+
+    // generate satisfy logic based on if there is an else clause and if it is conditional
+    if ( stmt->else_stmt && stmt->else_cond ) { // gen both else/non else branches
+        tryBody->push_back(
+            new IfStmt( stmt->else_cond->location,
+                new NameExpr( stmt->else_cond->location, elseWhenName ),
+                genElseClauseBranch( stmt, runName, statusArrName, clauseData ),
+                genNoElseClauseBranch( stmt, satName, runName, statusArrName, pCountName, clauseData )
+            )
+        );
+    } else if ( !stmt->else_stmt ) { // normal gen
+        tryBody->push_back( genNoElseClauseBranch( stmt, satName, runName, statusArrName, pCountName, clauseData ) );
+    } else { // generate just else
+        tryBody->push_back( genElseClauseBranch( stmt, runName, statusArrName, clauseData ) );
+    }
+
+    CompoundStmt * unregisters = new CompoundStmt( loc );
+    // generates for each clause: 
+    // if ( !has_run( clause_statuses[i] ) ) 
+    // OR if when_cond defined
+    // if ( when_cond_i && !has_run( clause_statuses[i] ) )
+    // body of if is:
+    // { if (unregister_select(A, clause1) && on_selected(A, clause1)) clause1->stmt; } // this conditionally runs the block unregister_select returns true (needed by some primitives)
+    Expr * ifCond;
+    UntypedExpr * statusExpr; // !clause_statuses[i]
+    for ( int i = 0; i < numClauses; i++ ) {
+        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
+
+        statusExpr = new UntypedExpr ( cLoc,
+            new NameExpr( cLoc, "!?" ),
+            {
+                new UntypedExpr ( cLoc, 
+                    new NameExpr( cLoc, "__CFA_has_clause_run" ),
+                    {
+                        genArrAccessExpr( cLoc, i, statusArrName )
+                    }
+                )
+            }
+        );
+
+        if ( stmt->clauses.at(i)->when_cond ) {
+            // generates: if( when_cond_i && !has_run(clause_statuses[i]) )
+            ifCond = new LogicalExpr( cLoc,
+                new CastExpr( cLoc,
+                    new NameExpr( cLoc, clauseData.at(i)->whenName ), 
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                new CastExpr( cLoc,
+                    statusExpr,
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                LogicalFlag::AndExpr
+            );
+        } else // generates: if( !clause_statuses[i] )
+            ifCond = statusExpr;
+        
+        unregisters->push_back( 
+            new IfStmt( cLoc,
+                ifCond,
+                new CompoundStmt( cLoc,
+                    {
+                        new IfStmt( cLoc,
+                            genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ),
+                            // ast::deepCopy( stmt->clauses.at(i)->stmt )
+                            genStmtBlock( stmt->clauses.at(i), clauseData.at(i) )
+                        )
+                    }
+                )
+                
+            )
+        );
+    }
+
+    body->push_back( 
+        new ast::TryStmt(
+            loc,
+            tryBody,
+            {},
+            new ast::FinallyClause( loc, unregisters )
+        )
+    );
+
+    for ( ClauseData * datum : clauseData )
+        delete datum;
+
+    return body;
+}
+
+// To add the predicates at global scope we need to do it in a second pass
+// Predicates are added after "struct select_node { ... };"
+class AddPredicateDecls final : public WithDeclsToAdd<> {
+    vector<FunctionDecl *> & satFns;
+    const StructDecl * selectNodeDecl = nullptr;
+
+  public:
+    void previsit( const StructDecl * decl ) {
+        if ( !decl->body ) {
+            return;
+        } else if ( "select_node" == decl->name ) {
+            assert( !selectNodeDecl );
+            selectNodeDecl = decl;
+            for ( FunctionDecl * fn : satFns )
+                declsToAddAfter.push_back(fn);            
+        }
+    }
+    AddPredicateDecls( vector<FunctionDecl *> & satFns ): satFns(satFns) {}
+};
+
+void generateWaitUntil( TranslationUnit & translationUnit ) {
+    vector<FunctionDecl *> satFns;
+	Pass<GenerateWaitUntilCore>::run( translationUnit, satFns );
+    Pass<AddPredicateDecls>::run( translationUnit, satFns );
+}
+
+} // namespace Concurrency
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Concurrency/Waituntil.hpp
===================================================================
--- src/Concurrency/Waituntil.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
+++ src/Concurrency/Waituntil.hpp	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -0,0 +1,33 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Waitfor.h --
+//
+// Author           : Thierry Delisle
+// Created On       : Mon Aug 28 11:03:31 2017
+// Last Modified By :
+// Last Modified On :
+// Update Count     : 1
+//
+
+#pragma once
+
+#include <list>  // for list
+
+class Declaration;
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace Concurrency {
+    void generateWaitUntil( ast::TranslationUnit & translationUnit );
+};
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Concurrency/module.mk
===================================================================
--- src/Concurrency/module.mk	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/Concurrency/module.mk	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -23,3 +23,5 @@
 	Concurrency/WaitforNew.cpp \
 	Concurrency/Waitfor.cc \
-	Concurrency/Waitfor.h 
+	Concurrency/Waitfor.h \
+	Concurrency/Waituntil.cpp \
+	Concurrency/Waituntil.hpp
Index: src/Parser/ParseNode.h
===================================================================
--- src/Parser/ParseNode.h	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/Parser/ParseNode.h	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -441,4 +441,8 @@
 ast::WaitForStmt * build_waitfor_else( const CodeLocation &, ast::WaitForStmt * existing, ExpressionNode * when, StatementNode * stmt );
 ast::WaitForStmt * build_waitfor_timeout( const CodeLocation &, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt );
+ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation &, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt );
+ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation &, ExpressionNode * when, StatementNode * stmt );
+ast::WaitUntilStmt::ClauseNode * build_waituntil_timeout( const CodeLocation &, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt );
+ast::WaitUntilStmt * build_waituntil_stmt( const CodeLocation &, ast::WaitUntilStmt::ClauseNode * root );
 ast::Stmt * build_with( const CodeLocation &, ExpressionNode * exprs, StatementNode * stmt );
 ast::Stmt * build_mutex( const CodeLocation &, ExpressionNode * exprs, StatementNode * stmt );
Index: src/Parser/StatementNode.cc
===================================================================
--- src/Parser/StatementNode.cc	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/Parser/StatementNode.cc	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -377,7 +377,7 @@
 ast::WaitForStmt * build_waitfor( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
 	auto clause = new ast::WaitForClause( location );
-	clause->target_func = maybeBuild( targetExpr );
+	clause->target = maybeBuild( targetExpr );
 	clause->stmt = maybeMoveBuild( stmt );
-	clause->cond = notZeroExpr( maybeMoveBuild( when ) );
+	clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
 
 	ExpressionNode * next = dynamic_cast<ExpressionNode *>( targetExpr->get_next() );
@@ -408,4 +408,93 @@
 	return existing;
 } // build_waitfor_timeout
+
+ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation & loc, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
+    ast::WhenClause * clause = new ast::WhenClause( loc );
+    clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
+    clause->stmt = maybeMoveBuild( stmt );
+    clause->target = maybeMoveBuild( targetExpr );
+    return new ast::WaitUntilStmt::ClauseNode( clause );
+}
+ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation & loc, ExpressionNode * when, StatementNode * stmt ) {
+    ast::WhenClause * clause = new ast::WhenClause( loc );
+    clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
+    clause->stmt = maybeMoveBuild( stmt );
+    return new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::ELSE, clause );
+}
+ast::WaitUntilStmt::ClauseNode * build_waituntil_timeout( const CodeLocation & loc, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt ) {
+    ast::WhenClause * clause = new ast::WhenClause( loc );
+    clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
+    clause->stmt = maybeMoveBuild( stmt );
+    clause->target = maybeMoveBuild( timeout );
+    return new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::TIMEOUT, clause );
+}
+
+ast::WaitUntilStmt * build_waituntil_stmt( const CodeLocation & loc, ast::WaitUntilStmt::ClauseNode * root ) {
+    ast::WaitUntilStmt * retStmt = new ast::WaitUntilStmt( loc );
+    retStmt->predicateTree = root;
+    
+    // iterative tree traversal
+    std::vector<ast::WaitUntilStmt::ClauseNode *> nodeStack; // stack needed for iterative traversal
+    ast::WaitUntilStmt::ClauseNode * currNode = nullptr;
+    ast::WaitUntilStmt::ClauseNode * lastInternalNode = nullptr;
+    ast::WaitUntilStmt::ClauseNode * cleanup = nullptr; // used to cleanup removed else/timeout
+    nodeStack.push_back(root);
+
+    do {
+        currNode = nodeStack.back();
+        nodeStack.pop_back(); // remove node since it will be processed
+
+        switch (currNode->op) {
+            case ast::WaitUntilStmt::ClauseNode::LEAF:
+                retStmt->clauses.push_back(currNode->leaf);
+                break;
+            case ast::WaitUntilStmt::ClauseNode::ELSE:
+                retStmt->else_stmt = currNode->leaf->stmt 
+                    ? ast::deepCopy( currNode->leaf->stmt )
+                    : nullptr;
+                
+                retStmt->else_cond = currNode->leaf->when_cond
+                    ? ast::deepCopy( currNode->leaf->when_cond )
+                    : nullptr;
+
+                delete currNode->leaf;
+                break;
+            case ast::WaitUntilStmt::ClauseNode::TIMEOUT:
+                retStmt->timeout_time = currNode->leaf->target 
+                    ? ast::deepCopy( currNode->leaf->target )
+                    : nullptr;
+                retStmt->timeout_stmt = currNode->leaf->stmt
+                    ? ast::deepCopy( currNode->leaf->stmt )
+                    : nullptr;
+                retStmt->timeout_cond = currNode->leaf->when_cond
+                    ? ast::deepCopy( currNode->leaf->when_cond )
+                    : nullptr;
+
+                delete currNode->leaf;
+                break;
+            default:
+                nodeStack.push_back( currNode->right ); // process right after left
+                nodeStack.push_back( currNode->left );
+
+                // Cut else/timeout out of the tree
+                if ( currNode->op == ast::WaitUntilStmt::ClauseNode::LEFT_OR ) {
+                    if ( lastInternalNode )
+                        lastInternalNode->right = currNode->left;
+                    else    // if not set then root is LEFT_OR 
+                        retStmt->predicateTree = currNode->left;
+    
+                    currNode->left = nullptr;
+                    cleanup = currNode;
+                }
+                
+                lastInternalNode = currNode;
+                break;
+        }
+    } while ( !nodeStack.empty() );
+
+    if ( cleanup ) delete cleanup;
+
+    return retStmt;
+}
 
 ast::Stmt * build_with( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/Parser/parser.yy	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -304,4 +304,5 @@
 	StatementNode * sn;
 	ast::WaitForStmt * wfs;
+    ast::WaitUntilStmt::ClauseNode * wuscn;
 	ast::Expr * constant;
 	CondCtl * ifctl;
@@ -425,5 +426,6 @@
 %type<en> when_clause					when_clause_opt				waitfor		waituntil		timeout
 %type<sn> waitfor_statement				waituntil_statement
-%type<wfs> wor_waitfor_clause			waituntil_clause			wand_waituntil_clause	wor_waituntil_clause
+%type<wfs> wor_waitfor_clause
+%type<wuscn> waituntil_clause			wand_waituntil_clause       wor_waituntil_clause
 
 // declarations
@@ -1683,30 +1685,33 @@
 waituntil_clause:
 	when_clause_opt waituntil statement
-		{ printf( "waituntil_clause 1\n" ); $$ = nullptr; }
+		{ $$ = build_waituntil_clause( yylloc, $1, $2, maybe_build_compound( yylloc, $3 ) ); }
 	| '(' wor_waituntil_clause ')'
-		{ printf( "waituntil_clause 2\n" ); $$ = nullptr; }
+		{ $$ = $2; }
 	;
 
 wand_waituntil_clause:
 	waituntil_clause									%prec THEN
-		{ printf( "wand_waituntil_clause 1\n" ); $$ = nullptr; }
+		{ $$ = $1; }
 	| waituntil_clause wand wand_waituntil_clause
-		{ printf( "wand_waituntil_clause 2\n" ); $$ = nullptr; }
+		{ $$ = new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::AND, $1, $3 ); }
 	;
 
 wor_waituntil_clause:
 	wand_waituntil_clause
-		{ printf( "wor_waituntil_clause 1\n" ); $$ = nullptr; }
+		{ $$ = $1; }
 	| wor_waituntil_clause wor wand_waituntil_clause
-		{ printf( "wor_waituntil_clause 2\n" ); $$ = nullptr; }
+		{ $$ = new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::OR, $1, $3 ); }
 	| wor_waituntil_clause wor when_clause_opt ELSE statement
-		{ printf( "wor_waituntil_clause 3\n" ); $$ = nullptr; }
+		{ $$ = new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::LEFT_OR, $1, build_waituntil_else( yylloc, $3, maybe_build_compound( yylloc, $5 ) ) ); }
 	| wor_waituntil_clause wor when_clause_opt timeout statement	%prec THEN
-		{ printf( "wor_waituntil_clause 4\n" ); $$ = nullptr; }
+		{ $$ = new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::LEFT_OR, $1, build_waituntil_timeout( yylloc, $3, $4, maybe_build_compound( yylloc, $5 ) ) ); }
 	// "else" must be conditional after timeout or timeout is never triggered (i.e., it is meaningless)
 	| wor_waituntil_clause wor when_clause_opt timeout statement wor ELSE statement // syntax error
 		{ SemanticError( yylloc, "else clause must be conditional after timeout or timeout never triggered." ); $$ = nullptr; }
 	| wor_waituntil_clause wor when_clause_opt timeout statement wor when_clause ELSE statement
-		{ printf( "wor_waituntil_clause 6\n" ); $$ = nullptr; }
+		{ $$ = new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::LEFT_OR, $1,
+                new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::OR, 
+                    build_waituntil_timeout( yylloc, $3, $4, maybe_build_compound( yylloc, $5 ) ), 
+                    build_waituntil_else( yylloc, $7, maybe_build_compound( yylloc, $9 ) ) ) ); }
 	;
 
@@ -1714,5 +1719,8 @@
 	wor_waituntil_clause								%prec THEN
 		// SKULLDUGGERY: create an empty compound statement to test parsing of waituntil statement.
-		{ $$ = new StatementNode( build_compound( yylloc, nullptr ) ); }
+		{
+            $$ = new StatementNode( build_waituntil_stmt( yylloc, $1 ) );
+            // $$ = new StatementNode( build_compound( yylloc, nullptr ) );
+        }
 	;
 
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/ResolvExpr/Resolver.cc	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -1730,10 +1730,10 @@
 
 			// Find all candidates for a function in canonical form
-			funcFinder.find( clause.target_func, ResolvMode::withAdjustment() );
+			funcFinder.find( clause.target, ResolvMode::withAdjustment() );
 
 			if ( funcFinder.candidates.empty() ) {
 				stringstream ss;
 				ss << "Use of undeclared indentifier '";
-				ss << clause.target_func.strict_as< ast::NameExpr >()->name;
+				ss << clause.target.strict_as< ast::NameExpr >()->name;
 				ss << "' in call to waitfor";
 				SemanticError( stmt->location, ss.str() );
@@ -1922,5 +1922,5 @@
 			auto clause2 = new ast::WaitForClause( clause.location );
 
-			clause2->target_func = funcCandidates.front()->expr;
+			clause2->target = funcCandidates.front()->expr;
 
 			clause2->target_args.reserve( clause.target_args.size() );
@@ -1945,5 +1945,5 @@
 
 			// Resolve the conditions as if it were an IfStmt, statements normally
-			clause2->cond = findSingleExpression( clause.cond, context );
+			clause2->when_cond = findSingleExpression( clause.when_cond, context );
 			clause2->stmt = clause.stmt->accept( *visitor );
 
Index: src/main.cc
===================================================================
--- src/main.cc	(revision 73bf7ddcf37d065575ed554dfb7dbd70672e83f8)
+++ src/main.cc	(revision c86b08de42d13ee7646e48f36c60ff65633dc0ea)
@@ -47,4 +47,5 @@
 #include "Concurrency/Keywords.h"           // for implementMutex, implement...
 #include "Concurrency/Waitfor.h"            // for generateWaitfor
+#include "Concurrency/Waituntil.hpp"        // for generateWaitUntil
 #include "ControlStruct/ExceptDecl.h"       // for translateExcept
 #include "ControlStruct/ExceptTranslate.h"  // for translateThrows, translat...
@@ -337,4 +338,5 @@
 		PASS( "Implement Concurrent Keywords", Concurrency::implementKeywords( transUnit ) );
 		PASS( "Forall Pointer Decay", Validate::decayForallPointers( transUnit ) );
+        PASS( "Implement Waituntil", Concurrency::generateWaitUntil( transUnit ) );
 		PASS( "Hoist Control Declarations", ControlStruct::hoistControlDecls( transUnit ) );
 
@@ -343,5 +345,4 @@
 		PASS( "Implement Actors", Concurrency::implementActors( transUnit ) );
         PASS( "Implement Virtual Destructors", Virtual::implementVirtDtors(transUnit) );
-        
 		PASS( "Implement Mutex", Concurrency::implementMutex( transUnit ) );
 		PASS( "Implement Thread Start", Concurrency::implementThreadStarter( transUnit ) );
