Repository: JuliaLang/julia
File: src/codegen.cpp (C++)

Code before:
        const jl_cgval_t &func = argv[1];
        const jl_cgval_t &argtypes = argv[2];
        if (func.constant && argtypes.constant && jl_is_code_instance(argtypes.constant)) {
            jl_code_instance_t *codeinst = (jl_code_instance_t*)argtypes.constant;
            jl_method_instance_t *mi = jl_get_ci_mi(codeinst);

            // 1. Re-pack arguments for emit_invoke
            // Builtin args are: [f, codeinst, arg1, arg2...]
            // emit_invoke expects: lival=codeinst, argv=[f, arg1, arg2...]
            SmallVector<jl_cgval_t, 4> invoke_args;
            invoke_args.push_back(argv[1]); // f
            for (size_t i = 3; i <= nargs; ++i) {
              invoke_args.push_back(argv[i]);
            }

            // 2. Emit Argument Type Assertions
            // invoke() implies a type assertion against the CodeInstance's specTypes
            jl_value_t *specTypes = mi->specTypes;
            // specTypes includes (func_type, arg1_type...), matching invoke_args
            for (size_t i = 0; i < invoke_args.size(); i++) {
              jl_value_t *ty = jl_nth_slot_type(specTypes, i);
              emit_typecheck(ctx, invoke_args[i], ty, "invoke");
            }

            // 3. Delegate to emit_invoke for the actual call generation
            *ret = emit_invoke(ctx, argtypes, invoke_args, invoke_args.size(), rt, false);
            return true;
        }
    }

    else if (f == BUILTIN(typeassert) && nargs == 2) {
        const jl_cgval_t &arg = argv[1];
        const jl_cgval_t &ty = argv[2];
        if (jl_is_type_type(ty.typ) && !jl_has_free_typevars(ty.typ)) {
            jl_value_t *tp0 = jl_tparam0(ty.typ);
            emit_typecheck(ctx, arg, tp0, "typeassert");
            *ret = update_julia_type(ctx, arg, tp0);
            return true;
        }
        if (jl_subtype(ty.typ, (jl_value_t*)jl_type_type)) {
            Value *rt_arg = boxed(ctx, arg);
            Value *rt_ty = boxed(ctx, ty);
            ctx.builder.CreateCall(prepare_call(jltypeassert_func), {rt_arg, rt_ty});
            *ret = arg;
            return true;
        }
    }

    else if (f == BUILTIN(isa) && nargs == 2) {
        const jl_cgval_t &arg = argv[1];
        const jl_cgval_t &ty = argv[2];

Review comment (bug):
You probably need to emit_invoke to match this error

```
if (invoke) {
            return invoke(args[0], &args[2], nargs - 2, codeinst);
        } else {
            if (codeinst->owner != jl_nothing || !jl_is_method(codeinst->def->def.value)) {
                jl_error("Failed to invoke or compile external codeinst");
            }
            return jl_gf_invoke_by_method(codeinst->def->def.method, args[0], &args[2], nargs - 1);
```

Similarly looking at https://github.com/JuliaLang/julia/pull/56660/changes#diff-b6cb5d410b973b5987bc13b1eba0f156fcaece59cfbd6cb12ac503ff44fd13ca

What happens when you provide an "uncompiled" codeinst? You would need a mechanism like in #52964 to go from `codeinst->owner` to the correct abstract interpreter / compiler instance. 

So one avenue forward would be to partially do #52964 but for now error on encountering a dynamic dispatch.


Code after:
        const jl_cgval_t &arg = argv[1];
        const jl_cgval_t &ty = argv[2];
        if (jl_is_type_type(ty.typ) && !jl_has_free_typevars(ty.typ)) {
            jl_value_t *tp0 = jl_tparam0(ty.typ);
            emit_typecheck(ctx, arg, tp0, "typeassert");
            *ret = update_julia_type(ctx, arg, tp0);
            return true;
        }
        if (jl_subtype(ty.typ, (jl_value_t*)jl_type_type)) {
            Value *rt_arg = boxed(ctx, arg);
            Value *rt_ty = boxed(ctx, ty);
            ctx.builder.CreateCall(prepare_call(jltypeassert_func), {rt_arg, rt_ty});
            *ret = arg;
            return true;
        }
    }

    else if (f == BUILTIN(isa) && nargs == 2) {
        const jl_cgval_t &arg = argv[1];
        const jl_cgval_t &ty = argv[2];
        if (jl_is_type_type(ty.typ) && !jl_has_free_typevars(ty.typ)) {
            jl_value_t *tp0 = jl_tparam0(ty.typ);
            Value *isa_result = emit_isa(ctx, arg, tp0, Twine()).first;
            *ret = mark_julia_type(ctx, isa_result, false, jl_bool_type);
            return true;
        }
    }

    else if (f == BUILTIN(issubtype) && nargs == 2) {
        const jl_cgval_t &ta = argv[1];
        const jl_cgval_t &tb = argv[2];
        if (jl_is_type_type(ta.typ) && !jl_has_free_typevars(ta.typ) &&
            jl_is_type_type(tb.typ) && !jl_has_free_typevars(tb.typ)) {
            int issub = jl_subtype(jl_tparam0(ta.typ), jl_tparam0(tb.typ));
            *ret = mark_julia_type(ctx, ConstantInt::get(getInt8Ty(ctx.builder.getContext()), issub), false, jl_bool_type);
            return true;
        }
    }

    else if (f == BUILTIN(_apply_iterate) && nargs == 3) {
        // turn Core._apply_iterate(iter, f, Tuple) ==> f(Tuple...) using the jlcall calling convention if Tuple is the va allocation
        if (ctx.vaSlot > 0) {
            if (LoadInst *load = dyn_cast_or_null<LoadInst>(argv[3].V)) {
                if (load->getPointerOperand() == ctx.slots[ctx.vaSlot].boxroot && ctx.argArray) {
                    Value *theF = boxed(ctx, argv[2]);
                    Value *nva = emit_n_varargs(ctx);
#ifdef _P64
                    nva = ctx.builder.CreateTrunc(nva, getInt32Ty(ctx.builder.getContext()));
#endif
                    Value *theArgs = emit_ptrgep(ctx, ctx.argArray, ctx.nReqArgs * sizeof(jl_value_t*));
                    Value *r = ctx.builder.CreateCall(prepare_call(jlapplygeneric_func), { theF, theArgs, nva });