I’ve decided that it’s best to have Callee::Global and Callee::Local in the Call instruction. It ensures that I don’t have to duplicate code in places that I don’t care about the callee.


Type Inference

I’m trying to figure out the type inference for function pointers. I’ve started by looking at SourceTypes::gen_constraints in lang/stypes.rs. Specifically, I’m looking at the SIInstr::Call branch to understand it better.

It looks like it currently uses func_id (for global functions) in two places:

// lhs type inference
if let Some(lhs) = lhs {
    let ty = &llvm_instr.get_type(&ctx.module.types);
    constraints.add_has_type(*lhs, ty.clone());
    constraints.add_type_equiv(*lhs, func_id.clone());
}
// parameter type inference
let params = &ctx
    .funcs
    .get(func_id)
    .expect(
        "All called functions should have been \
        declared, and thus appear in `funcs`.",
    )
    .param_vars;
    
for ((param, arg_var), arg_tl) in
    params.iter().zip(args.iter()).zip(args_tl)
{
    if let Some(arg_var) = arg_var {
        constraints.add_type_equiv(*arg_var, *arg_tl);
    }
 
    constraints.add_type_equiv(*param, *arg_tl);
}

I’m going to focus first on understanding LHS type inference.

LHS Type Inference

// lhs type inference
if let Some(lhs) = lhs {
    let ty = &llvm_instr.get_type(&ctx.module.types);
    constraints.add_has_type(*lhs, ty.clone());
    constraints.add_type_equiv(*lhs, func_id.clone());
}

My first note is that func_id implements the refinator::slang::stype::AsValue trait. I’m not sure what this trait does. Based on the documentation, it looks like it turns anything that can represent a type into an i64, which I think represents a unique type.

Why is it i64 and not u64?

It’s because that’s what egglog uses