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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`.
//
// We don't do any drop checking during hir typeck.
use crate::hir::def_id::{DefId, LocalDefId};
use rustc_errors::{struct_span_err, ErrorGuaranteed};
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::util::IgnoreRegions;
use rustc_middle::ty::{self, Predicate, Ty, TyCtxt};
/// This function confirms that the `Drop` implementation identified by
/// `drop_impl_did` is not any more specialized than the type it is
/// attached to (Issue #8142).
///
/// This means:
///
/// 1. The self type must be nominal (this is already checked during
/// coherence),
///
/// 2. The generic region/type parameters of the impl's self type must
/// all be parameters of the Drop impl itself (i.e., no
/// specialization like `impl Drop for Foo<i32>`), and,
///
/// 3. Any bounds on the generic parameters must be reflected in the
/// struct/enum definition for the nominal type itself (i.e.
/// cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`).
///
pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorGuaranteed> {
let dtor_self_type = tcx.type_of(drop_impl_did).subst_identity();
let dtor_predicates = tcx.predicates_of(drop_impl_did);
match dtor_self_type.kind() {
ty::Adt(adt_def, self_to_impl_substs) => {
ensure_drop_params_and_item_params_correspond(
tcx,
drop_impl_did.expect_local(),
adt_def.did(),
self_to_impl_substs,
)?;
ensure_drop_predicates_are_implied_by_item_defn(
tcx,
dtor_predicates,
adt_def.did().expect_local(),
self_to_impl_substs,
)
}
_ => {
// Destructors only work on nominal types. This was
// already checked by coherence, but compilation may
// not have been terminated.
let span = tcx.def_span(drop_impl_did);
let reported = tcx.sess.delay_span_bug(
span,
&format!("should have been rejected by coherence check: {dtor_self_type}"),
);
Err(reported)
}
}
}
fn ensure_drop_params_and_item_params_correspond<'tcx>(
tcx: TyCtxt<'tcx>,
drop_impl_did: LocalDefId,
self_type_did: DefId,
drop_impl_substs: SubstsRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
let Err(arg) = tcx.uses_unique_generic_params(drop_impl_substs, IgnoreRegions::No) else {
return Ok(())
};
let drop_impl_span = tcx.def_span(drop_impl_did);
let item_span = tcx.def_span(self_type_did);
let self_descr = tcx.def_descr(self_type_did);
let mut err =
struct_span_err!(tcx.sess, drop_impl_span, E0366, "`Drop` impls cannot be specialized");
match arg {
ty::util::NotUniqueParam::DuplicateParam(arg) => {
err.note(&format!("`{arg}` is mentioned multiple times"))
}
ty::util::NotUniqueParam::NotParam(arg) => {
err.note(&format!("`{arg}` is not a generic parameter"))
}
};
err.span_note(
item_span,
&format!(
"use the same sequence of generic lifetime, type and const parameters \
as the {self_descr} definition",
),
);
Err(err.emit())
}
/// Confirms that every predicate imposed by dtor_predicates is
/// implied by assuming the predicates attached to self_type_did.
fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
tcx: TyCtxt<'tcx>,
dtor_predicates: ty::GenericPredicates<'tcx>,
self_type_did: LocalDefId,
self_to_impl_substs: SubstsRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
let mut result = Ok(());
// Here is an example, analogous to that from
// `compare_impl_method`.
//
// Consider a struct type:
//
// struct Type<'c, 'b:'c, 'a> {
// x: &'a Contents // (contents are irrelevant;
// y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.)
// }
//
// and a Drop impl:
//
// impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> {
// fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y)
// }
//
// We start out with self_to_impl_substs, that maps the generic
// parameters of Type to that of the Drop impl.
//
// self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x}
//
// Applying this to the predicates (i.e., assumptions) provided by the item
// definition yields the instantiated assumptions:
//
// ['y : 'z]
//
// We then check all of the predicates of the Drop impl:
//
// ['y:'z, 'x:'y]
//
// and ensure each is in the list of instantiated
// assumptions. Here, `'y:'z` is present, but `'x:'y` is
// absent. So we report an error that the Drop impl injected a
// predicate that is not present on the struct definition.
// We can assume the predicates attached to struct/enum definition
// hold.
let generic_assumptions = tcx.predicates_of(self_type_did);
let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs);
let assumptions_in_impl_context = assumptions_in_impl_context.predicates;
debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates);
let self_param_env = tcx.param_env(self_type_did);
// An earlier version of this code attempted to do this checking
// via the traits::fulfill machinery. However, it ran into trouble
// since the fulfill machinery merely turns outlives-predicates
// 'a:'b and T:'b into region inference constraints. It is simpler
// just to look for all the predicates directly.
assert_eq!(dtor_predicates.parent, None);
for &(predicate, predicate_sp) in dtor_predicates.predicates {
// (We do not need to worry about deep analysis of type
// expressions etc because the Drop impls are already forced
// to take on a structure that is roughly an alpha-renaming of
// the generic parameters of the item definition.)
// This path now just checks *all* predicates via an instantiation of
// the `SimpleEqRelation`, which simply forwards to the `relate` machinery
// after taking care of anonymizing late bound regions.
//
// However, it may be more efficient in the future to batch
// the analysis together via the fulfill (see comment above regarding
// the usage of the fulfill machinery), rather than the
// repeated `.iter().any(..)` calls.
// This closure is a more robust way to check `Predicate` equality
// than simple `==` checks (which were the previous implementation).
// It relies on `ty::relate` for `TraitPredicate`, `ProjectionPredicate`,
// `ConstEvaluatable` and `TypeOutlives` (which implement the Relate trait),
// while delegating on simple equality for the other `Predicate`.
// This implementation solves (Issue #59497) and (Issue #58311).
// It is unclear to me at the moment whether the approach based on `relate`
// could be extended easily also to the other `Predicate`.
let predicate_matches_closure = |p: Predicate<'tcx>| {
let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env);
let predicate = predicate.kind();
let p = p.kind();
match (predicate.skip_binder(), p.skip_binder()) {
(
ty::PredicateKind::Clause(ty::Clause::Trait(a)),
ty::PredicateKind::Clause(ty::Clause::Trait(b)),
) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(),
(
ty::PredicateKind::Clause(ty::Clause::Projection(a)),
ty::PredicateKind::Clause(ty::Clause::Projection(b)),
) => relator.relate(predicate.rebind(a), p.rebind(b)).is_ok(),
(
ty::PredicateKind::ConstEvaluatable(a),
ty::PredicateKind::ConstEvaluatable(b),
) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(),
(
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
ty_a,
lt_a,
))),
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
ty_b,
lt_b,
))),
) => {
relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok()
&& relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok()
}
(ty::PredicateKind::WellFormed(arg_a), ty::PredicateKind::WellFormed(arg_b)) => {
relator.relate(predicate.rebind(arg_a), p.rebind(arg_b)).is_ok()
}
_ => predicate == p,
}
};
if !assumptions_in_impl_context.iter().copied().any(predicate_matches_closure) {
let item_span = tcx.def_span(self_type_did);
let self_descr = tcx.def_descr(self_type_did.to_def_id());
let reported = struct_span_err!(
tcx.sess,
predicate_sp,
E0367,
"`Drop` impl requires `{predicate}` but the {self_descr} it is implemented for does not",
)
.span_note(item_span, "the implementor must specify the same requirement")
.emit();
result = Err(reported);
}
}
result
}
/// This is an implementation of the [`TypeRelation`] trait with the
/// aim of simply comparing for equality (without side-effects).
///
/// It is not intended to be used anywhere else other than here.
pub(crate) struct SimpleEqRelation<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
}
impl<'tcx> SimpleEqRelation<'tcx> {
fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> SimpleEqRelation<'tcx> {
SimpleEqRelation { tcx, param_env }
}
}
impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.param_env
}
fn tag(&self) -> &'static str {
"dropck::SimpleEqRelation"
}
fn a_is_expected(&self) -> bool {
true
}
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_: ty::Variance,
_info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
// Here we ignore variance because we require drop impl's types
// to be *exactly* the same as to the ones in the struct definition.
self.relate(a, b)
}
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
debug!("SimpleEqRelation::tys(a={:?}, b={:?})", a, b);
ty::relate::super_relate_tys(self, a, b)
}
fn regions(
&mut self,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
debug!("SimpleEqRelation::regions(a={:?}, b={:?})", a, b);
// We can just equate the regions because LBRs have been
// already anonymized.
if a == b {
Ok(a)
} else {
// I'm not sure is this `TypeError` is the right one, but
// it should not matter as it won't be checked (the dropck
// will emit its own, more informative and higher-level errors
// in case anything goes wrong).
Err(TypeError::RegionsPlaceholderMismatch)
}
}
fn consts(
&mut self,
a: ty::Const<'tcx>,
b: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
debug!("SimpleEqRelation::consts(a={:?}, b={:?})", a, b);
ty::relate::super_relate_consts(self, a, b)
}
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
b: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<'tcx>,
{
debug!("SimpleEqRelation::binders({:?}: {:?}", a, b);
// Anonymizing the LBRs is necessary to solve (Issue #59497).
// After we do so, it should be totally fine to skip the binders.
let anon_a = self.tcx.anonymize_bound_vars(a);
let anon_b = self.tcx.anonymize_bound_vars(b);
self.relate(anon_a.skip_binder(), anon_b.skip_binder())?;
Ok(a)
}
}