summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_const_eval/src/util/compare_types.rs
blob: 265ca0c7884ced0a0dc8a35bc6ed48662c22100a (plain)
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
//! Routines to check for relations between fully inferred types.
//!
//! FIXME: Move this to a more general place. The utility of this extends to
//! other areas of the compiler as well.

use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::traits::{DefiningAnchor, ObligationCause};
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt, Variance};
use rustc_trait_selection::traits::ObligationCtxt;

/// Returns whether the two types are equal up to subtyping.
///
/// This is used in case we don't know the expected subtyping direction
/// and still want to check whether anything is broken.
pub fn is_equal_up_to_subtyping<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
    src: Ty<'tcx>,
    dest: Ty<'tcx>,
) -> bool {
    // Fast path.
    if src == dest {
        return true;
    }

    // Check for subtyping in either direction.
    relate_types(tcx, param_env, Variance::Covariant, src, dest)
        || relate_types(tcx, param_env, Variance::Covariant, dest, src)
}

/// Returns whether `src` is a subtype of `dest`, i.e. `src <: dest`.
///
/// When validating assignments, the variance should be `Covariant`. When checking
/// during `MirPhase` >= `MirPhase::Runtime(RuntimePhase::Initial)` variance should be `Invariant`
/// because we want to check for type equality.
///
/// This mostly ignores opaque types as it can be used in constraining contexts
/// while still computing the final underlying type.
pub fn relate_types<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
    variance: Variance,
    src: Ty<'tcx>,
    dest: Ty<'tcx>,
) -> bool {
    if src == dest {
        return true;
    }

    let mut builder =
        tcx.infer_ctxt().ignoring_regions().with_opaque_type_inference(DefiningAnchor::Bubble);
    let infcx = builder.build();
    let ocx = ObligationCtxt::new(&infcx);
    let cause = ObligationCause::dummy();
    let src = ocx.normalize(&cause, param_env, src);
    let dest = ocx.normalize(&cause, param_env, dest);
    match ocx.relate(&cause, param_env, variance, src, dest) {
        Ok(()) => {}
        Err(_) => return false,
    };
    let errors = ocx.select_all_or_error();
    // With `Reveal::All`, opaque types get normalized away, with `Reveal::UserFacing`
    // we would get unification errors because we're unable to look into opaque types,
    // even if they're constrained in our current function.
    for (key, ty) in infcx.take_opaque_types() {
        let hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args);
        if hidden_ty != ty.hidden_type.ty {
            span_bug!(
                ty.hidden_type.span,
                "{}, {}",
                tcx.type_of(key.def_id).instantiate(tcx, key.args),
                ty.hidden_type.ty
            );
        }
    }
    errors.is_empty()
}