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

Code before:
    Term::Chn { bod, .. } => term_to_affine(bod, var_uses, let_bodies),
    Term::App { fun: fst, arg: snd, .. }
    | Term::Sup { fst, snd, .. }
    | Term::Tup { fst, snd }
    | Term::Opx { fst, snd, .. } => {
      term_to_affine(fst, var_uses, let_bodies);
      term_to_affine(snd, var_uses, let_bodies);
    }
    Term::Match { scrutinee, arms } => {
      term_to_affine(scrutinee, var_uses, let_bodies);
      for (rule, term) in arms {
        match rule {
          Pattern::Num(MatchNum::Succ(Some(nam))) => {
            term_with_bind_to_affine(term, nam, var_uses, let_bodies)
          }
          Pattern::Num(_) => term_to_affine(term, var_uses, let_bodies),
          _ => unreachable!(),
        }
      }
    }
    Term::List { .. } => unreachable!("Should have been desugared already"),
    Term::Era | Term::Lnk { .. } | Term::Ref { .. } | Term::Num { .. } | Term::Str { .. } => (),
  };
}

fn check_unscoped(term: &Term) -> bool {
  match term {
    Term::Lnk { .. } | Term::Chn { .. } => true,
    Term::Lam { bod, .. } => check_unscoped(&bod),
    Term::Let { val: fst, nxt: snd, .. }
    | Term::Dup { val: fst, nxt: snd, .. }
    | Term::App { fun: fst, arg: snd, .. }
    | Term::Tup { fst, snd }
    | Term::Sup { fst, snd, .. }
    | Term::Opx { fst, snd, .. } => check_unscoped(&fst) || check_unscoped(&snd),
    Term::Match { scrutinee, arms } => {
      if check_unscoped(&scrutinee) {
        return true;
      }

      for (_, arm) in arms {
        if check_unscoped(arm) {
          return true;
        }
      }

      false
    }
    Term::Num { .. }
    | Term::Str { .. }
    | Term::List { .. }

Review comment (suggestion):
```suggestion
fn has_unscoped(term: &Term) -> bool {
```

Code after:
      }
    }

    Term::Dup { fst, snd, val, nxt, .. }
    | Term::Let { pat: Pattern::Tup(box Pattern::Var(fst), box Pattern::Var(snd)), val, nxt } => {
      let uses_fst = get_var_uses(fst.as_ref(), var_uses);
      let uses_snd = get_var_uses(snd.as_ref(), var_uses);
      term_to_affine(val, var_uses, let_bodies);
      term_to_affine(nxt, var_uses, let_bodies);
      duplicate_lam(fst, nxt, uses_fst);
      duplicate_lam(snd, nxt, uses_snd);
    }

    Term::Let { .. } => unreachable!(),

    // Var-using terms
    Term::Var { nam } => {
      let uses = var_uses[nam];
      *var_uses.get_mut(nam).unwrap() -= 1;
      if let Some(subst) = let_bodies.remove(nam) {
        *term = subst.clone();
      } else {
        let used_counter = used_var_counter(nam);

        *nam = dup_name(nam, uses);

        // Updates de actual var uses on the resulting term
        *var_uses.entry(used_counter).or_default() += 1;
      }
    }

    // Others
    Term::Chn { bod, .. } => term_to_affine(bod, var_uses, let_bodies),
    Term::App { fun: fst, arg: snd, .. }
    | Term::Sup { fst, snd, .. }
    | Term::Tup { fst, snd }
    | Term::Opx { fst, snd, .. } => {
      term_to_affine(fst, var_uses, let_bodies);
      term_to_affine(snd, var_uses, let_bodies);
    }
    Term::Match { scrutinee, arms } => {
      term_to_affine(scrutinee, var_uses, let_bodies);
      for (rule, term) in arms {
        match rule {
          Pattern::Num(MatchNum::Succ(Some(nam))) => {
            term_with_bind_to_affine(term, nam, var_uses, let_bodies)
          }
          Pattern::Num(_) => term_to_affine(term, var_uses, let_bodies),
          _ => unreachable!(),
        }
      }