|
| 1 | +use itertools::Itertools as _; |
1 | 2 | use rustc_data_structures::fx::FxIndexSet;
|
2 | 3 | use rustc_hir as hir;
|
3 |
| -use rustc_hir::def_id::DefId; |
| 4 | +use rustc_hir::def_id::{DefId, LocalDefId}; |
4 | 5 | use rustc_infer::infer::TyCtxtInferExt;
|
5 | 6 | use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
6 | 7 | use rustc_lint_defs::builtin::{REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE};
|
@@ -75,6 +76,8 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
|
75 | 76 | let mut trait_bounds = vec![];
|
76 | 77 | // Bounds that we find on the RPITITs in the impl signature.
|
77 | 78 | let mut impl_bounds = vec![];
|
| 79 | + // Pairs of trait and impl opaques. |
| 80 | + let mut pairs = vec![]; |
78 | 81 |
|
79 | 82 | for trait_projection in collector.types.into_iter().rev() {
|
80 | 83 | let impl_opaque_args = trait_projection.args.rebase_onto(tcx, trait_m.def_id, impl_m_args);
|
@@ -121,6 +124,8 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
|
121 | 124 | tcx.explicit_item_bounds(impl_opaque.def_id)
|
122 | 125 | .iter_instantiated_copied(tcx, impl_opaque.args),
|
123 | 126 | ));
|
| 127 | + |
| 128 | + pairs.push((trait_projection, impl_opaque)); |
124 | 129 | }
|
125 | 130 |
|
126 | 131 | let hybrid_preds = tcx
|
@@ -212,6 +217,39 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
|
212 | 217 | return;
|
213 | 218 | }
|
214 | 219 | }
|
| 220 | + |
| 221 | + // Make sure that the RPITIT doesn't capture fewer regions than |
| 222 | + // the trait definition. We hard-error if it captures *more*, since that |
| 223 | + // is literally unrepresentable in the type system; however, we may be |
| 224 | + // promising stronger outlives guarantees if we capture *fewer* regions. |
| 225 | + for (trait_projection, impl_opaque) in pairs { |
| 226 | + let impl_variances = tcx.variances_of(impl_opaque.def_id); |
| 227 | + let impl_captures: FxIndexSet<_> = impl_opaque |
| 228 | + .args |
| 229 | + .iter() |
| 230 | + .zip_eq(impl_variances) |
| 231 | + .filter(|(_, v)| **v == ty::Invariant) |
| 232 | + .map(|(arg, _)| arg) |
| 233 | + .collect(); |
| 234 | + |
| 235 | + let trait_variances = tcx.variances_of(trait_projection.def_id); |
| 236 | + let mut trait_captures = FxIndexSet::default(); |
| 237 | + for (arg, variance) in trait_projection.args.iter().zip_eq(trait_variances) { |
| 238 | + if *variance != ty::Invariant { |
| 239 | + continue; |
| 240 | + } |
| 241 | + arg.visit_with(&mut CollectParams { params: &mut trait_captures }); |
| 242 | + } |
| 243 | + |
| 244 | + if !trait_captures.iter().all(|arg| impl_captures.contains(arg)) { |
| 245 | + report_mismatched_rpitit_captures( |
| 246 | + tcx, |
| 247 | + impl_opaque.def_id.expect_local(), |
| 248 | + trait_captures, |
| 249 | + is_internal, |
| 250 | + ); |
| 251 | + } |
| 252 | + } |
215 | 253 | }
|
216 | 254 |
|
217 | 255 | struct ImplTraitInTraitCollector<'tcx> {
|
@@ -342,3 +380,65 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Anonymize<'tcx> {
|
342 | 380 | self.tcx.anonymize_bound_vars(t)
|
343 | 381 | }
|
344 | 382 | }
|
| 383 | + |
| 384 | +struct CollectParams<'a, 'tcx> { |
| 385 | + params: &'a mut FxIndexSet<ty::GenericArg<'tcx>>, |
| 386 | +} |
| 387 | +impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CollectParams<'_, 'tcx> { |
| 388 | + fn visit_ty(&mut self, ty: Ty<'tcx>) { |
| 389 | + if let ty::Param(_) = ty.kind() { |
| 390 | + self.params.insert(ty.into()); |
| 391 | + } else { |
| 392 | + ty.super_visit_with(self); |
| 393 | + } |
| 394 | + } |
| 395 | + fn visit_region(&mut self, r: ty::Region<'tcx>) { |
| 396 | + match r.kind() { |
| 397 | + ty::ReEarlyParam(_) | ty::ReLateParam(_) => { |
| 398 | + self.params.insert(r.into()); |
| 399 | + } |
| 400 | + _ => {} |
| 401 | + } |
| 402 | + } |
| 403 | + fn visit_const(&mut self, ct: ty::Const<'tcx>) { |
| 404 | + if let ty::ConstKind::Param(_) = ct.kind() { |
| 405 | + self.params.insert(ct.into()); |
| 406 | + } else { |
| 407 | + ct.super_visit_with(self); |
| 408 | + } |
| 409 | + } |
| 410 | +} |
| 411 | + |
| 412 | +fn report_mismatched_rpitit_captures<'tcx>( |
| 413 | + tcx: TyCtxt<'tcx>, |
| 414 | + impl_opaque_def_id: LocalDefId, |
| 415 | + mut trait_captured_args: FxIndexSet<ty::GenericArg<'tcx>>, |
| 416 | + is_internal: bool, |
| 417 | +) { |
| 418 | + let Some(use_bound_span) = |
| 419 | + tcx.hir_node_by_def_id(impl_opaque_def_id).expect_opaque_ty().bounds.iter().find_map( |
| 420 | + |bound| match *bound { |
| 421 | + rustc_hir::GenericBound::Use(_, span) => Some(span), |
| 422 | + hir::GenericBound::Trait(_) | hir::GenericBound::Outlives(_) => None, |
| 423 | + }, |
| 424 | + ) |
| 425 | + else { |
| 426 | + // I have no idea when you would ever undercapture without a `use<..>`. |
| 427 | + tcx.dcx().delayed_bug("expected use<..> to undercapture in an impl opaque"); |
| 428 | + return; |
| 429 | + }; |
| 430 | + |
| 431 | + trait_captured_args |
| 432 | + .sort_by_cached_key(|arg| !matches!(arg.unpack(), ty::GenericArgKind::Lifetime(_))); |
| 433 | + let suggestion = format!("use<{}>", trait_captured_args.iter().join(", ")); |
| 434 | + |
| 435 | + tcx.emit_node_span_lint( |
| 436 | + if is_internal { REFINING_IMPL_TRAIT_INTERNAL } else { REFINING_IMPL_TRAIT_REACHABLE }, |
| 437 | + tcx.local_def_id_to_hir_id(impl_opaque_def_id), |
| 438 | + use_bound_span, |
| 439 | + crate::errors::ReturnPositionImplTraitInTraitRefinedLifetimes { |
| 440 | + suggestion_span: use_bound_span, |
| 441 | + suggestion, |
| 442 | + }, |
| 443 | + ); |
| 444 | +} |
0 commit comments