|
1 |
| -use rustc_hir::{Movability, Mutability}; |
| 1 | +use rustc_data_structures::fx::FxHashMap; |
| 2 | +use rustc_hir::{def_id::DefId, Movability, Mutability}; |
2 | 3 | use rustc_infer::traits::query::NoSolution;
|
3 |
| -use rustc_middle::ty::{self, Ty, TyCtxt}; |
| 4 | +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable}; |
4 | 5 |
|
5 | 6 | use crate::solve::EvalCtxt;
|
6 | 7 |
|
@@ -231,3 +232,112 @@ pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
|
231 | 232 | }
|
232 | 233 | }
|
233 | 234 | }
|
| 235 | + |
| 236 | +/// Assemble a list of predicates that would be present on a theoretical |
| 237 | +/// user impl for an object type. These predicates must be checked any time |
| 238 | +/// we assemble a built-in object candidate for an object type, since they |
| 239 | +/// are not implied by the well-formedness of the type. |
| 240 | +/// |
| 241 | +/// For example, given the following traits: |
| 242 | +/// |
| 243 | +/// ```rust,ignore (theoretical code) |
| 244 | +/// trait Foo: Baz { |
| 245 | +/// type Bar: Copy; |
| 246 | +/// } |
| 247 | +/// |
| 248 | +/// trait Baz {} |
| 249 | +/// ``` |
| 250 | +/// |
| 251 | +/// For the dyn type `dyn Foo<Item = Ty>`, we can imagine there being a |
| 252 | +/// pair of theoretical impls: |
| 253 | +/// |
| 254 | +/// ```rust,ignore (theoretical code) |
| 255 | +/// impl Foo for dyn Foo<Item = Ty> |
| 256 | +/// where |
| 257 | +/// Self: Baz, |
| 258 | +/// <Self as Foo>::Bar: Copy, |
| 259 | +/// { |
| 260 | +/// type Bar = Ty; |
| 261 | +/// } |
| 262 | +/// |
| 263 | +/// impl Baz for dyn Foo<Item = Ty> {} |
| 264 | +/// ``` |
| 265 | +/// |
| 266 | +/// However, in order to make such impls well-formed, we need to do an |
| 267 | +/// additional step of eagerly folding the associated types in the where |
| 268 | +/// clauses of the impl. In this example, that means replacing |
| 269 | +/// `<Self as Foo>::Bar` with `Ty` in the first impl. |
| 270 | +pub(crate) fn predicates_for_object_candidate<'tcx>( |
| 271 | + ecx: &EvalCtxt<'_, 'tcx>, |
| 272 | + param_env: ty::ParamEnv<'tcx>, |
| 273 | + trait_ref: ty::TraitRef<'tcx>, |
| 274 | + object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, |
| 275 | +) -> Vec<ty::Predicate<'tcx>> { |
| 276 | + let tcx = ecx.tcx(); |
| 277 | + let mut requirements = vec![]; |
| 278 | + requirements.extend( |
| 279 | + tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.substs).predicates, |
| 280 | + ); |
| 281 | + for item in tcx.associated_items(trait_ref.def_id).in_definition_order() { |
| 282 | + // FIXME(associated_const_equality): Also add associated consts to |
| 283 | + // the requirements here. |
| 284 | + if item.kind == ty::AssocKind::Type { |
| 285 | + requirements.extend(tcx.item_bounds(item.def_id).subst(tcx, trait_ref.substs)); |
| 286 | + } |
| 287 | + } |
| 288 | + |
| 289 | + let mut replace_projection_with = FxHashMap::default(); |
| 290 | + for bound in object_bound { |
| 291 | + if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() { |
| 292 | + let proj = proj.with_self_ty(tcx, trait_ref.self_ty()); |
| 293 | + let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj)); |
| 294 | + assert_eq!( |
| 295 | + old_ty, |
| 296 | + None, |
| 297 | + "{} has two substitutions: {} and {}", |
| 298 | + proj.projection_ty, |
| 299 | + proj.term, |
| 300 | + old_ty.unwrap() |
| 301 | + ); |
| 302 | + } |
| 303 | + } |
| 304 | + |
| 305 | + requirements.fold_with(&mut ReplaceProjectionWith { |
| 306 | + ecx, |
| 307 | + param_env, |
| 308 | + mapping: replace_projection_with, |
| 309 | + }) |
| 310 | +} |
| 311 | + |
| 312 | +struct ReplaceProjectionWith<'a, 'tcx> { |
| 313 | + ecx: &'a EvalCtxt<'a, 'tcx>, |
| 314 | + param_env: ty::ParamEnv<'tcx>, |
| 315 | + mapping: FxHashMap<DefId, ty::PolyProjectionPredicate<'tcx>>, |
| 316 | +} |
| 317 | + |
| 318 | +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> { |
| 319 | + fn interner(&self) -> TyCtxt<'tcx> { |
| 320 | + self.ecx.tcx() |
| 321 | + } |
| 322 | + |
| 323 | + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { |
| 324 | + if let ty::Alias(ty::Projection, alias_ty) = *ty.kind() |
| 325 | + && let Some(replacement) = self.mapping.get(&alias_ty.def_id) |
| 326 | + { |
| 327 | + // We may have a case where our object type's projection bound is higher-ranked, |
| 328 | + // but the where clauses we instantiated are not. We can solve this by instantiating |
| 329 | + // the binder at the usage site. |
| 330 | + let proj = self.ecx.instantiate_binder_with_infer(*replacement); |
| 331 | + // FIXME: Technically this folder could be fallible? |
| 332 | + let nested = self |
| 333 | + .ecx |
| 334 | + .eq(self.param_env, alias_ty, proj.projection_ty) |
| 335 | + .expect("expected to be able to unify goal projection with dyn's projection"); |
| 336 | + // FIXME: Technically we could register these too.. |
| 337 | + assert!(nested.is_empty(), "did not expect unification to have any nested goals"); |
| 338 | + proj.term.ty().unwrap() |
| 339 | + } else { |
| 340 | + ty.super_fold_with(self) |
| 341 | + } |
| 342 | + } |
| 343 | +} |
0 commit comments