diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index 88a2db3dc6223..2a7aadf27f21d 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -130,7 +130,7 @@ pub fn find_associated_item<'tcx>( match ancestors.leaf_def(tcx, item.ident, item.kind) { Some(node_item) => { let substs = tcx.infer_ctxt().enter(|infcx| { - let param_env = param_env.with_reveal_all(); + let param_env = param_env.with_reveal_all_normalized(tcx); let substs = substs.rebase_onto(tcx, trait_def_id, impl_data.substs); let substs = translate_substs(&infcx, param_env, impl_data.impl_def_id, substs, node_item.node); diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 7f93e8c91e9d9..ef9d44d013e1a 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -1949,7 +1949,7 @@ impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> { /// Computes the layout of a type. Note that this implicitly /// executes in "reveal all" mode. fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { - let param_env = self.param_env.with_reveal_all(); + let param_env = self.param_env.with_reveal_all_normalized(self.tcx); let ty = self.tcx.normalize_erasing_regions(param_env, ty); let details = self.tcx.layout_raw(param_env.and(ty))?; let layout = TyLayout { @@ -1976,7 +1976,7 @@ impl LayoutOf for LayoutCx<'tcx, ty::query::TyCtxtAt<'tcx>> { /// Computes the layout of a type. Note that this implicitly /// executes in "reveal all" mode. fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { - let param_env = self.param_env.with_reveal_all(); + let param_env = self.param_env.with_reveal_all_normalized(*self.tcx); let ty = self.tcx.normalize_erasing_regions(param_env, ty); let details = self.tcx.layout_raw(param_env.and(ty))?; let layout = TyLayout { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 05a2704cc5dfc..70636cfee465a 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1666,7 +1666,7 @@ impl<'tcx> ParamEnv<'tcx> { /// environments like codegen or doing optimizations. /// /// N.B., if you want to have predicates in scope, use `ParamEnv::new`, - /// or invoke `param_env.with_reveal_all()`. + /// or invoke `param_env.with_reveal_all_normalized()`. #[inline] pub fn reveal_all() -> Self { Self::new(List::empty(), Reveal::All, None) @@ -1688,8 +1688,14 @@ impl<'tcx> ParamEnv<'tcx> { /// the desired behavior during codegen and certain other special /// contexts; normally though we want to use `Reveal::UserFacing`, /// which is the default. - pub fn with_reveal_all(self) -> Self { - ty::ParamEnv { reveal: Reveal::All, ..self } + /// + /// All opaque types in the caller_bounds of the `ParamEnv` + /// will be normalized to their underlying types. + pub fn with_reveal_all_normalized(self, tcx: TyCtxt<'tcx>) -> Self { + let caller_bounds = tcx.normalize_impl_trait_types( + &self.caller_bounds + ); + ty::ParamEnv { reveal: Reveal::All, caller_bounds, ..self } } /// Returns this same environment but with no caller bounds. diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 6cb0d1e9946b5..e6fe52b0758db 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -2319,7 +2319,7 @@ impl<'tcx> Const<'tcx> { ty: Ty<'tcx>, ) -> Option { assert_eq!(self.ty, ty); - let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size; + let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size; // if `ty` does not depend on generic parameters, use an empty param_env self.eval(tcx, param_env).val.try_to_bits(size) } @@ -2331,7 +2331,7 @@ impl<'tcx> Const<'tcx> { param_env: ParamEnv<'tcx>, ) -> &Const<'tcx> { let try_const_eval = |did, param_env: ParamEnv<'tcx>, substs| { - let param_env_and_substs = param_env.with_reveal_all().and(substs); + let param_env_and_substs = param_env.with_reveal_all_normalized(tcx).and(substs); // Avoid querying `tcx.const_eval(...)` with any e.g. inference vars. if param_env_and_substs.has_local_value() { diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 0b11a9efd81d7..6e5d3dc716dc4 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -8,6 +8,7 @@ use crate::mir::interpret::{sign_extend, truncate}; use crate::ich::NodeIdHashingMode; use crate::traits::{self, ObligationCause}; use crate::ty::{self, DefIdTree, Ty, TyCtxt, GenericParamDefKind, TypeFoldable}; +use crate::ty::fold::TypeFolder; use crate::ty::subst::{Subst, InternalSubsts, SubstsRef, GenericArgKind}; use crate::ty::query::TyCtxtAt; use crate::ty::TyKind::*; @@ -699,81 +700,32 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Normalizes all opaque types in the given value, replacing them + /// with their underlying types. + pub fn normalize_impl_trait_types>(self, val: &T) -> T { + let mut visitor = OpaqueTypeExpander { + seen_opaque_tys: FxHashSet::default(), + expanded_cache: FxHashMap::default(), + primary_def_id: None, + found_recursion: false, + check_recursion: false, + tcx: self, + }; + val.fold_with(&mut visitor) + } + /// Expands the given impl trait type, stopping if the type is recursive. pub fn try_expand_impl_trait_type( self, def_id: DefId, substs: SubstsRef<'tcx>, ) -> Result, Ty<'tcx>> { - use crate::ty::fold::TypeFolder; - - struct OpaqueTypeExpander<'tcx> { - // Contains the DefIds of the opaque types that are currently being - // expanded. When we expand an opaque type we insert the DefId of - // that type, and when we finish expanding that type we remove the - // its DefId. - seen_opaque_tys: FxHashSet, - // Cache of all expansions we've seen so far. This is a critical - // optimization for some large types produced by async fn trees. - expanded_cache: FxHashMap<(DefId, SubstsRef<'tcx>), Ty<'tcx>>, - primary_def_id: DefId, - found_recursion: bool, - tcx: TyCtxt<'tcx>, - } - - impl<'tcx> OpaqueTypeExpander<'tcx> { - fn expand_opaque_ty( - &mut self, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> Option> { - if self.found_recursion { - return None; - } - let substs = substs.fold_with(self); - if self.seen_opaque_tys.insert(def_id) { - let expanded_ty = match self.expanded_cache.get(&(def_id, substs)) { - Some(expanded_ty) => expanded_ty, - None => { - let generic_ty = self.tcx.type_of(def_id); - let concrete_ty = generic_ty.subst(self.tcx, substs); - let expanded_ty = self.fold_ty(concrete_ty); - self.expanded_cache.insert((def_id, substs), expanded_ty); - expanded_ty - } - }; - self.seen_opaque_tys.remove(&def_id); - Some(expanded_ty) - } else { - // If another opaque type that we contain is recursive, then it - // will report the error, so we don't have to. - self.found_recursion = def_id == self.primary_def_id; - None - } - } - } - - impl<'tcx> TypeFolder<'tcx> for OpaqueTypeExpander<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Opaque(def_id, substs) = t.kind { - self.expand_opaque_ty(def_id, substs).unwrap_or(t) - } else if t.has_projections() { - t.super_fold_with(self) - } else { - t - } - } - } - let mut visitor = OpaqueTypeExpander { seen_opaque_tys: FxHashSet::default(), expanded_cache: FxHashMap::default(), - primary_def_id: def_id, + primary_def_id: Some(def_id), found_recursion: false, + check_recursion: true, tcx: self, }; let expanded_type = visitor.expand_opaque_ty(def_id, substs).unwrap(); @@ -785,6 +737,77 @@ impl<'tcx> TyCtxt<'tcx> { } } +/// Expands any nested opaque types, caching the expansion +/// of each (DefId, SubstsRef) pair +struct OpaqueTypeExpander<'tcx> { + /// Contains the `DefId`s of the opaque types that are currently being + /// expanded. When we expand an opaque type we insert the `DefId` of + /// that type, and when we finish expanding that type we remove the + /// its `DefId`. + seen_opaque_tys: FxHashSet, + /// Cache of all expansions we've seen so far. This is a critical + /// optimization for some large types produced by `async fn` trees. + expanded_cache: FxHashMap<(DefId, SubstsRef<'tcx>), Ty<'tcx>>, + primary_def_id: Option, + found_recursion: bool, + /// Whether or not to check for recursive opaque types. + /// This is `true` when we're explicitly checking for opaque type + /// recursion, and 'false' otherwise to avoid unecessary work. + check_recursion: bool, + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> OpaqueTypeExpander<'tcx> { + fn expand_opaque_ty( + &mut self, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Option> { + if self.found_recursion { + return None; + } + let substs = substs.fold_with(self); + if !self.check_recursion || self.seen_opaque_tys.insert(def_id) { + let expanded_ty = match self.expanded_cache.get(&(def_id, substs)) { + Some(expanded_ty) => expanded_ty, + None => { + let generic_ty = self.tcx.type_of(def_id); + let concrete_ty = generic_ty.subst(self.tcx, substs); + let expanded_ty = self.fold_ty(concrete_ty); + self.expanded_cache.insert((def_id, substs), expanded_ty); + expanded_ty + } + }; + if self.check_recursion { + self.seen_opaque_tys.remove(&def_id); + } + Some(expanded_ty) + } else { + // If another opaque type that we contain is recursive, then it + // will report the error, so we don't have to. + self.found_recursion = def_id == *self.primary_def_id.as_ref().unwrap(); + None + } + } +} + +impl<'tcx> TypeFolder<'tcx> for OpaqueTypeExpander<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Opaque(def_id, substs) = t.kind { + self.expand_opaque_ty(def_id, substs).unwrap_or(t) + } else if t.has_projections() { + t.super_fold_with(self) + } else { + t + } + } +} + + impl<'tcx> ty::TyS<'tcx> { /// Checks whether values of this type `T` are *moved* or *copied* /// when referenced -- this amounts to a check for whether `T: diff --git a/src/librustc_codegen_utils/symbol_names/v0.rs b/src/librustc_codegen_utils/symbol_names/v0.rs index 1dfcc21f3903d..0cf58f259c7a4 100644 --- a/src/librustc_codegen_utils/symbol_names/v0.rs +++ b/src/librustc_codegen_utils/symbol_names/v0.rs @@ -264,7 +264,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id }; let mut param_env = self.tcx.param_env(impl_def_id) - .with_reveal_all(); + .with_reveal_all_normalized(self.tcx); if !substs.is_empty() { param_env = param_env.subst(self.tcx, substs); } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 5b208dd858763..afeafff3e4133 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -221,7 +221,7 @@ fn build_drop_shim<'tcx>( }); } let patch = { - let param_env = tcx.param_env(def_id).with_reveal_all(); + let param_env = tcx.param_env(def_id).with_reveal_all_normalized(tcx); let mut elaborator = DropShimElaborator { body: &body, patch: MirPatch::new(&body), diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index 42daba93bd293..79d73b9a30e48 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -25,7 +25,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { debug!("elaborate_drops({:?} @ {:?})", src, body.span); let def_id = src.def_id(); - let param_env = tcx.param_env(src.def_id()).with_reveal_all(); + let param_env = tcx.param_env(src.def_id()).with_reveal_all_normalized(tcx); let move_data = match MoveData::gather_moves(body, tcx) { Ok(move_data) => move_data, Err(_) => bug!("No `move_errors` should be allowed in MIR borrowck"), diff --git a/src/test/ui/type-alias-impl-trait/opaque-type-in-predicate.rs b/src/test/ui/type-alias-impl-trait/opaque-type-in-predicate.rs new file mode 100644 index 0000000000000..36a287e032ca3 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/opaque-type-in-predicate.rs @@ -0,0 +1,53 @@ +// run-pass + +// Regression test for issue #65918 - checks that we do not +// ICE when attempting to normalize a predicate containting +// opaque types + +#![feature(type_alias_impl_trait)] + +use std::marker::PhantomData; + +/* copied Index and TryFrom for convinience (and simplicity) */ +trait MyIndex { + type O; + fn my_index(self) -> Self::O; +} +trait MyFrom: Sized { + type Error; + fn my_from(value: T) -> Result; +} + +/* MCVE starts here */ +trait F {} +impl F for () {} +type DummyT = impl F; +fn _dummy_t() -> DummyT {} + +struct Phantom1(PhantomData); +struct Phantom2(PhantomData); +struct Scope(Phantom2>); + +impl Scope { + fn new() -> Self { + Scope(Phantom2(PhantomData)) + } +} + +impl MyFrom> for Phantom1 { + type Error = (); + fn my_from(_: Phantom2) -> Result { + Ok(Phantom1(PhantomData)) + } +} + +impl>>, U> MyIndex> for Scope { + type O = T; + fn my_index(self) -> Self::O { + MyFrom::my_from(self.0).ok().unwrap() + } +} + +fn main() { + let _pos: Phantom1> = Scope::new().my_index(); +}