Repository: HigherOrderCO/Bend
File: src/term/transform/linearize.rs (Rust)

Code before:
        }
      }
    }
    Term::Let { pat: LetPat::Tup(l_nam, r_nam), val, nxt } => {
      let fst_uses = l_nam.as_ref().map(|l_nam| *var_uses.get(l_nam).unwrap()).unwrap_or(0);
      let snd_uses = r_nam.as_ref().map(|r_nam| *var_uses.get(r_nam).unwrap()).unwrap_or(0);

      match (fst_uses, snd_uses) {
        (0, 0) => *nxt,
        (_, 0) => match *val {
          Term::Tup { fst, .. } => {
            let let_ = Term::Let { pat: LetPat::Var(l_nam.unwrap()), val: fst, nxt };
            term_to_affine(let_, var_uses, let_bodies)?
          }
          _ => unimplemented!(),
        },
        (0, _) => match *val {
          Term::Tup { snd, .. } => {
            let let_ = Term::Let { pat: LetPat::Var(r_nam.unwrap()), val: snd, nxt };
            term_to_affine(let_, var_uses, let_bodies)?
          }
          _ => unimplemented!(),
        },
        (_, _) => {
          let val = term_to_affine(*val, var_uses, let_bodies)?;
          let nxt = term_to_affine(*nxt, var_uses, let_bodies)?;
          let (nxt, l_nam) = duplicate_lam(l_nam.unwrap(), nxt, fst_uses);
          let (nxt, r_nam) = duplicate_lam(r_nam.unwrap(), nxt, snd_uses);
          Term::Let { pat: LetPat::Tup(l_nam, r_nam), val: Box::new(val), nxt: Box::new(nxt) }
        }
      }
    }
    Term::Dup { fst, snd, val, nxt } => {
      let uses_fst = fst.as_ref().map(|fst| *var_uses.get(fst).unwrap()).unwrap_or(0);
      let uses_snd = snd.as_ref().map(|snd| *var_uses.get(snd).unwrap()).unwrap_or(0);
      let val = term_to_affine(*val, var_uses, let_bodies)?;
      let nxt = term_to_affine(*nxt, var_uses, let_bodies)?;
      let (nxt, fst) = if let Some(fst) = fst { duplicate_lam(fst, nxt, uses_fst) } else { (nxt, fst) };
      let (nxt, snd) = if let Some(snd) = snd { duplicate_lam(snd, nxt, uses_snd) } else { (nxt, snd) };
      Term::Dup { fst, snd, val: Box::new(val), nxt: Box::new(nxt) }
    }
    Term::Match { cond, zero, succ } => {
      let cond = term_to_affine(*cond, var_uses, let_bodies)?;
      let zero = term_to_affine(*zero, var_uses, let_bodies)?;
      let succ = term_to_affine(*succ, var_uses, let_bodies)?;
      Term::Match { cond: Box::new(cond), zero: Box::new(zero), succ: Box::new(succ) }
    }
    Term::App { fun, arg } => Term::App {
      fun: Box::new(term_to_affine(*fun, var_uses, let_bodies)?),
      arg: Box::new(term_to_affine(*arg, var_uses, let_bodies)?),
    },

Review comment (suggestion):
Tup is not the only body that we expect here. For example:
`((@x let (a, b) = x; (+ a b)) (1, 2))`
This is a valid term that in the context of this pass should remain the same.

Code after:
        }
      }
    }
    Term::Let { pat: LetPat::Tup(l_nam, r_nam), val, nxt } => {
      // TODO: check if val is a Tuple
      let fst_uses = l_nam.as_ref().map(|l_nam| *var_uses.get(l_nam).unwrap()).unwrap_or(0);
      let snd_uses = r_nam.as_ref().map(|r_nam| *var_uses.get(r_nam).unwrap()).unwrap_or(0);
      let val = term_to_affine(*val, var_uses, let_bodies)?;
      let nxt = term_to_affine(*nxt, var_uses, let_bodies)?;
      let (nxt, fst) = if let Some(fst) = l_nam { duplicate_lam(fst, nxt, fst_uses) } else { (nxt, l_nam) };
      let (nxt, snd) = if let Some(snd) = r_nam { duplicate_lam(snd, nxt, snd_uses) } else { (nxt, r_nam) };
      Term::Let { pat: LetPat::Tup(fst, snd), val: Box::new(val), nxt: Box::new(nxt) }
    }
    Term::Dup { fst, snd, val, nxt } => {
      let uses_fst = fst.as_ref().map(|fst| *var_uses.get(fst).unwrap()).unwrap_or(0);
      let uses_snd = snd.as_ref().map(|snd| *var_uses.get(snd).unwrap()).unwrap_or(0);
      let val = term_to_affine(*val, var_uses, let_bodies)?;
      let nxt = term_to_affine(*nxt, var_uses, let_bodies)?;
      let (nxt, fst) = if let Some(fst) = fst { duplicate_lam(fst, nxt, uses_fst) } else { (nxt, fst) };
      let (nxt, snd) = if let Some(snd) = snd { duplicate_lam(snd, nxt, uses_snd) } else { (nxt, snd) };
      Term::Dup { fst, snd, val: Box::new(val), nxt: Box::new(nxt) }
    }
    Term::Match { cond, zero, succ } => {
      let cond = term_to_affine(*cond, var_uses, let_bodies)?;
      let zero = term_to_affine(*zero, var_uses, let_bodies)?;
      let succ = term_to_affine(*succ, var_uses, let_bodies)?;
      Term::Match { cond: Box::new(cond), zero: Box::new(zero), succ: Box::new(succ) }
    }
    Term::App { fun, arg } => Term::App {
      fun: Box::new(term_to_affine(*fun, var_uses, let_bodies)?),
      arg: Box::new(term_to_affine(*arg, var_uses, let_bodies)?),
    },
    Term::Sup { fst, snd } => Term::Sup {
      fst: Box::new(term_to_affine(*fst, var_uses, let_bodies)?),
      snd: Box::new(term_to_affine(*snd, var_uses, let_bodies)?),
    },
    Term::Opx { op, fst, snd } => Term::Opx {
      op,
      fst: Box::new(term_to_affine(*fst, var_uses, let_bodies)?),
      snd: Box::new(term_to_affine(*snd, var_uses, let_bodies)?),
    },
    Term::Tup { fst, snd } => Term::Tup {
      fst: Box::new(term_to_affine(*fst, var_uses, let_bodies)?),
      snd: Box::new(term_to_affine(*snd, var_uses, let_bodies)?),
    },
    t @ (Term::Era | Term::Lnk { .. } | Term::Ref { .. } | Term::Num { .. }) => t,
  };
  Ok(term)
}

fn make_dup_tree(nam: &Name, mut nxt: Term, uses: Val, dup_body: Option<Term>) -> Term {