about summary refs log tree commit diff
path: root/compiler/rustc_trait_selection/src/traits/misc.rs
blob: 46eea628a34412b4f6591b27d01d53673f0b6b83 (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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! Miscellaneous type-system utilities that are too small to deserve their own modules.

use crate::traits::{self, ObligationCause};

use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};

use crate::traits::error_reporting::TypeErrCtxtExt;

use super::outlives_bounds::InferCtxtExt;

#[derive(Clone)]
pub enum CopyImplementationError<'tcx> {
    InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>)>),
    NotAnAdt,
    HasDestructor,
}

/// Checks that the fields of the type (an ADT) all implement copy.
///
/// If fields don't implement copy, return an error containing a list of
/// those violating fields. If it's not an ADT, returns `Err(NotAnAdt)`.
pub fn type_allowed_to_implement_copy<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    self_type: Ty<'tcx>,
    parent_cause: ObligationCause<'tcx>,
) -> Result<(), CopyImplementationError<'tcx>> {
    let (adt, substs) = match self_type.kind() {
        // These types used to have a builtin impl.
        // Now libcore provides that impl.
        ty::Uint(_)
        | ty::Int(_)
        | ty::Bool
        | ty::Float(_)
        | ty::Char
        | ty::RawPtr(..)
        | ty::Never
        | ty::Ref(_, _, hir::Mutability::Not)
        | ty::Array(..) => return Ok(()),

        ty::Adt(adt, substs) => (adt, substs),

        _ => return Err(CopyImplementationError::NotAnAdt),
    };

    let copy_def_id = tcx.require_lang_item(hir::LangItem::Copy, Some(parent_cause.span));

    let mut infringing = Vec::new();
    for variant in adt.variants() {
        for field in &variant.fields {
            // Do this per-field to get better error messages.
            let infcx = tcx.infer_ctxt().build();
            let ocx = traits::ObligationCtxt::new(&infcx);

            let ty = field.ty(tcx, substs);
            if ty.references_error() {
                continue;
            }
            let span = tcx.def_span(field.did);
            // FIXME(compiler-errors): This gives us better spans for bad
            // projection types like in issue-50480.
            // If the ADT has substs, point to the cause we are given.
            // If it does not, then this field probably doesn't normalize
            // to begin with, and point to the bad field's span instead.
            let cause = if field
                .ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did()))
                .has_non_region_param()
            {
                parent_cause.clone()
            } else {
                ObligationCause::dummy_with_span(span)
            };

            let ty = ocx.normalize(&cause, param_env, ty);
            let normalization_errors = ocx.select_where_possible();
            if !normalization_errors.is_empty() {
                // Don't report this as a field that doesn't implement Copy,
                // but instead just implement this as a field that isn't WF.
                infcx.err_ctxt().report_fulfillment_errors(&normalization_errors, None);
                continue;
            }

            ocx.register_bound(cause, param_env, ty, copy_def_id);
            if !ocx.select_all_or_error().is_empty() {
                infringing.push((field, ty));
            }

            // Check regions assuming the self type of the impl is WF
            let outlives_env = OutlivesEnvironment::with_bounds(
                param_env,
                Some(&infcx),
                infcx.implied_bounds_tys(
                    param_env,
                    parent_cause.body_id,
                    FxIndexSet::from_iter([self_type]),
                ),
            );
            infcx.process_registered_region_obligations(
                outlives_env.region_bound_pairs(),
                param_env,
            );
            if !infcx.resolve_regions(&outlives_env).is_empty() {
                infringing.push((field, ty));
            }
        }
    }

    if !infringing.is_empty() {
        return Err(CopyImplementationError::InfrigingFields(infringing));
    }

    if adt.has_dtor(tcx) {
        return Err(CopyImplementationError::HasDestructor);
    }

    Ok(())
}