template<bool enforce_variables>
std::pair<UnpackedInput, InputFlags> unpack_input(PyObject *args) {
  ...
  flags.next_edges = (flags.is_executable ? collect_next_edges(unpacked.input_vars) : edge_list());
  return std::make_pair(std::move(unpacked), std::move(flags));
}

After this, the edges are assigned to the grad_fn by just doing cdata->set_next_edges(std::move(input_info.next_edges)); and the forward function is called through the python interpreter C API.
Once the output tensors are returned from the forward pass, they are processed and converted to variables inside the process_outputs function.
```c++
PyObject process_outputs(PyObject op_obj, const std::shared_ptr& cdata,
                          THPFunction grad_fn, const UnpackedInput& unpacked,
                          PyObject inputs, THPObjectPtr&& raw_output, bool is_executable,