pub fn resolve_scopes(&mut self, node: Option<Index>) {
        // Depth-first once-only traversal of CFG, skipping nodes we've already seen
        let mut visited = HashSet::new();

        // The stack tracks pairs of implicit continuations (the absence of which indicates the
        // `Done` continuation), and indices which might have that continuation or have children
        // which might have that continuation. We push newly discovered nodes onto this stack, and
        // pop from it to drive the traversal. We initialize the stack with the root node, if it is
        // present.
        let mut stack = vec![];
        stack.extend(node.map(|root| (None, root)));

        while let Some((implicit_cont, node)) = stack.pop() {
            // "Follow" a node by checking to see if we have visited it, and if not, pushing it on
            // to the stack to be visited later along with the supplied scoped implicit
            // continuation.
            let mut follow = |implicit_cont, node| {
                if let Some(node) = node {
                    if visited.insert(node) {
                        stack.push((implicit_cont, node));
                    }
                }
            };

            // This match is followed by a statement which checks for a continuation and adds it to
            // the queue if it finds one. As such, any branch which needs specifically to not check
            // its continuation and recursively resolve scopes inside it, will be ended with a
            // `continue`, to skip this recursion.
            //
            // Specifically this is *very* important for nodes such as `Offer` and `Choose`, which
            // will no longer have a continuation at all after this pass, as their continuations
            // will be "lifted" into each arm of the node.
            let CfgNode { expr, next, .. } = &mut self[node];
            match expr {
                Ir::Recv(_)
                | Ir::Send(_)
                | Ir::Type(_)
                | Ir::Break(_)
                | Ir::Continue(_)
                | Ir::Error => {}

                Ir::Call(child) => {
                    // `call` expressions evaluate until done; there is no implicit continuation
                    // this is why we pass the `None` implicit continuation to `follow`
                    follow(None, *child);
                }
                Ir::Split { tx_only, rx_only } => {
                    // `split` expressions evaluate until done; there is no implicit continuation
                    // this is why we pass the `None` implicit continuation to `follow`
                    follow(None, *tx_only);
                    follow(None, *rx_only);
                }
                Ir::Choose(choices) | Ir::Offer(choices) => {
                    // Inline the current implicit continuation into the next pointer of the node,
                    // if it is `Some`
                    follow(implicit_cont, *next);
                    // *Take* the next pointer out so that it is now None, as we are eliminating the
                    // continuation of this node
                    let new_implicit_cont = next.take().or(implicit_cont);

                    // Follow each arm of the `choose`/`offer` using the new implicit continuation
                    for choice in choices.iter_mut() {
                        // If the choice is `Done`, we need to reassign it to the new implicit
                        // continuation. Otherwise, we follow it.
                        if choice.is_some() {
                            follow(new_implicit_cont, *choice);
                        } else {
                            *choice = new_implicit_cont;
                        }
                    }
                }
                Ir::Loop(body) => {
                    let body = *body;
                    let continue0 = self.singleton(Ir::Continue(node));
                    // We generated this without knowing for sure whether it is reachable or not, so
                    // mark it machine-generated so that it doesn't trigger an unreachable code
                    // error.
                    self[continue0].allow_unreachable = true;
                    if let Some(body) = body {
                        // If the loop has a body, process it using the implicit continuation that
                        // continues to the loop itself
                        follow(Some(continue0), Some(body));
                    } else {
                        // Assign the body of the loop to `continue`: this will become an error in a
                        // later pass, because `loop { continue }` is unproductive
                        self[node].expr = Ir::Loop(Some(continue0));
                    }
                }
            }

            // Reborrow here because the `Loop` clause loses the borrow on self from expr/next.
            let CfgNode { expr, next, .. } = &mut self[node];
            match next {
                // If the next pointer exists, follow it and continue converting its syntax tree,
                // with the implicit continuation of *our current scope* (i.e. if we're in a `Loop`,
                // we're still in that `Loop` after following the continuation)
                Some(next) => follow(implicit_cont, Some(*next)),
                // If there is no explicit continuation for this node, *and* it is not a `Choose` or
                // `Offer` (which have had their explicit continuations erased and lowered into the
                // arms) then we need to assign the scoped implicit continuation, if there is one,
                // as the new explicit continuation
                None if !matches!(expr, Ir::Choose(_) | Ir::Offer(_)) => *next = implicit_cont,
                // This will only be reached if there is no explicit continuation and the node is a
                // `Choose` or `Offer`, in which case we want to do nothing.
                _ => {}
            }
        }
    }
/// The purpose of the scope resolution pass is to transform "implicit" continuations into
/// "explicit" continuations. For example, consider the session type `choose { ... }; send ()`.
/// In this type, the first parsing of the block will result in a `Choose` node with its
/// continuation/"next" pointer set to point to a `Send(())` node. However, in order to ensure
/// that the arms of the choose construct don't have to worry about the continuation that comes
/// "after" *in a higher/parent scope,* we have the resolve_scopes pass to "lower" this
/// continuation which implicitly follows every arm of the `Choose`, into becoming the
/// continuation of every relevant arm of the `Choose`. This does have some special cases, for
/// example we don't want to change or set a next continuation for a `Break` node or `Continue`
/// node.
The purpose of the scope resolution pass is to transform "implicit" continuations into
"explicit" continuations. For example, consider the session type `choose { ...
The purpose of the scope resolution pass is to transform " implicit " continuations into " explicit " continuations . For example consider the session type ` choose { ...
pub fn resolve_scopes(&mut self, node: Option<Index>) {
        // Depth-first once-only traversal of CFG, skipping nodes we've already seen
        let mut visited = HashSet::new();
        // The stack tracks pairs of implicit continuations (the absence of which indicates the
        // `Done` continuation), and indices which might have that continuation or have children
        // which might have that continuation. We push newly discovered nodes onto this stack, and
        // pop from it to drive the traversal. We initialize the stack with the root node, if it is
        // present.
        let mut stack = vec![];
        stack.extend(node.map(|root| (None, root)));
        while let Some((implicit_cont, node)) = stack.pop() {
            // "Follow" a node by checking to see if we have visited it, and if not, pushing it on
            // to the stack to be visited later along with the supplied scoped implicit
            // continuation.
            let mut follow = |implicit_cont, node| {
                if let Some(node) = node {
                    if visited.insert(node) {
                        stack.push((implicit_cont, node));
                    }
                }
            };
            // This match is followed by a statement which checks for a continuation and adds it to
            // the queue if it finds one. As such, any branch which needs specifically to not check
            // its continuation and recursively resolve scopes inside it, will be ended with a
            // `continue`, to skip this recursion.
            //
            // Specifically this is *very* important for nodes such as `Offer` and `Choose`, which
            // will no longer have a continuation at all after this pass, as their continuations
            // will be "lifted" into each arm of the node.
            let CfgNode { expr, next, .. } = &mut self[node];
            match expr {
                Ir::Recv(_)
                | Ir::Send(_)
                | Ir::Type(_)
                | Ir::Break(_)
                | Ir::Continue(_)
                | Ir::Error => {}
                Ir::Call(child) => {
                    // `call` expressions evaluate until done; there is no implicit continuation
                    // this is why we pass the `None` implicit continuation to `follow`
                    follow(None, *child);
                }
                Ir::Split { tx_only, rx_only } => {
                    // `split` expressions evaluate until done; there is no implicit continuation
                    // this is why we pass the `None` implicit continuation to `follow`
                    follow(None, *tx_only);
                    follow(None, *rx_only);
                }
                Ir::Choose(choices) | Ir::Offer(choices) => {
                    // Inline the current implicit continuation into the next pointer of the node,
                    // if it is `Some`
                    follow(implicit_cont, *next);
                    // *Take* the next pointer out so that it is now None, as we are eliminating the
                    // continuation of this node
                    let new_implicit_cont = next.take().or(implicit_cont);
                    // Follow each arm of the `choose`/`offer` using the new implicit continuation
                    for choice in choices.iter_mut() {
                        // If the choice is `Done`, we need to reassign it to the new implicit
                        // continuation. Otherwise, we follow it.
                        if choice.is_some() {
                            follow(new_implicit_cont, *choice);
                        } else {
                            *choice = new_implicit_cont;
                        }
                    }
                }
                Ir::Loop(body) => {
                    let body = *body;
                    let continue0 = self.singleton(Ir::Continue(node));
                    // We generated this without knowing for sure whether it is reachable or not, so
                    // mark it machine-generated so that it doesn't trigger an unreachable code
                    // error.
                    self[continue0].allow_unreachable = true;
                    if let Some(body) = body {
                        // If the loop has a body, process it using the implicit continuation that
                        // continues to the loop itself
                        follow(Some(continue0), Some(body));
                    } else {
                        // Assign the body of the loop to `continue`: this will become an error in a
                        // later pass, because `loop { continue }` is unproductive
                        self[node].expr = Ir::Loop(Some(continue0));
                    }
                }
            }
            // Reborrow here because the `Loop` clause loses the borrow on self from expr/next.
            let CfgNode { expr, next, .. } = &mut self[node];
            match next {
                // If the next pointer exists, follow it and continue converting its syntax tree,
                // with the implicit continuation of *our current scope* (i.e. if we're in a `Loop`,
                // we're still in that `Loop` after following the continuation)
                Some(next) => follow(implicit_cont, Some(*next)),
                // If there is no explicit continuation for this node, *and* it is not a `Choose` or
                // `Offer` (which have had their explicit continuations erased and lowered into the
                // arms) then we need to assign the scoped implicit continuation, if there is one,
                // as the new explicit continuation
                None if !matches!(expr, Ir::Choose(_) | Ir::Offer(_)) => *next = implicit_cont,
                // This will only be reached if there is no explicit continuation and the node is a
                // `Choose` or `Offer`, in which case we want to do nothing.
                _ => {}
            }
        }
    }
pub fn resolve_scopes ( & mut self , node : Option < Index > ) { // Depth-first once-only traversal of CFG, skipping nodes we've already seen let mut visited = HashSet :: new ( ) ; // The stack tracks pairs of implicit continuations (the absence of which indicates the // `Done` continuation), and indices which might have that continuation or have children // which might have that continuation. We push newly discovered nodes onto this stack, and // pop from it to drive the traversal. We initialize the stack with the root node, if it is // present. let mut stack = vec ! [ ] ; stack . extend ( node . map ( | root | ( None , root ) ) ) ; while let Some ( ( implicit_cont , node ) ) = stack . pop ( ) { // "Follow" a node by checking to see if we have visited it, and if not, pushing it on // to the stack to be visited later along with the supplied scoped implicit // continuation. let mut follow = | implicit_cont , node | { if let Some ( node ) = node { if visited . insert ( node ) { stack . push ( ( implicit_cont , node ) ) ; } } } ; // This match is followed by a statement which checks for a continuation and adds it to // the queue if it finds one. As such, any branch which needs specifically to not check // its continuation and recursively resolve scopes inside it, will be ended with a // `continue`, to skip this recursion. // // Specifically this is *very* important for nodes such as `Offer` and `Choose`, which // will no longer have a continuation at all after this pass, as their continuations // will be "lifted" into each arm of the node. let CfgNode { expr , next , .. } = & mut self [ node ] ; match expr { Ir :: Recv ( _ ) | Ir :: Send ( _ ) | Ir :: Type ( _ ) | Ir :: Break ( _ ) | Ir :: Continue ( _ ) | Ir :: Error => { } Ir :: Call ( child ) => { // `call` expressions evaluate until done; there is no implicit continuation // this is why we pass the `None` implicit continuation to `follow` follow ( None , * child ) ; } Ir :: Split { tx_only , rx_only } => { // `split` expressions evaluate until done; there is no implicit continuation // this is why we pass the `None` implicit continuation to `follow` follow ( None , * tx_only ) ; follow ( None , * rx_only ) ; } Ir :: Choose ( choices ) | Ir :: Offer ( choices ) => { // Inline the current implicit continuation into the next pointer of the node, // if it is `Some` follow ( implicit_cont , * next ) ; // *Take* the next pointer out so that it is now None, as we are eliminating the // continuation of this node let new_implicit_cont = next . take ( ) . or ( implicit_cont ) ; // Follow each arm of the `choose`/`offer` using the new implicit continuation for choice in choices . iter_mut ( ) { // If the choice is `Done`, we need to reassign it to the new implicit // continuation. Otherwise, we follow it. if choice . is_some ( ) { follow ( new_implicit_cont , * choice ) ; } else { * choice = new_implicit_cont ; } } } Ir :: Loop ( body ) => { let body = * body ; let continue0 = self . singleton ( Ir :: Continue ( node ) ) ; // We generated this without knowing for sure whether it is reachable or not, so // mark it machine-generated so that it doesn't trigger an unreachable code // error. self [ continue0 ] . allow_unreachable = true ; if let Some ( body ) = body { // If the loop has a body, process it using the implicit continuation that // continues to the loop itself follow ( Some ( continue0 ) , Some ( body ) ) ; } else { // Assign the body of the loop to `continue`: this will become an error in a // later pass, because `loop { continue }` is unproductive self [ node ] . expr = Ir :: Loop ( Some ( continue0 ) ) ; } } } // Reborrow here because the `Loop` clause loses the borrow on self from expr/next. let CfgNode { expr , next , .. } = & mut self [ node ] ; match next { // If the next pointer exists, follow it and continue converting its syntax tree, // with the implicit continuation of *our current scope* (i.e. if we're in a `Loop`, // we're still in that `Loop` after following the continuation) Some ( next ) => follow ( implicit_cont , Some ( * next ) ) , // If there is no explicit continuation for this node, *and* it is not a `Choose` or // `Offer` (which have had their explicit continuations erased and lowered into the // arms) then we need to assign the scoped implicit continuation, if there is one, // as the new explicit continuation None if ! matches ! ( expr Ir Choose ( ) Ir Offer ( ) ) => * next = implicit_cont , // This will only be reached if there is no explicit continuation and the node is a // `Choose` or `Offer`, in which case we want to do nothing. _ => { } } } }
The purpose of the scope resolution pass is to transform "implicit" continuations into
"explicit" continuations.
The purpose of the scope resolution pass is to transform " implicit " continuations into " explicit " continuations .
&mut self
node
Option<Index>