fn vectorizable(for_loop: &Expr) -> Option<HashSet<Symbol>> {
    if let For {
        ref iters,
        builder: ref init_builder,
        ref func,
    } = for_loop.kind
    {
        // Check if the iterators are consumed.
        if vectorizable_iters(&iters) {
            // Check if at least one of the builders can be vectorized.
            if let Some(true) = vectorizable_builder(init_builder) {
                // Check the loop function.
                if let Lambda {
                    ref params,
                    ref body,
                } = func.kind
                {
                    let mut passed = true;

                    // Identifiers defined within the loop.
                    let mut defined_in_loop = HashSet::new();
                    for param in params.iter() {
                        defined_in_loop.insert(param.name.clone());
                    }

                    // Check if subexpressions in the body are all vectorizable.
                    body.traverse(&mut |f| {
                        if passed {
                            match f.kind {
                                Literal(_) => {}

                                Ident(ref name) => {
                                    if f.ty == params[1].ty && *name == params[1].name {
                                        // Used an index expression in the loop body.
                                        passed = false;
                                    }
                                }

                                UnaryOp { .. } => {}
                                BinOp { .. } => {}

                                Let { ref name, .. } => {
                                    defined_in_loop.insert(name.clone());
                                }

                                // TODO: do we want to allow all GetFields and MakeStructs, or look inside them?
                                GetField { .. } => {}

                                MakeStruct { .. } => {}

                                Merge { .. } => {}

                                Select { .. } => {}

                                _ => {
                                    passed = false;
                                }
                            }
                        }
                    });

                    if !passed {
                        trace!("Vectorization failed due to unsupported expression in loop body");
                        return None;
                    }

                    // If the data in the vector is not a Scalar, we can't vectorize it.
                    let mut check_arg_ty = false;
                    if let Scalar(_) = params[2].ty {
                        check_arg_ty = true;
                    } else if let Struct(ref field_tys) = params[2].ty {
                        if field_tys.iter().all(|t| match *t {
                            Scalar(_) => true,
                            _ => false,
                        }) {
                            check_arg_ty = true;
                        }
                    }

                    if !check_arg_ty {
                        trace!("Vectorization failed due to unsupported type");
                        return None;
                    }

                    let mut idens = HashSet::new();

                    // Check if there are identifiers defined outside the loop. If so, we need to
                    // broadcast them to vectorize them.
                    let mut passed = true;
                    body.traverse(&mut |e| match e.kind {
                        Ident(ref name) if !defined_in_loop.contains(name) => {
                            if let Scalar(_) = e.ty {
                                idens.insert(name.clone());
                            } else {
                                passed = false;
                            }
                        }
                        _ => {}
                    });

                    if !passed {
                        trace!("Unsupported pattern: non-scalar identifier that must be broadcast");
                        return None;
                    }
                    return Some(idens);
                }
            }
        }
    }
    trace!("Vectorization failed due to unsupported pattern");
    None
}
Checks basic vectorizability for a loop - this is a strong check which ensure that the only
expressions which appear in a function body are vectorizable expressions (see
`docs/internals/vectorization.md` for details)
weld/src/optimizer/transforms/vectorizer.rs