1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Copyright (c) 2024 <Wei Li>.
//
// This source code is licensed under the GNU license found in the
// LICENSE file in the root directory of this source tree.

//! This module provides essential functions for resolving call targets.

use rustc_hir::def_id::DefId;
use rustc_middle::ty::{GenericArgsRef, TyCtxt, TyKind};

use crate::util;

/// Try to resolve the function with `def_id` and `gen_args`. 
/// 
/// If the function is not a trait method, (`def_id`, `gen_args`) is returned
/// directly. Otherwise, the function is devirtualized to a specific implementation.
pub fn resolve_fn_def<'tcx>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    gen_args: GenericArgsRef<'tcx>,
) -> (DefId, GenericArgsRef<'tcx>) {
    if tcx.is_mir_available(def_id) && !util::is_trait_method(tcx, def_id) {
        (def_id, gen_args)
    } else if let Some((resolved_def_id, resolved_substs)) =
        try_to_devirtualize(tcx, def_id, gen_args)
    {
        (resolved_def_id, resolved_substs)
    } else {
        // if the function cannot be resolved, 
        // return the original (def_id, gen_args) pair directly.
        (def_id, gen_args)
    }
}

/// Try to devirtualize a trait method with `def_id` and `gen_args`. 
/// 
/// Returns `None` if the given `def_id` does not correspond to a trait method or 
/// we cannot resolve the trait method to a specific instance. For example, the 
/// first gen_arg is a dynamic type.
pub fn try_to_devirtualize<'tcx>(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    gen_args: GenericArgsRef<'tcx>,
) -> Option<(DefId, GenericArgsRef<'tcx>)> {
    if !util::is_trait_method(tcx, def_id) {
        return None;
    }

    // A trait method cannot be devirtualized when the first gen_arg corresponds
    // to a dynamic type.
    let arg0_ty = gen_args
        .types()
        .next()
        .expect("Expect `Self` substition in trait method invocation");
    if matches!(arg0_ty.kind(), TyKind::Dynamic(..)) {
        return None;
    }

    let param_env = rustc_middle::ty::ParamEnv::reveal_all();
    let abi = tcx
        .type_of(def_id)
        .skip_binder()
        .fn_sig(tcx)
        .abi();    
    let resolved_instance = if abi == rustc_target::spec::abi::Abi::Rust {
        // Instance::resolve panics if try_normalize_erasing_regions returns an error.
        // It is difficult to determine exactly when this error will occur.
        if tcx.try_normalize_erasing_regions(param_env, gen_args).is_err() {
            None
        } else {
            Some(rustc_middle::ty::Instance::resolve(
                tcx,
                param_env,
                def_id,
                gen_args,
            ))
        }
    } else {
        None
    };
    if let Some(Ok(Some(instance))) = resolved_instance {
        let resolved_def_id = instance.def.def_id();
        return Some((resolved_def_id, instance.args));
    }
    None
}