|
1 |
| -use crate::infer::InferCtxt; |
| 1 | +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; |
| 2 | +use crate::infer::{InferCtxt, InferOk}; |
| 3 | +use crate::traits; |
2 | 4 | use rustc_data_structures::sync::Lrc;
|
3 | 5 | use rustc_data_structures::vec_map::VecMap;
|
4 | 6 | use rustc_hir as hir;
|
5 |
| -use rustc_middle::ty::subst::GenericArgKind; |
| 7 | +use rustc_hir::def_id::LocalDefId; |
| 8 | +use rustc_middle::ty::fold::BottomUpFolder; |
| 9 | +use rustc_middle::ty::subst::{GenericArgKind, Subst}; |
6 | 10 | use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitor};
|
7 | 11 | use rustc_span::Span;
|
8 | 12 |
|
@@ -52,6 +56,49 @@ pub struct OpaqueTypeDecl<'tcx> {
|
52 | 56 | }
|
53 | 57 |
|
54 | 58 | impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
| 59 | + /// Replaces all opaque types in `value` with fresh inference variables |
| 60 | + /// and creates appropriate obligations. For example, given the input: |
| 61 | + /// |
| 62 | + /// impl Iterator<Item = impl Debug> |
| 63 | + /// |
| 64 | + /// this method would create two type variables, `?0` and `?1`. It would |
| 65 | + /// return the type `?0` but also the obligations: |
| 66 | + /// |
| 67 | + /// ?0: Iterator<Item = ?1> |
| 68 | + /// ?1: Debug |
| 69 | + /// |
| 70 | + /// Moreover, it returns an `OpaqueTypeMap` that would map `?0` to |
| 71 | + /// info about the `impl Iterator<..>` type and `?1` to info about |
| 72 | + /// the `impl Debug` type. |
| 73 | + /// |
| 74 | + /// # Parameters |
| 75 | + /// |
| 76 | + /// - `parent_def_id` -- the `DefId` of the function in which the opaque type |
| 77 | + /// is defined |
| 78 | + /// - `body_id` -- the body-id with which the resulting obligations should |
| 79 | + /// be associated |
| 80 | + /// - `param_env` -- the in-scope parameter environment to be used for |
| 81 | + /// obligations |
| 82 | + /// - `value` -- the value within which we are instantiating opaque types |
| 83 | + /// - `value_span` -- the span where the value came from, used in error reporting |
| 84 | + pub fn instantiate_opaque_types<T: TypeFoldable<'tcx>>( |
| 85 | + &self, |
| 86 | + body_id: hir::HirId, |
| 87 | + param_env: ty::ParamEnv<'tcx>, |
| 88 | + value: T, |
| 89 | + value_span: Span, |
| 90 | + ) -> InferOk<'tcx, T> { |
| 91 | + debug!( |
| 92 | + "instantiate_opaque_types(value={:?}, body_id={:?}, \ |
| 93 | + param_env={:?}, value_span={:?})", |
| 94 | + value, body_id, param_env, value_span, |
| 95 | + ); |
| 96 | + let mut instantiator = |
| 97 | + Instantiator { infcx: self, body_id, param_env, value_span, obligations: vec![] }; |
| 98 | + let value = instantiator.instantiate_opaque_types_in_map(value); |
| 99 | + InferOk { value, obligations: instantiator.obligations } |
| 100 | + } |
| 101 | + |
55 | 102 | /// Given the map `opaque_types` containing the opaque
|
56 | 103 | /// `impl Trait` types whose underlying, hidden types are being
|
57 | 104 | /// inferred, this method adds constraints to the regions
|
@@ -359,3 +406,232 @@ where
|
359 | 406 | ControlFlow::CONTINUE
|
360 | 407 | }
|
361 | 408 | }
|
| 409 | + |
| 410 | +struct Instantiator<'a, 'tcx> { |
| 411 | + infcx: &'a InferCtxt<'a, 'tcx>, |
| 412 | + body_id: hir::HirId, |
| 413 | + param_env: ty::ParamEnv<'tcx>, |
| 414 | + value_span: Span, |
| 415 | + obligations: Vec<traits::PredicateObligation<'tcx>>, |
| 416 | +} |
| 417 | + |
| 418 | +impl<'a, 'tcx> Instantiator<'a, 'tcx> { |
| 419 | + fn instantiate_opaque_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T { |
| 420 | + let tcx = self.infcx.tcx; |
| 421 | + value.fold_with(&mut BottomUpFolder { |
| 422 | + tcx, |
| 423 | + ty_op: |ty| { |
| 424 | + if ty.references_error() { |
| 425 | + return tcx.ty_error(); |
| 426 | + } else if let ty::Opaque(def_id, substs) = ty.kind() { |
| 427 | + // Check that this is `impl Trait` type is |
| 428 | + // declared by `parent_def_id` -- i.e., one whose |
| 429 | + // value we are inferring. At present, this is |
| 430 | + // always true during the first phase of |
| 431 | + // type-check, but not always true later on during |
| 432 | + // NLL. Once we support named opaque types more fully, |
| 433 | + // this same scenario will be able to arise during all phases. |
| 434 | + // |
| 435 | + // Here is an example using type alias `impl Trait` |
| 436 | + // that indicates the distinction we are checking for: |
| 437 | + // |
| 438 | + // ```rust |
| 439 | + // mod a { |
| 440 | + // pub type Foo = impl Iterator; |
| 441 | + // pub fn make_foo() -> Foo { .. } |
| 442 | + // } |
| 443 | + // |
| 444 | + // mod b { |
| 445 | + // fn foo() -> a::Foo { a::make_foo() } |
| 446 | + // } |
| 447 | + // ``` |
| 448 | + // |
| 449 | + // Here, the return type of `foo` references an |
| 450 | + // `Opaque` indeed, but not one whose value is |
| 451 | + // presently being inferred. You can get into a |
| 452 | + // similar situation with closure return types |
| 453 | + // today: |
| 454 | + // |
| 455 | + // ```rust |
| 456 | + // fn foo() -> impl Iterator { .. } |
| 457 | + // fn bar() { |
| 458 | + // let x = || foo(); // returns the Opaque assoc with `foo` |
| 459 | + // } |
| 460 | + // ``` |
| 461 | + if let Some(def_id) = def_id.as_local() { |
| 462 | + let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); |
| 463 | + let parent_def_id = self.infcx.defining_use_anchor; |
| 464 | + let def_scope_default = || { |
| 465 | + let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id); |
| 466 | + parent_def_id == tcx.hir().local_def_id(opaque_parent_hir_id) |
| 467 | + }; |
| 468 | + let (in_definition_scope, origin) = |
| 469 | + match tcx.hir().expect_item(opaque_hir_id).kind { |
| 470 | + // Anonymous `impl Trait` |
| 471 | + hir::ItemKind::OpaqueTy(hir::OpaqueTy { |
| 472 | + impl_trait_fn: Some(parent), |
| 473 | + origin, |
| 474 | + .. |
| 475 | + }) => (parent == parent_def_id.to_def_id(), origin), |
| 476 | + // Named `type Foo = impl Bar;` |
| 477 | + hir::ItemKind::OpaqueTy(hir::OpaqueTy { |
| 478 | + impl_trait_fn: None, |
| 479 | + origin, |
| 480 | + .. |
| 481 | + }) => ( |
| 482 | + may_define_opaque_type(tcx, parent_def_id, opaque_hir_id), |
| 483 | + origin, |
| 484 | + ), |
| 485 | + _ => (def_scope_default(), hir::OpaqueTyOrigin::TyAlias), |
| 486 | + }; |
| 487 | + if in_definition_scope { |
| 488 | + let opaque_type_key = |
| 489 | + OpaqueTypeKey { def_id: def_id.to_def_id(), substs }; |
| 490 | + return self.fold_opaque_ty(ty, opaque_type_key, origin); |
| 491 | + } |
| 492 | + |
| 493 | + debug!( |
| 494 | + "instantiate_opaque_types_in_map: \ |
| 495 | + encountered opaque outside its definition scope \ |
| 496 | + def_id={:?}", |
| 497 | + def_id, |
| 498 | + ); |
| 499 | + } |
| 500 | + } |
| 501 | + |
| 502 | + ty |
| 503 | + }, |
| 504 | + lt_op: |lt| lt, |
| 505 | + ct_op: |ct| ct, |
| 506 | + }) |
| 507 | + } |
| 508 | + |
| 509 | + #[instrument(skip(self), level = "debug")] |
| 510 | + fn fold_opaque_ty( |
| 511 | + &mut self, |
| 512 | + ty: Ty<'tcx>, |
| 513 | + opaque_type_key: OpaqueTypeKey<'tcx>, |
| 514 | + origin: hir::OpaqueTyOrigin, |
| 515 | + ) -> Ty<'tcx> { |
| 516 | + let infcx = self.infcx; |
| 517 | + let tcx = infcx.tcx; |
| 518 | + let OpaqueTypeKey { def_id, substs } = opaque_type_key; |
| 519 | + |
| 520 | + // Use the same type variable if the exact same opaque type appears more |
| 521 | + // than once in the return type (e.g., if it's passed to a type alias). |
| 522 | + if let Some(opaque_defn) = infcx.inner.borrow().opaque_types.get(&opaque_type_key) { |
| 523 | + debug!("re-using cached concrete type {:?}", opaque_defn.concrete_ty.kind()); |
| 524 | + return opaque_defn.concrete_ty; |
| 525 | + } |
| 526 | + |
| 527 | + let ty_var = infcx.next_ty_var(TypeVariableOrigin { |
| 528 | + kind: TypeVariableOriginKind::TypeInference, |
| 529 | + span: self.value_span, |
| 530 | + }); |
| 531 | + |
| 532 | + // Ideally, we'd get the span where *this specific `ty` came |
| 533 | + // from*, but right now we just use the span from the overall |
| 534 | + // value being folded. In simple cases like `-> impl Foo`, |
| 535 | + // these are the same span, but not in cases like `-> (impl |
| 536 | + // Foo, impl Bar)`. |
| 537 | + let definition_span = self.value_span; |
| 538 | + |
| 539 | + { |
| 540 | + let mut infcx = self.infcx.inner.borrow_mut(); |
| 541 | + infcx.opaque_types.insert( |
| 542 | + OpaqueTypeKey { def_id, substs }, |
| 543 | + OpaqueTypeDecl { opaque_type: ty, definition_span, concrete_ty: ty_var, origin }, |
| 544 | + ); |
| 545 | + infcx.opaque_types_vars.insert(ty_var, ty); |
| 546 | + } |
| 547 | + |
| 548 | + debug!("generated new type inference var {:?}", ty_var.kind()); |
| 549 | + |
| 550 | + let item_bounds = tcx.explicit_item_bounds(def_id); |
| 551 | + |
| 552 | + self.obligations.reserve(item_bounds.len()); |
| 553 | + for (predicate, _) in item_bounds { |
| 554 | + debug!(?predicate); |
| 555 | + let predicate = predicate.subst(tcx, substs); |
| 556 | + debug!(?predicate); |
| 557 | + |
| 558 | + // We can't normalize associated types from `rustc_infer`, but we can eagerly register inference variables for them. |
| 559 | + let predicate = predicate.fold_with(&mut BottomUpFolder { |
| 560 | + tcx, |
| 561 | + ty_op: |ty| match ty.kind() { |
| 562 | + ty::Projection(projection_ty) => infcx.infer_projection( |
| 563 | + self.param_env, |
| 564 | + *projection_ty, |
| 565 | + traits::ObligationCause::misc(self.value_span, self.body_id), |
| 566 | + 0, |
| 567 | + &mut self.obligations, |
| 568 | + ), |
| 569 | + _ => ty, |
| 570 | + }, |
| 571 | + lt_op: |lt| lt, |
| 572 | + ct_op: |ct| ct, |
| 573 | + }); |
| 574 | + debug!(?predicate); |
| 575 | + |
| 576 | + if let ty::PredicateKind::Projection(projection) = predicate.kind().skip_binder() { |
| 577 | + if projection.ty.references_error() { |
| 578 | + // No point on adding these obligations since there's a type error involved. |
| 579 | + return tcx.ty_error(); |
| 580 | + } |
| 581 | + } |
| 582 | + // Change the predicate to refer to the type variable, |
| 583 | + // which will be the concrete type instead of the opaque type. |
| 584 | + // This also instantiates nested instances of `impl Trait`. |
| 585 | + let predicate = self.instantiate_opaque_types_in_map(predicate); |
| 586 | + |
| 587 | + let cause = |
| 588 | + traits::ObligationCause::new(self.value_span, self.body_id, traits::OpaqueType); |
| 589 | + |
| 590 | + // Require that the predicate holds for the concrete type. |
| 591 | + debug!(?predicate); |
| 592 | + self.obligations.push(traits::Obligation::new(cause, self.param_env, predicate)); |
| 593 | + } |
| 594 | + |
| 595 | + ty_var |
| 596 | + } |
| 597 | +} |
| 598 | + |
| 599 | +/// Returns `true` if `opaque_hir_id` is a sibling or a child of a sibling of `def_id`. |
| 600 | +/// |
| 601 | +/// Example: |
| 602 | +/// ```rust |
| 603 | +/// pub mod foo { |
| 604 | +/// pub mod bar { |
| 605 | +/// pub trait Bar { .. } |
| 606 | +/// |
| 607 | +/// pub type Baz = impl Bar; |
| 608 | +/// |
| 609 | +/// fn f1() -> Baz { .. } |
| 610 | +/// } |
| 611 | +/// |
| 612 | +/// fn f2() -> bar::Baz { .. } |
| 613 | +/// } |
| 614 | +/// ``` |
| 615 | +/// |
| 616 | +/// Here, `def_id` is the `LocalDefId` of the defining use of the opaque type (e.g., `f1` or `f2`), |
| 617 | +/// and `opaque_hir_id` is the `HirId` of the definition of the opaque type `Baz`. |
| 618 | +/// For the above example, this function returns `true` for `f1` and `false` for `f2`. |
| 619 | +fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: LocalDefId, opaque_hir_id: hir::HirId) -> bool { |
| 620 | + let mut hir_id = tcx.hir().local_def_id_to_hir_id(def_id); |
| 621 | + |
| 622 | + // Named opaque types can be defined by any siblings or children of siblings. |
| 623 | + let scope = tcx.hir().get_defining_scope(opaque_hir_id); |
| 624 | + // We walk up the node tree until we hit the root or the scope of the opaque type. |
| 625 | + while hir_id != scope && hir_id != hir::CRATE_HIR_ID { |
| 626 | + hir_id = tcx.hir().get_parent_item(hir_id); |
| 627 | + } |
| 628 | + // Syntactically, we are allowed to define the concrete type if: |
| 629 | + let res = hir_id == scope; |
| 630 | + trace!( |
| 631 | + "may_define_opaque_type(def={:?}, opaque_node={:?}) = {}", |
| 632 | + tcx.hir().find(hir_id), |
| 633 | + tcx.hir().get(opaque_hir_id), |
| 634 | + res |
| 635 | + ); |
| 636 | + res |
| 637 | +} |
0 commit comments