fn uniquify_helper(expr: &mut Expr, symbol_stack: &mut SymbolStack) -> WeldResult<()> {
    match expr.kind {
        // First, handle expressions which define *new* symbols - Let and Lambda
        Lambda {
            ref mut params,
            ref mut body,
        } => {
            // Update the parameter of the lambda with new names.
            let original_params = params.clone();
            for param in params.iter_mut() {
                let sym = &mut param.name;
                symbol_stack.push_symbol(sym.clone());
                *sym = symbol_stack.symbol(sym.clone())?;
            }

            // Then, uniquify the lambda using the newly pushed symbols.
            uniquify_helper(body, symbol_stack)?;

            // Finally, pop off the symbol names since they are out of scope now.
            for param in original_params {
                symbol_stack.pop_symbol(param.name)?;
            }
        }
        Let {
            ref mut name,
            ref mut value,
            ref mut body,
        } => {
            // First, uniquify the value *without* the updated stack, since the Let hasn't defined
            // the symbol yet.
            uniquify_helper(value, symbol_stack)?;

            // Now, push the Let's symbol name.
            symbol_stack.push_symbol(name.clone());
            let original_name = name.clone();
            *name = symbol_stack.symbol(name.clone())?;

            // uniquify the body with the new scope.
            uniquify_helper(body, symbol_stack)?;

            // Pop off the scope.
            symbol_stack.pop_symbol(original_name)?;
        }
        // Now handle identifiers, which are changed to reflect new symbols.
        Ident(ref mut sym) => {
            *sym = symbol_stack.symbol(sym.clone())?;
        }
        // For all other expressions, call uniquify_helper on the children.
        _ => {
            for child in expr.children_mut() {
                uniquify_helper(child, symbol_stack)?;
            }
        }
    }
    Ok(())
}
The main helper function for uniquify, which uses `SymbolStack` to track scope and assign
unique names to each symbol. The prerequisite is that each symbol has `id = 0`.
weld/src/ast/uniquify.rs