From f873c1e2db8d06be6a816b838655906fa9f2dac9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 14 Feb 2018 16:00:07 -0500 Subject: [PATCH 01/27] require `Lifted` types to outlive `'tcx` --- src/librustc/ty/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index b760649c37d15..e7d6bdd3383da 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1508,7 +1508,7 @@ impl<'gcx: 'tcx, 'tcx> GlobalCtxt<'gcx> { /// pointer differs. The latter case is possible if a primitive type, /// e.g. `()` or `u8`, was interned in a different context. pub trait Lift<'tcx> { - type Lifted; + type Lifted: 'tcx; fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option; } From 23837c1901ab234ee6bc806744cd47d5a652cdfc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 9 Feb 2018 10:34:23 -0500 Subject: [PATCH 02/27] improve TypeFoldable/Lift macros and make a bunch of stuff use them Improvements: - Use Clone not Copy for the "simple cases" - Separate TypeFoldable and Lift for the "simple cases" - Support generics type parameters - Support named fields in enum variants - etc --- src/librustc/infer/mod.rs | 13 - src/librustc/lib.rs | 1 + src/librustc/macros.rs | 300 +++++++++++++ src/librustc/mir/cache.rs | 4 + src/librustc/mir/mod.rs | 186 +++----- src/librustc/mir/tcx.rs | 24 +- src/librustc/traits/structural_impls.rs | 281 ++---------- src/librustc/ty/fold.rs | 3 + src/librustc/ty/relate.rs | 11 +- src/librustc/ty/structural_impls.rs | 541 ++++++------------------ 10 files changed, 551 insertions(+), 813 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 5d44b2043e26c..b94f1819c7ddb 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1647,19 +1647,6 @@ impl<'tcx> TypeFoldable<'tcx> for ValuePairs<'tcx> { } } -impl<'tcx> TypeFoldable<'tcx> for TypeTrace<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - TypeTrace { - cause: self.cause.fold_with(folder), - values: self.values.fold_with(folder) - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.cause.visit_with(visitor) || self.values.visit_with(visitor) - } -} - impl<'tcx> fmt::Debug for RegionObligation<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "RegionObligation(sub_region={:?}, sup_type={:?})", diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 56de2939ffae1..2cf176036296b 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -57,6 +57,7 @@ #![feature(inclusive_range)] #![feature(inclusive_range_syntax)] #![cfg_attr(windows, feature(libc))] +#![feature(macro_lifetime_matcher)] #![feature(macro_vis_matcher)] #![feature(match_default_bindings)] #![feature(never_type)] diff --git a/src/librustc/macros.rs b/src/librustc/macros.rs index 9a394e524817b..6013e3aef81e1 100644 --- a/src/librustc/macros.rs +++ b/src/librustc/macros.rs @@ -138,3 +138,303 @@ macro_rules! impl_stable_hash_for_spanned { ); } +/////////////////////////////////////////////////////////////////////////// +// Lift and TypeFoldable macros +// +// When possible, use one of these (relatively) convenient macros to write +// the impls for you. + +#[macro_export] +macro_rules! CloneLiftImpls { + (for <$tcx:lifetime> { $($ty:ty,)+ }) => { + $( + impl<$tcx> $crate::ty::Lift<$tcx> for $ty { + type Lifted = Self; + fn lift_to_tcx<'a, 'gcx>(&self, _: $crate::ty::TyCtxt<'a, 'gcx, $tcx>) -> Option { + Some(Clone::clone(self)) + } + } + )+ + }; + + ($($ty:ty,)+) => { + CloneLiftImpls! { + for <'tcx> { + $($ty,)+ + } + } + }; +} + +/// Used for types that are `Copy` and which **do not care arena +/// allocated data** (i.e., don't need to be folded). +#[macro_export] +macro_rules! CloneTypeFoldableImpls { + (for <$tcx:lifetime> { $($ty:ty,)+ }) => { + $( + impl<$tcx> $crate::ty::fold::TypeFoldable<$tcx> for $ty { + fn super_fold_with<'gcx: $tcx, F: $crate::ty::fold::TypeFolder<'gcx, $tcx>>( + &self, + _: &mut F + ) -> $ty { + Clone::clone(self) + } + + fn super_visit_with>( + &self, + _: &mut F) + -> bool + { + false + } + } + )+ + }; + + ($($ty:ty,)+) => { + CloneTypeFoldableImpls! { + for <'tcx> { + $($ty,)+ + } + } + }; +} + +#[macro_export] +macro_rules! CloneTypeFoldableAndLiftImpls { + ($($t:tt)*) => { + CloneTypeFoldableImpls! { $($t)* } + CloneLiftImpls! { $($t)* } + } +} + +#[macro_export] +macro_rules! BraceStructLiftImpl { + (impl<$($p:tt),*> Lift<$tcx:tt> for $s:path { + type Lifted = $lifted:ty; + $($field:ident),* $(,)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::ty::Lift<$tcx> for $s + $(where $($wc)*)* + { + type Lifted = $lifted; + + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<$lifted> { + $(let $field = tcx.lift(&self.$field)?;)* + Some(Self::Lifted { $($field),* }) + } + } + }; +} + +#[macro_export] +macro_rules! EnumLiftImpl { + (impl<$($p:tt),*> Lift<$tcx:tt> for $s:path { + type Lifted = $lifted:ty; + $( + ($variant:path) ( $( $variant_arg:ident),* ) + ),* + $(,)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::ty::Lift<$tcx> for $s + $(where $($wc)*)* + { + type Lifted = $lifted; + + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<$lifted> { + match self { + $($variant ( $($variant_arg),* ) => { + Some($variant ( $(tcx.lift($variant_arg)?),* )) + })* + } + } + } + }; +} + +#[macro_export] +macro_rules! BraceStructTypeFoldableImpl { + (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path { + $($field:ident),* $(,)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s + $(where $($wc)*)* + { + fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>( + &self, + folder: &mut V, + ) -> Self { + let $s { $($field,)* } = self; + $s { $($field: $crate::ty::fold::TypeFoldable::fold_with($field, folder),)* } + } + + fn super_visit_with>( + &self, + visitor: &mut V, + ) -> bool { + let $s { $($field,)* } = self; + false $(|| $crate::ty::fold::TypeFoldable::visit_with($field, visitor))* + } + } + }; +} + +#[macro_export] +macro_rules! TupleStructTypeFoldableImpl { + (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path { + $($field:ident),* $(,)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s + $(where $($wc)*)* + { + fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>( + &self, + folder: &mut V, + ) -> Self { + let $s($($field,)*)= self; + $s($($crate::ty::fold::TypeFoldable::fold_with($field, folder),)*) + } + + fn super_visit_with>( + &self, + visitor: &mut V, + ) -> bool { + let $s($($field,)*) = self; + false $(|| $crate::ty::fold::TypeFoldable::visit_with($field, visitor))* + } + } + }; +} + +#[macro_export] +macro_rules! EnumTypeFoldableImpl { + (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path { + $($variants:tt)* + } $(where $($wc:tt)*)*) => { + impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s + $(where $($wc)*)* + { + fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>( + &self, + folder: &mut V, + ) -> Self { + EnumTypeFoldableImpl!(@FoldVariants(self, folder) input($($variants)*) output()) + } + + fn super_visit_with>( + &self, + visitor: &mut V, + ) -> bool { + EnumTypeFoldableImpl!(@VisitVariants(self, visitor) input($($variants)*) output()) + } + } + }; + + (@FoldVariants($this:expr, $folder:expr) input() output($($output:tt)*)) => { + match $this { + $($output)* + } + }; + + (@FoldVariants($this:expr, $folder:expr) + input( ($variant:path) ( $($variant_arg:ident),* ) , $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeFoldableImpl!( + @FoldVariants($this, $folder) + input($($input)*) + output( + $variant ( $($variant_arg),* ) => { + $variant ( + $($crate::ty::fold::TypeFoldable::fold_with($variant_arg, $folder)),* + ) + } + $($output)* + ) + ) + }; + + (@FoldVariants($this:expr, $folder:expr) + input( ($variant:path) { $($variant_arg:ident),* $(,)* } , $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeFoldableImpl!( + @FoldVariants($this, $folder) + input($($input)*) + output( + $variant { $($variant_arg),* } => { + $variant { + $($variant_arg: $crate::ty::fold::TypeFoldable::fold_with( + $variant_arg, $folder + )),* } + } + $($output)* + ) + ) + }; + + (@FoldVariants($this:expr, $folder:expr) + input( ($variant:path), $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeFoldableImpl!( + @FoldVariants($this, $folder) + input($($input)*) + output( + $variant => { $variant } + $($output)* + ) + ) + }; + + (@VisitVariants($this:expr, $visitor:expr) input() output($($output:tt)*)) => { + match $this { + $($output)* + } + }; + + (@VisitVariants($this:expr, $visitor:expr) + input( ($variant:path) ( $($variant_arg:ident),* ) , $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeFoldableImpl!( + @VisitVariants($this, $visitor) + input($($input)*) + output( + $variant ( $($variant_arg),* ) => { + false $(|| $crate::ty::fold::TypeFoldable::visit_with( + $variant_arg, $visitor + ))* + } + $($output)* + ) + ) + }; + + (@VisitVariants($this:expr, $visitor:expr) + input( ($variant:path) { $($variant_arg:ident),* $(,)* } , $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeFoldableImpl!( + @VisitVariants($this, $visitor) + input($($input)*) + output( + $variant { $($variant_arg),* } => { + false $(|| $crate::ty::fold::TypeFoldable::visit_with( + $variant_arg, $visitor + ))* + } + $($output)* + ) + ) + }; + + (@VisitVariants($this:expr, $visitor:expr) + input( ($variant:path), $($input:tt)*) + output( $($output:tt)*) ) => { + EnumTypeFoldableImpl!( + @VisitVariants($this, $visitor) + input($($input)*) + output( + $variant => { false } + $($output)* + ) + ) + }; +} + diff --git a/src/librustc/mir/cache.rs b/src/librustc/mir/cache.rs index 45cb70d007068..9b75c19a875eb 100644 --- a/src/librustc/mir/cache.rs +++ b/src/librustc/mir/cache.rs @@ -76,3 +76,7 @@ fn calculate_predecessors(mir: &Mir) -> IndexVec> { result } + +CloneTypeFoldableAndLiftImpls! { + Cache, +} diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index e39765699f9be..939710ffd2b86 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2109,148 +2109,90 @@ pub enum ClosureOutlivesSubject<'tcx> { * TypeFoldable implementations for MIR types */ -impl<'tcx> TypeFoldable<'tcx> for Mir<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - Mir { - basic_blocks: self.basic_blocks.fold_with(folder), - visibility_scopes: self.visibility_scopes.clone(), - visibility_scope_info: self.visibility_scope_info.clone(), - promoted: self.promoted.fold_with(folder), - yield_ty: self.yield_ty.fold_with(folder), - generator_drop: self.generator_drop.fold_with(folder), - generator_layout: self.generator_layout.fold_with(folder), - local_decls: self.local_decls.fold_with(folder), - arg_count: self.arg_count, - upvar_decls: self.upvar_decls.clone(), - spread_arg: self.spread_arg, - span: self.span, - cache: cache::Cache::new() - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.basic_blocks.visit_with(visitor) || - self.generator_drop.visit_with(visitor) || - self.generator_layout.visit_with(visitor) || - self.yield_ty.visit_with(visitor) || - self.promoted.visit_with(visitor) || - self.local_decls.visit_with(visitor) - } +CloneTypeFoldableAndLiftImpls! { + Mutability, + SourceInfo, + UpvarDecl, + ValidationOp, + VisibilityScopeData, + VisibilityScope, + VisibilityScopeInfo, } -impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - GeneratorLayout { - fields: self.fields.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.fields.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for Mir<'tcx> { + basic_blocks, + visibility_scopes, + visibility_scope_info, + promoted, + yield_ty, + generator_drop, + generator_layout, + local_decls, + arg_count, + upvar_decls, + spread_arg, + span, + cache, } } -impl<'tcx> TypeFoldable<'tcx> for LocalDecl<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - LocalDecl { - ty: self.ty.fold_with(folder), - ..self.clone() - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.ty.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> { + fields } } -impl<'tcx> TypeFoldable<'tcx> for BasicBlockData<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - BasicBlockData { - statements: self.statements.fold_with(folder), - terminator: self.terminator.fold_with(folder), - is_cleanup: self.is_cleanup - } +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for LocalDecl<'tcx> { + mutability, + is_user_variable, + internal, + ty, + name, + source_info, + syntactic_scope, } +} - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.statements.visit_with(visitor) || self.terminator.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for BasicBlockData<'tcx> { + statements, + terminator, + is_cleanup, } } -impl<'tcx> TypeFoldable<'tcx> for ValidationOperand<'tcx, Place<'tcx>> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ValidationOperand { - place: self.place.fold_with(folder), - ty: self.ty.fold_with(folder), - re: self.re, - mutbl: self.mutbl, - } +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ValidationOperand<'tcx, Place<'tcx>> { + place, ty, re, mutbl } +} - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.place.visit_with(visitor) || self.ty.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> { + source_info, kind } } -impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - use mir::StatementKind::*; - - let kind = match self.kind { - Assign(ref place, ref rval) => Assign(place.fold_with(folder), rval.fold_with(folder)), - SetDiscriminant { ref place, variant_index } => SetDiscriminant { - place: place.fold_with(folder), - variant_index, - }, - StorageLive(ref local) => StorageLive(local.fold_with(folder)), - StorageDead(ref local) => StorageDead(local.fold_with(folder)), - InlineAsm { ref asm, ref outputs, ref inputs } => InlineAsm { - asm: asm.clone(), - outputs: outputs.fold_with(folder), - inputs: inputs.fold_with(folder) - }, - - // Note for future: If we want to expose the region scopes - // during the fold, we need to either generalize EndRegion - // to carry `[ty::Region]`, or extend the `TypeFolder` - // trait with a `fn fold_scope`. - EndRegion(ref region_scope) => EndRegion(region_scope.clone()), - - Validate(ref op, ref places) => - Validate(op.clone(), - places.iter().map(|operand| operand.fold_with(folder)).collect()), - - Nop => Nop, - }; - Statement { - source_info: self.source_info, - kind, - } +EnumTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for StatementKind<'tcx> { + (StatementKind::Assign)(a, b), + (StatementKind::SetDiscriminant) { place, variant_index }, + (StatementKind::StorageLive)(a), + (StatementKind::StorageDead)(a), + (StatementKind::InlineAsm) { asm, outputs, inputs }, + (StatementKind::Validate)(a, b), + (StatementKind::EndRegion)(a), + (StatementKind::Nop), } +} - fn super_visit_with>(&self, visitor: &mut V) -> bool { - use mir::StatementKind::*; - - match self.kind { - Assign(ref place, ref rval) => { place.visit_with(visitor) || rval.visit_with(visitor) } - SetDiscriminant { ref place, .. } => place.visit_with(visitor), - StorageLive(ref local) | - StorageDead(ref local) => local.visit_with(visitor), - InlineAsm { ref outputs, ref inputs, .. } => - outputs.visit_with(visitor) || inputs.visit_with(visitor), - - // Note for future: If we want to expose the region scopes - // during the visit, we need to either generalize EndRegion - // to carry `[ty::Region]`, or extend the `TypeVisitor` - // trait with a `fn visit_scope`. - EndRegion(ref _scope) => false, - - Validate(ref _op, ref places) => - places.iter().any(|ty_and_place| ty_and_place.visit_with(visitor)), - - Nop => false, - } - } +EnumTypeFoldableImpl! { + impl<'tcx, T> TypeFoldable<'tcx> for ClearCrossCrate { + (ClearCrossCrate::Clear), + (ClearCrossCrate::Set)(a), + } where T: TypeFoldable<'tcx> } impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 7d232ac20bfd5..d779ccd17360a 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -16,7 +16,6 @@ use mir::*; use ty::subst::{Subst, Substs}; use ty::{self, AdtDef, Ty, TyCtxt}; -use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use hir; use ty::util::IntTypeExt; @@ -100,25 +99,10 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> { } } -impl<'tcx> TypeFoldable<'tcx> for PlaceTy<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - match *self { - PlaceTy::Ty { ty } => PlaceTy::Ty { ty: ty.fold_with(folder) }, - PlaceTy::Downcast { adt_def, substs, variant_index } => { - PlaceTy::Downcast { - adt_def, - substs: substs.fold_with(folder), - variant_index, - } - } - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - PlaceTy::Ty { ty } => ty.visit_with(visitor), - PlaceTy::Downcast { substs, .. } => substs.visit_with(visitor) - } +EnumTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for PlaceTy<'tcx> { + (PlaceTy::Ty) { ty }, + (PlaceTy::Downcast) { adt_def, substs, variant_index }, } } diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 9dd5aaee7b72f..99adc1fc873de 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -363,258 +363,67 @@ impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx } } -impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableImplData<'tcx, N> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - traits::VtableImplData { - impl_def_id: self.impl_def_id, - substs: self.substs.fold_with(folder), - nested: self.nested.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.substs.visit_with(visitor) || self.nested.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableImplData<'tcx, N> { + impl_def_id, substs, nested + } where N: TypeFoldable<'tcx> } -impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableGeneratorData<'tcx, N> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - traits::VtableGeneratorData { - closure_def_id: self.closure_def_id, - substs: self.substs.fold_with(folder), - nested: self.nested.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.substs.visit_with(visitor) || self.nested.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableGeneratorData<'tcx, N> { + closure_def_id, substs, nested + } where N: TypeFoldable<'tcx> } -impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableClosureData<'tcx, N> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - traits::VtableClosureData { - closure_def_id: self.closure_def_id, - substs: self.substs.fold_with(folder), - nested: self.nested.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.substs.visit_with(visitor) || self.nested.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableClosureData<'tcx, N> { + closure_def_id, substs, nested + } where N: TypeFoldable<'tcx> } -impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableAutoImplData { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - traits::VtableAutoImplData { - trait_def_id: self.trait_def_id, - nested: self.nested.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.nested.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableAutoImplData { + trait_def_id, nested + } where N: TypeFoldable<'tcx> } -impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableBuiltinData { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - traits::VtableBuiltinData { - nested: self.nested.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.nested.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableBuiltinData { + nested + } where N: TypeFoldable<'tcx> } -impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx, N> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - traits::VtableObjectData { - upcast_trait_ref: self.upcast_trait_ref.fold_with(folder), - vtable_base: self.vtable_base, - nested: self.nested.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.upcast_trait_ref.visit_with(visitor) || self.nested.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableObjectData<'tcx, N> { + upcast_trait_ref, vtable_base, nested + } where N: TypeFoldable<'tcx> } -impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableFnPointerData<'tcx, N> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - traits::VtableFnPointerData { - fn_ty: self.fn_ty.fold_with(folder), - nested: self.nested.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.fn_ty.visit_with(visitor) || self.nested.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx, N> TypeFoldable<'tcx> for traits::VtableFnPointerData<'tcx, N> { + fn_ty, + nested + } where N: TypeFoldable<'tcx> } -impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - match *self { - traits::VtableImpl(ref v) => traits::VtableImpl(v.fold_with(folder)), - traits::VtableAutoImpl(ref t) => traits::VtableAutoImpl(t.fold_with(folder)), - traits::VtableGenerator(ref d) => { - traits::VtableGenerator(d.fold_with(folder)) - } - traits::VtableClosure(ref d) => { - traits::VtableClosure(d.fold_with(folder)) - } - traits::VtableFnPointer(ref d) => { - traits::VtableFnPointer(d.fold_with(folder)) - } - traits::VtableParam(ref n) => traits::VtableParam(n.fold_with(folder)), - traits::VtableBuiltin(ref d) => traits::VtableBuiltin(d.fold_with(folder)), - traits::VtableObject(ref d) => traits::VtableObject(d.fold_with(folder)), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - traits::VtableImpl(ref v) => v.visit_with(visitor), - traits::VtableAutoImpl(ref t) => t.visit_with(visitor), - traits::VtableGenerator(ref d) => d.visit_with(visitor), - traits::VtableClosure(ref d) => d.visit_with(visitor), - traits::VtableFnPointer(ref d) => d.visit_with(visitor), - traits::VtableParam(ref n) => n.visit_with(visitor), - traits::VtableBuiltin(ref d) => d.visit_with(visitor), - traits::VtableObject(ref d) => d.visit_with(visitor), - } - } +EnumTypeFoldableImpl! { + impl<'tcx, N> TypeFoldable<'tcx> for traits::Vtable<'tcx, N> { + (traits::VtableImpl)(a), + (traits::VtableAutoImpl)(a), + (traits::VtableGenerator)(a), + (traits::VtableClosure)(a), + (traits::VtableFnPointer)(a), + (traits::VtableParam)(a), + (traits::VtableBuiltin)(a), + (traits::VtableObject)(a), + } where N: TypeFoldable<'tcx> } -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Normalized<'tcx, T> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - Normalized { - value: self.value.fold_with(folder), - obligations: self.obligations.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.value.visit_with(visitor) || self.obligations.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx, T> TypeFoldable<'tcx> for Normalized<'tcx, T> { + value, + obligations + } where T: TypeFoldable<'tcx> } -impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCauseCode<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - match *self { - super::ExprAssignable | - super::MatchExpressionArm { arm_span: _, source: _ } | - super::IfExpression | - super::IfExpressionWithNoElse | - super::MainFunctionType | - super::StartFunctionType | - super::IntrinsicType | - super::MethodReceiver | - super::MiscObligation | - super::SliceOrArrayElem | - super::TupleElem | - super::ItemObligation(_) | - super::AssignmentLhsSized | - super::TupleInitializerSized | - super::StructInitializerSized | - super::VariableType(_) | - super::ReturnType(_) | - super::SizedReturnType | - super::SizedYieldType | - super::ReturnNoExpression | - super::RepeatVec | - super::FieldSized(_) | - super::ConstSized | - super::SharedStatic | - super::BlockTailExpression(_) | - super::CompareImplMethodObligation { .. } => self.clone(), - - super::ProjectionWf(proj) => super::ProjectionWf(proj.fold_with(folder)), - super::ReferenceOutlivesReferent(ty) => { - super::ReferenceOutlivesReferent(ty.fold_with(folder)) - } - super::ObjectTypeBound(ty, r) => { - super::ObjectTypeBound(ty.fold_with(folder), r.fold_with(folder)) - } - super::ObjectCastObligation(ty) => { - super::ObjectCastObligation(ty.fold_with(folder)) - } - super::BuiltinDerivedObligation(ref cause) => { - super::BuiltinDerivedObligation(cause.fold_with(folder)) - } - super::ImplDerivedObligation(ref cause) => { - super::ImplDerivedObligation(cause.fold_with(folder)) - } - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - super::ExprAssignable | - super::MatchExpressionArm { arm_span: _, source: _ } | - super::IfExpression | - super::IfExpressionWithNoElse | - super::MainFunctionType | - super::StartFunctionType | - super::IntrinsicType | - super::MethodReceiver | - super::MiscObligation | - super::SliceOrArrayElem | - super::TupleElem | - super::ItemObligation(_) | - super::AssignmentLhsSized | - super::TupleInitializerSized | - super::StructInitializerSized | - super::VariableType(_) | - super::ReturnType(_) | - super::SizedReturnType | - super::SizedYieldType | - super::ReturnNoExpression | - super::RepeatVec | - super::FieldSized(_) | - super::ConstSized | - super::SharedStatic | - super::BlockTailExpression(_) | - super::CompareImplMethodObligation { .. } => false, - - super::ProjectionWf(proj) => proj.visit_with(visitor), - super::ReferenceOutlivesReferent(ty) => ty.visit_with(visitor), - super::ObjectTypeBound(ty, r) => ty.visit_with(visitor) || r.visit_with(visitor), - super::ObjectCastObligation(ty) => ty.visit_with(visitor), - super::BuiltinDerivedObligation(ref cause) => cause.visit_with(visitor), - super::ImplDerivedObligation(ref cause) => cause.visit_with(visitor) - } - } -} - -impl<'tcx> TypeFoldable<'tcx> for traits::DerivedObligationCause<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - traits::DerivedObligationCause { - parent_trait_ref: self.parent_trait_ref.fold_with(folder), - parent_code: self.parent_code.fold_with(folder) - } - } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.parent_trait_ref.visit_with(visitor) || self.parent_code.visit_with(visitor) - } -} - -impl<'tcx> TypeFoldable<'tcx> for traits::ObligationCause<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - traits::ObligationCause { - span: self.span, - body_id: self.body_id, - code: self.code.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.code.visit_with(visitor) - } -} diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 4cc7406af721e..8071cd0c639da 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -49,6 +49,9 @@ use util::nodemap::FxHashSet; /// The TypeFoldable trait is implemented for every type that can be folded. /// Basically, every type that has a corresponding method in TypeFolder. +/// +/// To implement this conveniently, use the +/// `BraceStructTypeFoldableImpl` etc macros found in `macros.rs`. pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self; fn fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index bae1ce31a5e77..0ca2769aa17b8 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -18,7 +18,6 @@ use middle::const_val::ConstVal; use traits::Reveal; use ty::subst::{UnpackedKind, Substs}; use ty::{self, Ty, TyCtxt, TypeFoldable}; -use ty::fold::{TypeVisitor, TypeFolder}; use ty::error::{ExpectedFound, TypeError}; use mir::interpret::{GlobalId, Value, PrimVal}; use util::common::ErrorReported; @@ -326,13 +325,9 @@ impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> { #[derive(Debug, Clone)] struct GeneratorWitness<'tcx>(&'tcx ty::Slice>); -impl<'tcx> TypeFoldable<'tcx> for GeneratorWitness<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - GeneratorWitness(self.0.fold_with(folder)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.0.visit_with(visitor) +TupleStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for GeneratorWitness<'tcx> { + a } } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 78fccaa113106..0627bcdfb0ec1 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -28,153 +28,37 @@ use std::rc::Rc; // For things that don't carry any arena-allocated data (and are // copy...), just add them to this list. -macro_rules! CopyImpls { - ($($ty:ty,)+) => { - $( - impl<'tcx> Lift<'tcx> for $ty { - type Lifted = Self; - fn lift_to_tcx<'a, 'gcx>(&self, _: TyCtxt<'a, 'gcx, 'tcx>) -> Option { - Some(*self) - } - } - - impl<'tcx> TypeFoldable<'tcx> for $ty { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _: &mut F) -> $ty { - *self - } - - fn super_visit_with>(&self, _: &mut F) -> bool { - false - } - } - )+ - } -} - -CopyImpls! { +CloneTypeFoldableAndLiftImpls! { (), + bool, + usize, + u64, + ::middle::region::Scope, + ::syntax::ast::FloatTy, + ::syntax::ast::NodeId, + ::syntax_pos::symbol::Symbol, + ::hir::def::Def, + ::hir::def_id::DefId, + ::hir::InlineAsm, + ::hir::MatchSource, + ::hir::Mutability, ::hir::Unsafety, ::syntax::abi::Abi, - ::hir::def_id::DefId, ::mir::Local, ::mir::Promoted, ::traits::Reveal, + ::ty::adjustment::AutoBorrowMutability, + ::ty::AdtKind, + // Including `BoundRegion` is a *bit* dubious, but direct + // references to bound region appear in `ty::Error`, and aren't + // really meant to be folded. In general, we can only fold a fully + // general `Region`. + ::ty::BoundRegion, + ::ty::ClosureKind, + ::ty::IntVarValue, ::syntax_pos::Span, } -/////////////////////////////////////////////////////////////////////////// -// Macros -// -// When possible, use one of these (relatively) convenient macros to write -// the impls for you. - -#[macro_export] -macro_rules! BraceStructLiftImpl { - (impl<$($p:tt),*> Lift<$tcx:tt> for $s:path { - type Lifted = $lifted:ty; - $($field:ident),* $(,)* - } $(where $($wc:tt)*)*) => { - impl<$($p),*> $crate::ty::Lift<$tcx> for $s - $(where $($wc)*)* - { - type Lifted = $lifted; - - fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<$lifted> { - $(let $field = tcx.lift(&self.$field)?;)* - Some(Self::Lifted { $($field),* }) - } - } - }; -} - -#[macro_export] -macro_rules! EnumLiftImpl { - (impl<$($p:tt),*> Lift<$tcx:tt> for $s:path { - type Lifted = $lifted:ty; - $( - ($variant:path) ( $( $variant_arg:ident),* ) - ),* - $(,)* - } $(where $($wc:tt)*)*) => { - impl<$($p),*> $crate::ty::Lift<$tcx> for $s - $(where $($wc)*)* - { - type Lifted = $lifted; - - fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<$lifted> { - match self { - $($variant ( $($variant_arg),* ) => { - Some($variant ( $(tcx.lift($variant_arg)?),* )) - })* - } - } - } - }; -} - -#[macro_export] -macro_rules! BraceStructTypeFoldableImpl { - (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path { - $($field:ident),* $(,)* - } $(where $($wc:tt)*)*) => { - impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s - $(where $($wc)*)* - { - fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>( - &self, - folder: &mut V, - ) -> Self { - let $s { $($field,)* } = self; - $s { $($field: $field.fold_with(folder),)* } - } - - fn super_visit_with>( - &self, - visitor: &mut V, - ) -> bool { - let $s { $($field,)* } = self; - false $(|| $field.visit_with(visitor))* - } - } - }; -} - -#[macro_export] -macro_rules! EnumTypeFoldableImpl { - (impl<$($p:tt),*> TypeFoldable<$tcx:tt> for $s:path { - $( - ($variant:path) ( $( $variant_arg:ident),* ) - ),* - $(,)* - } $(where $($wc:tt)*)*) => { - impl<$($p),*> $crate::ty::fold::TypeFoldable<$tcx> for $s - $(where $($wc)*)* - { - fn super_fold_with<'gcx: $tcx, V: $crate::ty::fold::TypeFolder<'gcx, $tcx>>( - &self, - folder: &mut V, - ) -> Self { - match self { - $($variant ( $($variant_arg),* ) => { - $variant ( $($variant_arg.fold_with(folder)),* ) - })* - } - } - - fn super_visit_with>( - &self, - visitor: &mut V, - ) -> bool { - match self { - $($variant ( $($variant_arg),* ) => { - false $(|| $variant_arg.visit_with(visitor))* - })* - } - } - } - }; -} - /////////////////////////////////////////////////////////////////////////// // Lift implementations @@ -776,6 +660,17 @@ BraceStructLiftImpl! { // can easily refactor the folding into the TypeFolder trait as // needed. +/// AdtDefs are basically the same as a DefId. +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::AdtDef { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, _folder: &mut F) -> Self { + *self + } + + fn super_visit_with>(&self, _visitor: &mut V) -> bool { + false + } +} + impl<'tcx, T:TypeFoldable<'tcx>, U:TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T, U) { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> (T, U) { (self.0.fold_with(folder), self.1.fold_with(folder)) @@ -786,14 +681,11 @@ impl<'tcx, T:TypeFoldable<'tcx>, U:TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T } } -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Option { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - self.as_ref().map(|t| t.fold_with(folder)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } +EnumTypeFoldableImpl! { + impl<'tcx, T> TypeFoldable<'tcx> for Option { + (Some)(a), + (None), + } where T: TypeFoldable<'tcx> } impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc { @@ -881,22 +773,11 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice } } -impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialPredicate<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - use ty::ExistentialPredicate::*; - match *self { - Trait(ref tr) => Trait(tr.fold_with(folder)), - Projection(ref p) => Projection(p.fold_with(folder)), - AutoTrait(did) => AutoTrait(did), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - ty::ExistentialPredicate::Trait(ref tr) => tr.visit_with(visitor), - ty::ExistentialPredicate::Projection(ref p) => p.visit_with(visitor), - ty::ExistentialPredicate::AutoTrait(_) => false, - } +EnumTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialPredicate<'tcx> { + (ty::ExistentialPredicate::Trait)(a), + (ty::ExistentialPredicate::Projection)(a), + (ty::ExistentialPredicate::AutoTrait)(a), } } @@ -1049,13 +930,9 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { } } -impl<'tcx> TypeFoldable<'tcx> for ty::TypeAndMut<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::TypeAndMut { ty: self.ty.fold_with(folder), mutbl: self.mutbl } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.ty.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::TypeAndMut<'tcx> { + ty, mutbl } } @@ -1065,20 +942,9 @@ BraceStructTypeFoldableImpl! { } } -impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - let inputs_and_output = self.inputs_and_output.fold_with(folder); - ty::FnSig { - inputs_and_output: folder.tcx().intern_type_list(&inputs_and_output), - variadic: self.variadic, - unsafety: self.unsafety, - abi: self.abi, - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.inputs().iter().any(|i| i.visit_with(visitor)) || - self.output().visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> { + inputs_and_output, variadic, unsafety, abi } } @@ -1117,28 +983,15 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Region<'tcx> { } } -impl<'tcx> TypeFoldable<'tcx> for ty::ClosureSubsts<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::ClosureSubsts { - substs: self.substs.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.substs.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::ClosureSubsts<'tcx> { + substs, } } -impl<'tcx> TypeFoldable<'tcx> for ty::GeneratorInterior<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::GeneratorInterior { - witness: self.witness.fold_with(folder), - movable: self.movable, - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.witness.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::GeneratorInterior<'tcx> { + witness, movable, } } @@ -1149,74 +1002,32 @@ BraceStructTypeFoldableImpl! { } } -impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::Adjust<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - match *self { - ty::adjustment::Adjust::NeverToAny | - ty::adjustment::Adjust::ReifyFnPointer | - ty::adjustment::Adjust::UnsafeFnPointer | - ty::adjustment::Adjust::ClosureFnPointer | - ty::adjustment::Adjust::MutToConstPointer | - ty::adjustment::Adjust::Unsize => self.clone(), - ty::adjustment::Adjust::Deref(ref overloaded) => { - ty::adjustment::Adjust::Deref(overloaded.fold_with(folder)) - } - ty::adjustment::Adjust::Borrow(ref autoref) => { - ty::adjustment::Adjust::Borrow(autoref.fold_with(folder)) - } - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - ty::adjustment::Adjust::NeverToAny | - ty::adjustment::Adjust::ReifyFnPointer | - ty::adjustment::Adjust::UnsafeFnPointer | - ty::adjustment::Adjust::ClosureFnPointer | - ty::adjustment::Adjust::MutToConstPointer | - ty::adjustment::Adjust::Unsize => false, - ty::adjustment::Adjust::Deref(ref overloaded) => { - overloaded.visit_with(visitor) - } - ty::adjustment::Adjust::Borrow(ref autoref) => { - autoref.visit_with(visitor) - } - } +EnumTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::Adjust<'tcx> { + (ty::adjustment::Adjust::NeverToAny), + (ty::adjustment::Adjust::ReifyFnPointer), + (ty::adjustment::Adjust::UnsafeFnPointer), + (ty::adjustment::Adjust::ClosureFnPointer), + (ty::adjustment::Adjust::MutToConstPointer), + (ty::adjustment::Adjust::Unsize), + (ty::adjustment::Adjust::Deref)(a), + (ty::adjustment::Adjust::Borrow)(a), } } -impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::OverloadedDeref<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::adjustment::OverloadedDeref { - region: self.region.fold_with(folder), - mutbl: self.mutbl, - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.region.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::OverloadedDeref<'tcx> { + region, mutbl, } } -impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::AutoBorrow<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - match *self { - ty::adjustment::AutoBorrow::Ref(ref r, m) => { - ty::adjustment::AutoBorrow::Ref(r.fold_with(folder), m) - } - ty::adjustment::AutoBorrow::RawPtr(m) => ty::adjustment::AutoBorrow::RawPtr(m) - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - ty::adjustment::AutoBorrow::Ref(r, _m) => r.visit_with(visitor), - ty::adjustment::AutoBorrow::RawPtr(_m) => false, - } +EnumTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::AutoBorrow<'tcx> { + (ty::adjustment::AutoBorrow::Ref)(a, b), + (ty::adjustment::AutoBorrow::RawPtr)(m), } } - BraceStructTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for ty::GenericPredicates<'tcx> { parent, predicates @@ -1234,43 +1045,17 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice> { } } -impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - match *self { - ty::Predicate::Trait(ref a) => - ty::Predicate::Trait(a.fold_with(folder)), - ty::Predicate::Subtype(ref binder) => - ty::Predicate::Subtype(binder.fold_with(folder)), - ty::Predicate::RegionOutlives(ref binder) => - ty::Predicate::RegionOutlives(binder.fold_with(folder)), - ty::Predicate::TypeOutlives(ref binder) => - ty::Predicate::TypeOutlives(binder.fold_with(folder)), - ty::Predicate::Projection(ref binder) => - ty::Predicate::Projection(binder.fold_with(folder)), - ty::Predicate::WellFormed(data) => - ty::Predicate::WellFormed(data.fold_with(folder)), - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => - ty::Predicate::ClosureKind(closure_def_id, closure_substs.fold_with(folder), kind), - ty::Predicate::ObjectSafe(trait_def_id) => - ty::Predicate::ObjectSafe(trait_def_id), - ty::Predicate::ConstEvaluatable(def_id, substs) => - ty::Predicate::ConstEvaluatable(def_id, substs.fold_with(folder)), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - ty::Predicate::Trait(ref a) => a.visit_with(visitor), - ty::Predicate::Subtype(ref binder) => binder.visit_with(visitor), - ty::Predicate::RegionOutlives(ref binder) => binder.visit_with(visitor), - ty::Predicate::TypeOutlives(ref binder) => binder.visit_with(visitor), - ty::Predicate::Projection(ref binder) => binder.visit_with(visitor), - ty::Predicate::WellFormed(data) => data.visit_with(visitor), - ty::Predicate::ClosureKind(_closure_def_id, closure_substs, _kind) => - closure_substs.visit_with(visitor), - ty::Predicate::ObjectSafe(_trait_def_id) => false, - ty::Predicate::ConstEvaluatable(_def_id, substs) => substs.visit_with(visitor), - } +EnumTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { + (ty::Predicate::Trait)(a), + (ty::Predicate::Subtype)(a), + (ty::Predicate::RegionOutlives)(a), + (ty::Predicate::TypeOutlives)(a), + (ty::Predicate::Projection)(a), + (ty::Predicate::WellFormed)(a), + (ty::Predicate::ClosureKind)(a, b, c), + (ty::Predicate::ObjectSafe)(a), + (ty::Predicate::ConstEvaluatable)(a, b), } } @@ -1298,71 +1083,40 @@ BraceStructTypeFoldableImpl! { } } -impl<'tcx> TypeFoldable<'tcx> for ty::SubtypePredicate<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::SubtypePredicate { - a_is_expected: self.a_is_expected, - a: self.a.fold_with(folder), - b: self.b.fold_with(folder) - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.a.visit_with(visitor) || self.b.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx, T> TypeFoldable<'tcx> for ty::ParamEnvAnd<'tcx, T> { + param_env, value + } where T: TypeFoldable<'tcx> } -impl<'tcx> TypeFoldable<'tcx> for ty::TraitPredicate<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::TraitPredicate { - trait_ref: self.trait_ref.fold_with(folder) - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.trait_ref.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::SubtypePredicate<'tcx> { + a_is_expected, a, b } } -impl<'tcx,T,U> TypeFoldable<'tcx> for ty::OutlivesPredicate - where T : TypeFoldable<'tcx>, - U : TypeFoldable<'tcx>, -{ - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::OutlivesPredicate(self.0.fold_with(folder), - self.1.fold_with(folder)) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.0.visit_with(visitor) || self.1.visit_with(visitor) +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::TraitPredicate<'tcx> { + trait_ref } } -impl<'tcx> TypeFoldable<'tcx> for ty::ClosureUpvar<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::ClosureUpvar { - def: self.def, - span: self.span, - ty: self.ty.fold_with(folder), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.ty.visit_with(visitor) - } +TupleStructTypeFoldableImpl! { + impl<'tcx,T,U> TypeFoldable<'tcx> for ty::OutlivesPredicate { + a, b + } where T : TypeFoldable<'tcx>, U : TypeFoldable<'tcx>, } -impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::error::ExpectedFound { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::error::ExpectedFound { - expected: self.expected.fold_with(folder), - found: self.found.fold_with(folder), - } +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::ClosureUpvar<'tcx> { + def, span, ty } +} - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.expected.visit_with(visitor) || self.found.visit_with(visitor) - } +BraceStructTypeFoldableImpl! { + impl<'tcx, T> TypeFoldable<'tcx> for ty::error::ExpectedFound { + expected, found + } where T: TypeFoldable<'tcx> } impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec { @@ -1375,69 +1129,28 @@ impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec } } -impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - use ty::error::TypeError::*; - - match *self { - Mismatch => Mismatch, - UnsafetyMismatch(x) => UnsafetyMismatch(x.fold_with(folder)), - AbiMismatch(x) => AbiMismatch(x.fold_with(folder)), - Mutability => Mutability, - TupleSize(x) => TupleSize(x), - FixedArraySize(x) => FixedArraySize(x), - ArgCount => ArgCount, - RegionsDoesNotOutlive(a, b) => { - RegionsDoesNotOutlive(a.fold_with(folder), b.fold_with(folder)) - }, - RegionsInsufficientlyPolymorphic(a, b) => { - RegionsInsufficientlyPolymorphic(a, b.fold_with(folder)) - }, - RegionsOverlyPolymorphic(a, b) => { - RegionsOverlyPolymorphic(a, b.fold_with(folder)) - }, - IntMismatch(x) => IntMismatch(x), - FloatMismatch(x) => FloatMismatch(x), - Traits(x) => Traits(x), - VariadicMismatch(x) => VariadicMismatch(x), - CyclicTy(t) => CyclicTy(t.fold_with(folder)), - ProjectionMismatched(x) => ProjectionMismatched(x), - ProjectionBoundsLength(x) => ProjectionBoundsLength(x), - Sorts(x) => Sorts(x.fold_with(folder)), - ExistentialMismatch(x) => ExistentialMismatch(x.fold_with(folder)), - OldStyleLUB(ref x) => OldStyleLUB(x.fold_with(folder)), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - use ty::error::TypeError::*; - - match *self { - UnsafetyMismatch(x) => x.visit_with(visitor), - AbiMismatch(x) => x.visit_with(visitor), - RegionsDoesNotOutlive(a, b) => { - a.visit_with(visitor) || b.visit_with(visitor) - }, - RegionsInsufficientlyPolymorphic(_, b) | - RegionsOverlyPolymorphic(_, b) => { - b.visit_with(visitor) - }, - Sorts(x) => x.visit_with(visitor), - OldStyleLUB(ref x) => x.visit_with(visitor), - ExistentialMismatch(x) => x.visit_with(visitor), - CyclicTy(t) => t.visit_with(visitor), - Mismatch | - Mutability | - TupleSize(_) | - FixedArraySize(_) | - ArgCount | - IntMismatch(_) | - FloatMismatch(_) | - Traits(_) | - VariadicMismatch(_) | - ProjectionMismatched(_) | - ProjectionBoundsLength(_) => false, - } +EnumTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> { + (ty::error::TypeError::Mismatch), + (ty::error::TypeError::UnsafetyMismatch)(x), + (ty::error::TypeError::AbiMismatch)(x), + (ty::error::TypeError::Mutability), + (ty::error::TypeError::TupleSize)(x), + (ty::error::TypeError::FixedArraySize)(x), + (ty::error::TypeError::ArgCount), + (ty::error::TypeError::RegionsDoesNotOutlive)(a, b), + (ty::error::TypeError::RegionsInsufficientlyPolymorphic)(a, b), + (ty::error::TypeError::RegionsOverlyPolymorphic)(a, b), + (ty::error::TypeError::IntMismatch)(x), + (ty::error::TypeError::FloatMismatch)(x), + (ty::error::TypeError::Traits)(x), + (ty::error::TypeError::VariadicMismatch)(x), + (ty::error::TypeError::CyclicTy)(t), + (ty::error::TypeError::ProjectionMismatched)(x), + (ty::error::TypeError::ProjectionBoundsLength)(x), + (ty::error::TypeError::Sorts)(x), + (ty::error::TypeError::ExistentialMismatch)(x), + (ty::error::TypeError::OldStyleLUB)(x), } } From 5ddcd09b53989f0a3c3fcefa5ba3ee9d40e60064 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 9 Feb 2018 10:39:46 -0500 Subject: [PATCH 03/27] add `TypeRelation` and `Lift` impls for `Kind` --- src/librustc/ty/relate.rs | 33 +++++++++++++++++++++++---------- src/librustc/ty/subst.rs | 13 ++++++++++++- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 0ca2769aa17b8..7f48f8c431fac 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -16,7 +16,7 @@ use hir::def_id::DefId; use middle::const_val::ConstVal; use traits::Reveal; -use ty::subst::{UnpackedKind, Substs}; +use ty::subst::{Kind, UnpackedKind, Substs}; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::error::{ExpectedFound, TypeError}; use mir::interpret::{GlobalId, Value, PrimVal}; @@ -142,15 +142,7 @@ pub fn relate_substs<'a, 'gcx, 'tcx, R>(relation: &mut R, let params = a_subst.iter().zip(b_subst).enumerate().map(|(i, (a, b))| { let variance = variances.map_or(ty::Invariant, |v| v[i]); - match (a.unpack(), b.unpack()) { - (UnpackedKind::Lifetime(a_lt), UnpackedKind::Lifetime(b_lt)) => { - Ok(relation.relate_with_variance(variance, &a_lt, &b_lt)?.into()) - } - (UnpackedKind::Type(a_ty), UnpackedKind::Type(b_ty)) => { - Ok(relation.relate_with_variance(variance, &a_ty, &b_ty)?.into()) - } - (UnpackedKind::Lifetime(_), _) | (UnpackedKind::Type(_), _) => bug!() - } + relation.relate_with_variance(variance, a, b) }); Ok(tcx.mk_substs(params)?) @@ -693,6 +685,27 @@ impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for Box { } } +impl<'tcx> Relate<'tcx> for Kind<'tcx> { + fn relate<'a, 'gcx, R>( + relation: &mut R, + a: &Kind<'tcx>, + b: &Kind<'tcx> + ) -> RelateResult<'tcx, Kind<'tcx>> + where + R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'a+'tcx, 'tcx: 'a, + { + match (a.unpack(), b.unpack()) { + (UnpackedKind::Lifetime(a_lt), UnpackedKind::Lifetime(b_lt)) => { + Ok(relation.relate(&a_lt, &b_lt)?.into()) + } + (UnpackedKind::Type(a_ty), UnpackedKind::Type(b_ty)) => { + Ok(relation.relate(&a_ty, &b_ty)?.into()) + } + (UnpackedKind::Lifetime(_), _) | (UnpackedKind::Type(_), _) => bug!() + } + } +} + /////////////////////////////////////////////////////////////////////////// // Error handling diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs index 5e3417e98c2e9..e70c72335aa36 100644 --- a/src/librustc/ty/subst.rs +++ b/src/librustc/ty/subst.rs @@ -11,7 +11,7 @@ // Type substitutions. use hir::def_id::DefId; -use ty::{self, Slice, Region, Ty, TyCtxt}; +use ty::{self, Lift, Slice, Region, Ty, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use serialize::{self, Encodable, Encoder, Decodable, Decoder}; @@ -113,6 +113,17 @@ impl<'tcx> fmt::Display for Kind<'tcx> { } } +impl<'a, 'tcx> Lift<'tcx> for Kind<'a> { + type Lifted = Kind<'tcx>; + + fn lift_to_tcx<'cx, 'gcx>(&self, tcx: TyCtxt<'cx, 'gcx, 'tcx>) -> Option { + match self.unpack() { + UnpackedKind::Lifetime(a) => a.lift_to_tcx(tcx).map(|a| a.into()), + UnpackedKind::Type(a) => a.lift_to_tcx(tcx).map(|a| a.into()), + } + } +} + impl<'tcx> TypeFoldable<'tcx> for Kind<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { match self.unpack() { From 10ae216b7516af352fbb477875887b085f731aad Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 9 Feb 2018 11:24:18 -0500 Subject: [PATCH 04/27] fix typo in comment --- src/librustc/infer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index b94f1819c7ddb..d5afd6e9d3ea6 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1246,7 +1246,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // it can be resolved to an int/float variable, which // can then be recursively resolved, hence the // recursion. Note though that we prevent type - // variables from unifying to other type variables + // variables from unifyxing to other type variables // directly (though they may be embedded // structurally), and we prevent cycles in any case, // so this recursion should always be of very limited From d0aff859d55766e132f3bbdd67e3d38a1d95e80a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 9 Feb 2018 10:43:15 -0500 Subject: [PATCH 05/27] make regions "traceable" so you can do `infcx.at(..).eq(r1, r2)` --- src/librustc/infer/at.rs | 14 +++++++++++ src/librustc/infer/error_reporting/mod.rs | 1 + src/librustc/infer/mod.rs | 30 ++++++----------------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/librustc/infer/at.rs b/src/librustc/infer/at.rs index 3fd7ee276729f..d9fbf4aa51482 100644 --- a/src/librustc/infer/at.rs +++ b/src/librustc/infer/at.rs @@ -281,6 +281,20 @@ impl<'tcx> ToTrace<'tcx> for Ty<'tcx> { } } +impl<'tcx> ToTrace<'tcx> for ty::Region<'tcx> { + fn to_trace(cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self) + -> TypeTrace<'tcx> + { + TypeTrace { + cause: cause.clone(), + values: Regions(ExpectedFound::new(a_is_expected, a, b)) + } + } +} + impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> { fn to_trace(cause: &ObligationCause<'tcx>, a_is_expected: bool, diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 52caf46878b15..07551c4ebd319 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -975,6 +975,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> { match *values { infer::Types(ref exp_found) => self.expected_found_str_ty(exp_found), + infer::Regions(ref exp_found) => self.expected_found_str(exp_found), infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found), infer::PolyTraitRefs(ref exp_found) => self.expected_found_str(exp_found), } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index d5afd6e9d3ea6..d952e430f6195 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -26,7 +26,7 @@ use ty::subst::Substs; use ty::{TyVid, IntVid, FloatVid}; use ty::{self, Ty, TyCtxt}; use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; -use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use ty::fold::TypeFoldable; use ty::relate::RelateResult; use traits::{self, ObligationCause, PredicateObligations, Reveal}; use rustc_data_structures::unify as ut; @@ -191,6 +191,7 @@ pub type SkolemizationMap<'tcx> = BTreeMap>; #[derive(Clone, Debug)] pub enum ValuePairs<'tcx> { Types(ExpectedFound>), + Regions(ExpectedFound>), TraitRefs(ExpectedFound>), PolyTraitRefs(ExpectedFound>), } @@ -1623,27 +1624,12 @@ impl RegionVariableOrigin { } } -impl<'tcx> TypeFoldable<'tcx> for ValuePairs<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - match *self { - ValuePairs::Types(ref ef) => { - ValuePairs::Types(ef.fold_with(folder)) - } - ValuePairs::TraitRefs(ref ef) => { - ValuePairs::TraitRefs(ef.fold_with(folder)) - } - ValuePairs::PolyTraitRefs(ref ef) => { - ValuePairs::PolyTraitRefs(ef.fold_with(folder)) - } - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - ValuePairs::Types(ref ef) => ef.visit_with(visitor), - ValuePairs::TraitRefs(ref ef) => ef.visit_with(visitor), - ValuePairs::PolyTraitRefs(ref ef) => ef.visit_with(visitor), - } +EnumTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for ValuePairs<'tcx> { + (ValuePairs::Types)(a), + (ValuePairs::Regions)(a), + (ValuePairs::TraitRefs)(a), + (ValuePairs::PolyTraitRefs)(a), } } From 0037cca2f79c63143e3849ec84eebdaf4b451712 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 9 Feb 2018 10:43:36 -0500 Subject: [PATCH 06/27] comment the purpose of `TransNormalize` --- src/librustc/infer/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index d952e430f6195..713cf56085151 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -487,6 +487,10 @@ pub struct CombinedSnapshot<'a, 'tcx:'a> { /// Helper trait for shortening the lifetimes inside a /// value for post-type-checking normalization. +/// +/// This trait offers a normalization method where the inputs and +/// outputs both have the `'gcx` lifetime; the implementations +/// internally create inference contexts and/or lift as needed. pub trait TransNormalize<'gcx>: TypeFoldable<'gcx> { fn trans_normalize<'a, 'tcx>(&self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, From 652b3b71f30eb6657bc63d351570dc7ff3b78eb0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Feb 2018 04:16:25 -0500 Subject: [PATCH 07/27] random reformatting --- .../borrow_check/nll/type_check/liveness.rs | 10 +++----- .../borrow_check/nll/type_check/mod.rs | 23 +++++++++++-------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs index a50b99937475e..5b3f439e0ebb9 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -105,8 +105,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo for live_local in live_locals { debug!( "add_liveness_constraints: location={:?} live_local={:?}", - location, - live_local + location, live_local ); self.flow_inits.each_state_bit(|mpi_init| { @@ -157,8 +156,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo { debug!( "push_type_live_constraint(live_ty={:?}, location={:?})", - value, - location + value, location ); self.tcx.for_each_free_region(&value, |live_region| { @@ -182,9 +180,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo ) { debug!( "add_drop_live_constraint(dropped_local={:?}, dropped_ty={:?}, location={:?})", - dropped_local, - dropped_ty, - location + dropped_local, dropped_ty, location ); // If we end visiting the same type twice (usually due to a cycle involving diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 36e173dd5d640..c474cabdb3c5b 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -243,8 +243,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { fn sanitize_constant(&mut self, constant: &Constant<'tcx>, location: Location) { debug!( "sanitize_constant(constant={:?}, location={:?})", - constant, - location + constant, location ); let expected_ty = match constant.literal { @@ -678,8 +677,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let data = self.infcx.take_and_reset_region_constraints(); if !data.is_empty() { - debug!("fully_perform_op: constraints generated at {:?} are {:#?}", - locations, data); + debug!( + "fully_perform_op: constraints generated at {:?} are {:#?}", + locations, data + ); self.constraints .outlives_sets .push(OutlivesSet { locations, data }); @@ -1137,12 +1138,16 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } TerminatorKind::FalseUnwind { real_target, - unwind + unwind, } => { self.assert_iscleanup(mir, block_data, real_target, is_cleanup); if let Some(unwind) = unwind { if is_cleanup { - span_mirbug!(self, block_data, "cleanup in cleanup block via false unwind"); + span_mirbug!( + self, + block_data, + "cleanup in cleanup block via false unwind" + ); } self.assert_iscleanup(mir, block_data, unwind, true); } @@ -1435,8 +1440,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { debug!( "prove_aggregate_predicates(aggregate_kind={:?}, location={:?})", - aggregate_kind, - location + aggregate_kind, location ); let instantiated_predicates = match aggregate_kind { @@ -1502,8 +1506,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn prove_predicates(&mut self, predicates: &[ty::Predicate<'tcx>], location: Location) { debug!( "prove_predicates(predicates={:?}, location={:?})", - predicates, - location + predicates, location ); self.fully_perform_op(location.at_self(), |this| { let cause = this.misc(this.last_span); From 1d377d10a1c86152a49b46f446557c150fda655d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 20 Feb 2018 10:36:29 -0500 Subject: [PATCH 08/27] add handy helper for Cell, used for perf stats --- src/librustc/util/common.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index e1ae41f24721a..96b77d351e24c 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -373,3 +373,13 @@ fn test_to_readable_str() { assert_eq!("1_000_000", to_readable_str(1_000_000)); assert_eq!("1_234_567", to_readable_str(1_234_567)); } + +pub trait CellUsizeExt { + fn increment(&self); +} + +impl CellUsizeExt for Cell { + fn increment(&self) { + self.set(self.get() + 1); + } +} From 6d0f9319dfd4a115f64fd5e00be3749da5eda8bc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 10 Feb 2018 13:18:02 -0500 Subject: [PATCH 09/27] refactor `ParamEnv::empty(Reveal)` into two distinct methods - `ParamEnv::empty()` -- does not reveal all, good for typeck - `ParamEnv::reveal_all()` -- does, good for trans - `param_env.with_reveal_all()` -- converts an existing parameter environment --- src/librustc/infer/mod.rs | 6 ++--- src/librustc/lint/context.rs | 3 +-- src/librustc/traits/coherence.rs | 4 +-- src/librustc/traits/error_reporting.rs | 3 +-- src/librustc/traits/mod.rs | 2 +- src/librustc/traits/select.rs | 2 +- src/librustc/traits/specialize/mod.rs | 4 +-- src/librustc/traits/structural_impls.rs | 2 -- src/librustc/ty/instance.rs | 2 +- src/librustc/ty/layout.rs | 8 +++--- src/librustc/ty/mod.rs | 4 +-- src/librustc/ty/relate.rs | 3 +-- src/librustc/ty/util.rs | 26 +++++++++++++++++--- src/librustc_driver/test.rs | 4 +-- src/librustc_lint/builtin.rs | 4 +-- src/librustc_mir/interpret/eval_context.rs | 3 +-- src/librustc_mir/monomorphize/collector.rs | 17 ++++++------- src/librustc_mir/monomorphize/mod.rs | 4 +-- src/librustc_mir/transform/qualify_consts.rs | 4 +-- src/librustc_passes/rvalue_promotion.rs | 3 +-- src/librustc_trans/callee.rs | 3 +-- src/librustc_trans/common.rs | 7 +++--- src/librustc_trans/context.rs | 5 ++-- src/librustc_trans/mir/block.rs | 3 +-- src/librustc_trans/mir/constant.rs | 9 +++---- src/librustc_typeck/check/compare_method.rs | 4 +-- src/librustc_typeck/check/dropck.rs | 4 +-- src/librustc_typeck/check/mod.rs | 2 +- src/librustc_typeck/lib.rs | 4 +-- 29 files changed, 77 insertions(+), 72 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 713cf56085151..5f0c2d1e76bcc 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -28,7 +28,7 @@ use ty::{self, Ty, TyCtxt}; use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; use ty::fold::TypeFoldable; use ty::relate::RelateResult; -use traits::{self, ObligationCause, PredicateObligations, Reveal}; +use traits::{self, ObligationCause, PredicateObligations}; use rustc_data_structures::unify as ut; use std::cell::{Cell, RefCell, Ref, RefMut}; use std::collections::BTreeMap; @@ -563,7 +563,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { { debug!("fully_normalize_associated_types_in(t={:?})", value); - let param_env = ty::ParamEnv::empty(Reveal::All); + let param_env = ty::ParamEnv::reveal_all(); let value = self.erase_regions(value); if !value.has_projections() { @@ -593,7 +593,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { } self.infer_ctxt().enter(|infcx| { - value.trans_normalize(&infcx, env.reveal_all()) + value.trans_normalize(&infcx, env.with_reveal_all()) }) } } diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index a1381817223f1..4fa6594df169c 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -34,7 +34,6 @@ use lint::levels::{LintLevelSets, LintLevelsBuilder}; use middle::privacy::AccessLevels; use rustc_serialize::{Decoder, Decodable, Encoder, Encodable}; use session::{config, early_error, Session}; -use traits::Reveal; use ty::{self, TyCtxt, Ty}; use ty::layout::{LayoutError, LayoutOf, TyLayout}; use util::nodemap::FxHashMap; @@ -1055,7 +1054,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let mut cx = LateContext { tcx, tables: &ty::TypeckTables::empty(None), - param_env: ty::ParamEnv::empty(Reveal::UserFacing), + param_env: ty::ParamEnv::empty(), access_levels, lint_sess: LintSession::new(&tcx.sess.lint_store), last_ast_node_with_lint_attrs: ast::CRATE_NODE_ID, diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs index 183b1a5470e5a..06926b1648df5 100644 --- a/src/librustc/traits/coherence.rs +++ b/src/librustc/traits/coherence.rs @@ -16,7 +16,7 @@ use hir::def_id::{DefId, LOCAL_CRATE}; use syntax_pos::DUMMY_SP; -use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause, Reveal}; +use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause}; use traits::IntercrateMode; use traits::select::IntercrateAmbiguityCause; use ty::{self, Ty, TyCtxt}; @@ -125,7 +125,7 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>, // types into scope; instead, we replace the generic types with // fresh type variables, and hence we do our evaluations in an // empty environment. - let param_env = ty::ParamEnv::empty(Reveal::UserFacing); + let param_env = ty::ParamEnv::empty(); let a_impl_header = with_fresh_ty_vars(selcx, param_env, a_def_id); let b_impl_header = with_fresh_ty_vars(selcx, param_env, b_def_id); diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index cd2d0d7e2a043..b8455b97fa41f 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -21,7 +21,6 @@ use super::{ TraitNotObjectSafe, ConstEvalFailure, PredicateObligation, - Reveal, SelectionContext, SelectionError, ObjectSafetyViolation, @@ -140,7 +139,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // FIXME: I'm just not taking associated types at all here. // Eventually I'll need to implement param-env-aware // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. - let param_env = ty::ParamEnv::empty(Reveal::UserFacing); + let param_env = ty::ParamEnv::empty(); if let Ok(_) = self.can_sub(param_env, error, implication) { debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication); return true diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index e0e85600b9036..4450c60b68d3b 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -695,7 +695,7 @@ fn normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, predicates); let result = tcx.infer_ctxt().enter(|infcx| { - let param_env = ty::ParamEnv::empty(Reveal::All); + let param_env = ty::ParamEnv::reveal_all(); let mut selcx = SelectionContext::new(&infcx); let mut fulfill_cx = FulfillmentContext::new(); let cause = ObligationCause::dummy(); diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 91d86394b0192..728702ef61fd1 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -777,7 +777,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // value in order to work, so we can clear out the param env and get better // caching. (If the current param env is inconsistent, we don't care what happens). debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation); - obligation.param_env = ty::ParamEnv::empty(obligation.param_env.reveal); + obligation.param_env = obligation.param_env.without_caller_bounds(); } let stack = self.push_stack(previous_stack, &obligation); diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index d11565618a687..5ea089abb8e86 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -26,7 +26,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use hir::def_id::DefId; use infer::{InferCtxt, InferOk}; use ty::subst::{Subst, Substs}; -use traits::{self, Reveal, ObligationCause}; +use traits::{self, ObligationCause}; use traits::select::IntercrateAmbiguityCause; use ty::{self, TyCtxt, TypeFoldable}; use syntax_pos::DUMMY_SP; @@ -132,7 +132,7 @@ pub fn find_associated_item<'a, 'tcx>( match ancestors.defs(tcx, item.name, item.kind, trait_def_id).next() { Some(node_item) => { let substs = tcx.infer_ctxt().enter(|infcx| { - let param_env = ty::ParamEnv::empty(Reveal::All); + let param_env = ty::ParamEnv::reveal_all(); 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/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 99adc1fc873de..a2d98a456f49a 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -425,5 +425,3 @@ BraceStructTypeFoldableImpl! { obligations } where T: TypeFoldable<'tcx> } - - diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 614158bafa638..ebb058c5d2929 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -199,7 +199,7 @@ impl<'a, 'b, 'tcx> Instance<'tcx> { _ => { if Some(def_id) == tcx.lang_items().drop_in_place_fn() { let ty = substs.type_at(0); - if ty.needs_drop(tcx, ty::ParamEnv::empty(traits::Reveal::All)) { + if ty.needs_drop(tcx, ty::ParamEnv::reveal_all()) { debug!(" => nontrivial drop glue"); ty::InstanceDef::DropGlue(def_id, Some(ty)) } else { diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 1b919ad68d047..9ce232ba17303 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -2058,7 +2058,7 @@ impl<'a, 'tcx> LayoutOf> for LayoutCx<'tcx, TyCtxt<'a, 'tcx, '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.reveal_all(); + let param_env = self.param_env.with_reveal_all(); let ty = self.tcx.normalize_associated_type_in_env(&ty, param_env); let details = self.tcx.layout_raw(param_env.and(ty))?; let layout = TyLayout { @@ -2084,9 +2084,9 @@ impl<'a, 'tcx> LayoutOf> for LayoutCx<'tcx, ty::maps::TyCtxtAt<'a, '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.reveal_all(); - let ty = self.tcx.normalize_associated_type_in_env(&ty, param_env.reveal_all()); - let details = self.tcx.layout_raw(param_env.reveal_all().and(ty))?; + let param_env = self.param_env.with_reveal_all(); + let ty = self.tcx.normalize_associated_type_in_env(&ty, param_env); + let details = self.tcx.layout_raw(param_env.and(ty))?; let layout = TyLayout { ty, details diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 63494438f7d82..40d7aa9556df9 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1420,7 +1420,7 @@ impl<'tcx> ParamEnv<'tcx> { } } else { ParamEnvAnd { - param_env: ParamEnv::empty(self.reveal), + param_env: self.without_caller_bounds(), value, } } @@ -1829,7 +1829,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { tcx: TyCtxt<'a, 'gcx, 'tcx>, expr_did: DefId, ) -> Option> { - let param_env = ParamEnv::empty(traits::Reveal::UserFacing); + let param_env = ParamEnv::empty(); let repr_type = self.repr.discr_type(); let bit_size = layout::Integer::from_attr(tcx, repr_type).size().bits(); let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 7f48f8c431fac..72e7d16b64dce 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -15,7 +15,6 @@ use hir::def_id::DefId; use middle::const_val::ConstVal; -use traits::Reveal; use ty::subst::{Kind, UnpackedKind, Substs}; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::error::{ExpectedFound, TypeError}; @@ -473,7 +472,7 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, ConstVal::Value(Value::ByVal(prim)) => Ok(prim.to_u64().unwrap()), ConstVal::Unevaluated(def_id, substs) => { // FIXME(eddyb) get the right param_env. - let param_env = ty::ParamEnv::empty(Reveal::UserFacing); + let param_env = ty::ParamEnv::empty(); match tcx.lift_to_global(&substs) { Some(substs) => { let instance = ty::Instance::resolve( diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index cbd9a1b8d4f93..ca6817d992b6e 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -183,9 +183,22 @@ pub enum Representability { impl<'tcx> ty::ParamEnv<'tcx> { /// Construct a trait environment suitable for contexts where - /// there are no where clauses in scope. - pub fn empty(reveal: Reveal) -> Self { - Self::new(ty::Slice::empty(), reveal, ty::UniverseIndex::ROOT) + /// there are no where clauses in scope. Hidden types (like `impl + /// Trait`) are left hidden, so this is suitable for ordinary + /// type-checking. + pub fn empty() -> Self { + Self::new(ty::Slice::empty(), Reveal::UserFacing, ty::UniverseIndex::ROOT) + } + + /// Construct a trait environment with no where clauses in scope + /// where the values of all `impl Trait` and other hidden types + /// are revealed. This is suitable for monomorphized, post-typeck + /// environments like trans or doing optimizations. + /// + /// NB. If you want to have predicates in scope, use `ParamEnv::new`, + /// or invoke `param_env.with_reveal_all()`. + pub fn reveal_all() -> Self { + Self::new(ty::Slice::empty(), Reveal::All, ty::UniverseIndex::ROOT) } /// Construct a trait environment with the given set of predicates. @@ -202,10 +215,15 @@ impl<'tcx> ty::ParamEnv<'tcx> { /// the desired behavior during trans and certain other special /// contexts; normally though we want to use `Reveal::UserFacing`, /// which is the default. - pub fn reveal_all(self) -> Self { + pub fn with_reveal_all(self) -> Self { ty::ParamEnv { reveal: Reveal::All, ..self } } + /// Returns this same environment but with no caller bounds. + pub fn without_caller_bounds(self) -> Self { + ty::ParamEnv { caller_bounds: ty::Slice::empty(), ..self } + } + pub fn can_type_implement_copy<'a>(self, tcx: TyCtxt<'a, 'tcx, 'tcx>, self_type: Ty<'tcx>, span: Span) diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index becdbb54e5beb..06610609ebdbd 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -18,7 +18,7 @@ use rustc_lint; use rustc_resolve::MakeGlobMap; use rustc::middle::region; use rustc::ty::subst::Subst; -use rustc::traits::{ObligationCause, Reveal}; +use rustc::traits::ObligationCause; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::maps::OnDiskCache; use rustc::infer::{self, InferOk, InferResult}; @@ -153,7 +153,7 @@ fn test_env(source_string: &str, |tcx| { tcx.infer_ctxt().enter(|infcx| { let mut region_scope_tree = region::ScopeTree::default(); - let param_env = ty::ParamEnv::empty(Reveal::UserFacing); + let param_env = ty::ParamEnv::empty(); body(Env { infcx: &infcx, region_scope_tree: &mut region_scope_tree, diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index c6698cbd00689..031033f7208e1 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -33,7 +33,7 @@ use rustc::hir::def_id::DefId; use rustc::cfg; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; -use rustc::traits::{self, Reveal}; +use rustc::traits; use rustc::hir::map as hir_map; use util::nodemap::NodeSet; use lint::{LateContext, LintContext, LintArray}; @@ -525,7 +525,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations { if def.has_dtor(cx.tcx) { return; } - let param_env = ty::ParamEnv::empty(Reveal::UserFacing); + let param_env = ty::ParamEnv::empty(); if !ty.moves_by_default(cx.tcx, param_env, item.span) { return; } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 13090ca53302b..d766ea3e2bd0d 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -949,8 +949,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M pub fn const_eval(&self, gid: GlobalId<'tcx>) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> { let param_env = if self.tcx.is_static(gid.instance.def_id()).is_some() { - use rustc::traits; - ty::ParamEnv::empty(traits::Reveal::All) + ty::ParamEnv::reveal_all() } else { self.param_env }; diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index f6b47efca31cd..561500a3f3c5f 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -196,7 +196,6 @@ use rustc::hir::def_id::DefId; use rustc::middle::const_val::ConstVal; use rustc::mir::interpret::{Value, PrimVal, AllocId, Pointer}; use rustc::middle::lang_items::{ExchangeMallocFnLangItem, StartFnLangItem}; -use rustc::traits; use rustc::ty::subst::{Substs, Kind}; use rustc::ty::{self, TypeFoldable, Ty, TyCtxt}; use rustc::ty::adjustment::CustomCoerceUnsized; @@ -383,7 +382,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance, promoted: None, }; - let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let param_env = ty::ParamEnv::reveal_all(); match tcx.const_eval(param_env.and(cid)) { Ok(val) => collect_const(tcx, val, instance.substs, &mut neighbors), @@ -654,7 +653,7 @@ fn visit_fn_use<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, { if let ty::TyFnDef(def_id, substs) = ty.sty { let instance = ty::Instance::resolve(tcx, - ty::ParamEnv::empty(traits::Reveal::All), + ty::ParamEnv::reveal_all(), def_id, substs).unwrap(); visit_instance_use(tcx, instance, is_direct_call, output); @@ -776,7 +775,7 @@ fn find_vtable_types_for_unsizing<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| { let type_has_metadata = |ty: Ty<'tcx>| -> bool { use syntax_pos::DUMMY_SP; - if ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::empty(traits::Reveal::All)) { + if ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) { return false; } let tail = tcx.struct_tail(ty); @@ -859,7 +858,7 @@ fn create_mono_items_for_vtable_methods<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let methods = methods.iter().cloned().filter_map(|method| method) .map(|(def_id, substs)| ty::Instance::resolve( tcx, - ty::ParamEnv::empty(traits::Reveal::All), + ty::ParamEnv::reveal_all(), def_id, substs).unwrap()) .filter(|&instance| should_monomorphize_locally(tcx, &instance)) @@ -1013,7 +1012,7 @@ impl<'b, 'a, 'v> RootCollector<'b, 'a, 'v> { let start_instance = Instance::resolve( self.tcx, - ty::ParamEnv::empty(traits::Reveal::All), + ty::ParamEnv::reveal_all(), start_def_id, self.tcx.mk_substs(iter::once(Kind::from(main_ret_ty))) ).unwrap(); @@ -1062,7 +1061,7 @@ fn create_mono_items_for_default_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } let instance = ty::Instance::resolve(tcx, - ty::ParamEnv::empty(traits::Reveal::All), + ty::ParamEnv::reveal_all(), method.def_id, callee_substs).unwrap(); @@ -1120,7 +1119,7 @@ fn collect_neighbours<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, output, param_substs: instance.substs, }.visit_mir(&mir); - let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let param_env = ty::ParamEnv::reveal_all(); for (i, promoted) in mir.promoted.iter().enumerate() { use rustc_data_structures::indexed_vec::Idx; let cid = GlobalId { @@ -1155,7 +1154,7 @@ fn collect_const<'a, 'tcx>( let val = match constant.val { ConstVal::Unevaluated(def_id, substs) => { - let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let param_env = ty::ParamEnv::reveal_all(); let substs = tcx.trans_apply_param_substs(param_substs, &substs); let instance = ty::Instance::resolve(tcx, diff --git a/src/librustc_mir/monomorphize/mod.rs b/src/librustc_mir/monomorphize/mod.rs index 2ca6c76a8009a..4c9789782a6e1 100644 --- a/src/librustc_mir/monomorphize/mod.rs +++ b/src/librustc_mir/monomorphize/mod.rs @@ -154,7 +154,7 @@ pub fn resolve_drop_in_place<'a, 'tcx>( { let def_id = tcx.require_lang_item(DropInPlaceFnLangItem); let substs = tcx.intern_substs(&[ty.into()]); - Instance::resolve(tcx, ty::ParamEnv::empty(traits::Reveal::All), def_id, substs).unwrap() + Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap() } pub fn custom_coerce_unsize_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -168,7 +168,7 @@ pub fn custom_coerce_unsize_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, substs: tcx.mk_substs_trait(source_ty, &[target_ty]) }); - match tcx.trans_fulfill_obligation( (ty::ParamEnv::empty(traits::Reveal::All), trait_ref)) { + match tcx.trans_fulfill_obligation( (ty::ParamEnv::reveal_all(), trait_ref)) { traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap() } diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 88618122e4f11..94446a98e63fe 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -21,7 +21,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::middle::const_val::ConstVal; -use rustc::traits::{self, Reveal}; +use rustc::traits; use rustc::ty::{self, TyCtxt, Ty, TypeFoldable}; use rustc::ty::cast::CastTy; use rustc::ty::maps::Providers; @@ -1237,7 +1237,7 @@ impl MirPass for QualifyAndPromoteConstants { } let ty = mir.return_ty(); tcx.infer_ctxt().enter(|infcx| { - let param_env = ty::ParamEnv::empty(Reveal::UserFacing); + let param_env = ty::ParamEnv::empty(); let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic); let mut fulfillment_cx = traits::FulfillmentContext::new(); fulfillment_cx.register_bound(&infcx, diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs index 16278c37a0cc0..356ad9ec11bb7 100644 --- a/src/librustc_passes/rvalue_promotion.rs +++ b/src/librustc_passes/rvalue_promotion.rs @@ -34,7 +34,6 @@ use rustc::middle::mem_categorization::Categorization; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::ty::subst::Substs; -use rustc::traits::Reveal; use rustc::util::nodemap::{ItemLocalSet, NodeSet}; use rustc::hir; use rustc_data_structures::sync::Lrc; @@ -87,7 +86,7 @@ fn rvalue_promotable_map<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, in_static: false, promotable: false, mut_rvalue_borrows: NodeSet(), - param_env: ty::ParamEnv::empty(Reveal::UserFacing), + param_env: ty::ParamEnv::empty(), identity_substs: Substs::empty(), result: ItemLocalSet(), }; diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 54cc561e80415..1dcf349e23bd8 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -25,7 +25,6 @@ use type_of::LayoutLlvmExt; use rustc::hir::def_id::DefId; use rustc::ty::{self, TypeFoldable}; use rustc::ty::layout::LayoutOf; -use rustc::traits; use rustc::ty::subst::Substs; use rustc_back::PanicStrategy; @@ -185,7 +184,7 @@ pub fn resolve_and_get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, cx, ty::Instance::resolve( cx.tcx, - ty::ParamEnv::empty(traits::Reveal::All), + ty::ParamEnv::reveal_all(), def_id, substs ).unwrap() diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 7c4e2340d5bdc..c71c0cc0ebf2c 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -25,7 +25,6 @@ use declare; use type_::Type; use type_of::LayoutLlvmExt; use value::Value; -use rustc::traits; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::layout::{HasDataLayout, LayoutOf}; use rustc::hir; @@ -40,15 +39,15 @@ use syntax_pos::{Span, DUMMY_SP}; pub use context::CodegenCx; pub fn type_needs_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.needs_drop(tcx, ty::ParamEnv::empty(traits::Reveal::All)) + ty.needs_drop(tcx, ty::ParamEnv::reveal_all()) } pub fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::empty(traits::Reveal::All)) + ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) } pub fn type_is_freeze<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_freeze(tcx, ty::ParamEnv::empty(traits::Reveal::All), DUMMY_SP) + ty.is_freeze(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP) } /* diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index dc5e788959448..1f2c3cc883c68 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -14,7 +14,6 @@ use llvm::{ContextRef, ModuleRef, ValueRef}; use rustc::dep_graph::DepGraphSafe; use rustc::hir; use rustc::hir::def_id::DefId; -use rustc::traits; use debuginfo; use callee; use base; @@ -435,7 +434,7 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> { pub fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool { use syntax_pos::DUMMY_SP; - if ty.is_sized(self.tcx.at(DUMMY_SP), ty::ParamEnv::empty(traits::Reveal::All)) { + if ty.is_sized(self.tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) { return false; } @@ -464,7 +463,7 @@ impl<'a, 'tcx> LayoutOf> for &'a CodegenCx<'a, 'tcx> { type TyLayout = TyLayout<'tcx>; fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { - self.tcx.layout_of(ty::ParamEnv::empty(traits::Reveal::All).and(ty)) + self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) .unwrap_or_else(|e| match e { LayoutError::SizeOverflow(_) => self.sess().fatal(&e.to_string()), _ => bug!("failed to get layout for `{}`: {}", ty, e) diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index efb5338f680af..606c1396c1daf 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -12,7 +12,6 @@ use llvm::{self, ValueRef, BasicBlockRef}; use rustc::middle::lang_items; use rustc::ty::{self, TypeFoldable}; use rustc::ty::layout::{self, LayoutOf}; -use rustc::traits; use rustc::mir; use abi::{Abi, FnType, ArgType, PassMode}; use base; @@ -419,7 +418,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { let (instance, mut llfn) = match callee.layout.ty.sty { ty::TyFnDef(def_id, substs) => { (Some(ty::Instance::resolve(bx.cx.tcx, - ty::ParamEnv::empty(traits::Reveal::All), + ty::ParamEnv::reveal_all(), def_id, substs).unwrap()), None) diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 6aa8b7e5449fd..977c7c983d6f2 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -12,7 +12,6 @@ use llvm::{self, ValueRef}; use rustc::middle::const_val::{ConstVal, ConstEvalErr}; use rustc_mir::interpret::{read_target_uint, const_val_field}; use rustc::hir::def_id::DefId; -use rustc::traits; use rustc::mir; use rustc_data_structures::indexed_vec::Idx; use rustc::mir::interpret::{Allocation, GlobalId, MemoryPointer, PrimVal, Value as MiriValue}; @@ -126,7 +125,7 @@ pub fn trans_static_initializer<'a, 'tcx>( instance, promoted: None }; - let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let param_env = ty::ParamEnv::reveal_all(); cx.tcx.const_eval(param_env.and(cid))?; let alloc_id = cx @@ -152,7 +151,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { match constant.val { ConstVal::Unevaluated(def_id, ref substs) => { let tcx = bx.tcx(); - let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let param_env = ty::ParamEnv::reveal_all(); let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap(); let cid = GlobalId { instance, @@ -172,7 +171,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { ) -> Result> { match constant.literal { mir::Literal::Promoted { index } => { - let param_env = ty::ParamEnv::empty(traits::Reveal::All); + let param_env = ty::ParamEnv::reveal_all(); let cid = mir::interpret::GlobalId { instance: self.instance, promoted: Some(index), @@ -201,7 +200,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { let values: Result, _> = (0..fields).map(|field| { let field = const_val_field( bx.tcx(), - ty::ParamEnv::empty(traits::Reveal::All), + ty::ParamEnv::reveal_all(), self.instance, None, mir::Field::new(field as usize), diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index a3a1f2a930778..b6459b624104f 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -501,7 +501,7 @@ fn compare_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty::TraitContainer(_) => tcx.mk_self_type() }; let self_arg_ty = *tcx.fn_sig(method.def_id).input(0).skip_binder(); - let param_env = ty::ParamEnv::empty(Reveal::All); + let param_env = ty::ParamEnv::reveal_all(); tcx.infer_ctxt().enter(|infcx| { let self_arg_ty = tcx.liberate_late_bound_regions( @@ -759,7 +759,7 @@ pub fn compare_const_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref); tcx.infer_ctxt().enter(|infcx| { - let param_env = ty::ParamEnv::empty(Reveal::UserFacing); + let param_env = ty::ParamEnv::empty(); let inh = Inherited::new(infcx, impl_c.def_id); let infcx = &inh.infcx; diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 165cfe6604e8b..f33536227a0ba 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -16,7 +16,7 @@ use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; use rustc::ty::subst::{Subst, Substs, UnpackedKind}; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::traits::{self, Reveal, ObligationCause}; +use rustc::traits::{self, ObligationCause}; use util::common::ErrorReported; use util::nodemap::FxHashSet; @@ -126,7 +126,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>( // it did the wrong thing, so I chose to preserve existing // behavior, since it ought to be simply more // conservative. -nmatsakis - let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty(Reveal::UserFacing)); + let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); infcx.resolve_regions_and_report_errors(drop_impl_did, ®ion_scope_tree, &outlives_env); Ok(()) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 1ea1ff1fae24d..48591998a1fb8 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3997,7 +3997,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } hir::ExprRepeat(ref element, count) => { let count_def_id = tcx.hir.body_owner_def_id(count); - let param_env = ty::ParamEnv::empty(traits::Reveal::UserFacing); + let param_env = ty::ParamEnv::empty(); let substs = Substs::identity_for_item(tcx.global_tcx(), count_def_id); let instance = ty::Instance::resolve( tcx.global_tcx(), diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 49a23f14338b4..ea90c35cb8f2e 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -110,7 +110,7 @@ use rustc::infer::InferOk; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::Providers; -use rustc::traits::{FulfillmentContext, ObligationCause, ObligationCauseCode, Reveal}; +use rustc::traits::{FulfillmentContext, ObligationCause, ObligationCauseCode}; use session::{CompileIncomplete, config}; use util::common::time; @@ -158,7 +158,7 @@ fn require_same_types<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, actual: Ty<'tcx>) -> bool { tcx.infer_ctxt().enter(|ref infcx| { - let param_env = ty::ParamEnv::empty(Reveal::UserFacing); + let param_env = ty::ParamEnv::empty(); let mut fulfill_cx = FulfillmentContext::new(); match infcx.at(&cause, param_env).eq(expected, actual) { Ok(InferOk { obligations, .. }) => { From 64d4ed300b52834954179284a0d0a6eb5084d39e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 14 Feb 2018 05:08:02 -0500 Subject: [PATCH 10/27] move ParamEnv methods from `ty/util` to `ty/mod` --- src/librustc/ty/mod.rs | 44 ++++++++++++++++++++++++++++++++++++++++- src/librustc/ty/util.rs | 44 +---------------------------------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 40d7aa9556df9..f81a2b9750aed 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -29,7 +29,7 @@ use mir::Mir; use mir::interpret::{GlobalId, Value, PrimVal}; use mir::GeneratorLayout; use session::CrateDisambiguator; -use traits; +use traits::{self, Reveal}; use ty; use ty::subst::{Subst, Substs}; use ty::util::{IntTypeExt, Discr}; @@ -1396,6 +1396,48 @@ pub struct ParamEnv<'tcx> { } impl<'tcx> ParamEnv<'tcx> { + /// Construct a trait environment suitable for contexts where + /// there are no where clauses in scope. Hidden types (like `impl + /// Trait`) are left hidden, so this is suitable for ordinary + /// type-checking. + pub fn empty() -> Self { + Self::new(ty::Slice::empty(), Reveal::UserFacing, ty::UniverseIndex::ROOT) + } + + /// Construct a trait environment with no where clauses in scope + /// where the values of all `impl Trait` and other hidden types + /// are revealed. This is suitable for monomorphized, post-typeck + /// environments like trans or doing optimizations. + /// + /// NB. If you want to have predicates in scope, use `ParamEnv::new`, + /// or invoke `param_env.with_reveal_all()`. + pub fn reveal_all() -> Self { + Self::new(ty::Slice::empty(), Reveal::All, ty::UniverseIndex::ROOT) + } + + /// Construct a trait environment with the given set of predicates. + pub fn new(caller_bounds: &'tcx ty::Slice>, + reveal: Reveal, + universe: ty::UniverseIndex) + -> Self { + ty::ParamEnv { caller_bounds, reveal, universe } + } + + /// Returns a new parameter environment with the same clauses, but + /// which "reveals" the true results of projections in all cases + /// (even for associated types that are specializable). This is + /// the desired behavior during trans 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 } + } + + /// Returns this same environment but with no caller bounds. + pub fn without_caller_bounds(self) -> Self { + ty::ParamEnv { caller_bounds: ty::Slice::empty(), ..self } + } + /// Creates a suitable environment in which to perform trait /// queries on the given value. This will either be `self` *or* /// the empty environment, depending on whether `value` references diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index ca6817d992b6e..a7ee8579fb989 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -16,7 +16,7 @@ use hir::map::{DefPathData, Node}; use hir; use ich::NodeIdHashingMode; use middle::const_val::ConstVal; -use traits::{self, Reveal}; +use traits; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::fold::TypeVisitor; use ty::subst::{Subst, UnpackedKind}; @@ -182,48 +182,6 @@ pub enum Representability { } impl<'tcx> ty::ParamEnv<'tcx> { - /// Construct a trait environment suitable for contexts where - /// there are no where clauses in scope. Hidden types (like `impl - /// Trait`) are left hidden, so this is suitable for ordinary - /// type-checking. - pub fn empty() -> Self { - Self::new(ty::Slice::empty(), Reveal::UserFacing, ty::UniverseIndex::ROOT) - } - - /// Construct a trait environment with no where clauses in scope - /// where the values of all `impl Trait` and other hidden types - /// are revealed. This is suitable for monomorphized, post-typeck - /// environments like trans or doing optimizations. - /// - /// NB. If you want to have predicates in scope, use `ParamEnv::new`, - /// or invoke `param_env.with_reveal_all()`. - pub fn reveal_all() -> Self { - Self::new(ty::Slice::empty(), Reveal::All, ty::UniverseIndex::ROOT) - } - - /// Construct a trait environment with the given set of predicates. - pub fn new(caller_bounds: &'tcx ty::Slice>, - reveal: Reveal, - universe: ty::UniverseIndex) - -> Self { - ty::ParamEnv { caller_bounds, reveal, universe } - } - - /// Returns a new parameter environment with the same clauses, but - /// which "reveals" the true results of projections in all cases - /// (even for associated types that are specializable). This is - /// the desired behavior during trans 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 } - } - - /// Returns this same environment but with no caller bounds. - pub fn without_caller_bounds(self) -> Self { - ty::ParamEnv { caller_bounds: ty::Slice::empty(), ..self } - } - pub fn can_type_implement_copy<'a>(self, tcx: TyCtxt<'a, 'tcx, 'tcx>, self_type: Ty<'tcx>, span: Span) From 80b4c45ee47833338164c6eb015f421890712863 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 20 Feb 2018 10:37:35 -0500 Subject: [PATCH 11/27] change `ParamEnv::and` to sometimes keep the environment [VIC] In general, we've been moving towards a semantics where you can have contradictory where-clauses, and we try to honor them. There are already existing run-pass tests where we take that philosophy as well (e.g., `compile-fail/issue-36839.rs`). The current behavior of `and`, where it strips the environment, breaks that code. --- src/librustc/ty/mod.rs | 49 ++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index f81a2b9750aed..e3405e0c3b360 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1439,31 +1439,38 @@ impl<'tcx> ParamEnv<'tcx> { } /// Creates a suitable environment in which to perform trait - /// queries on the given value. This will either be `self` *or* - /// the empty environment, depending on whether `value` references - /// type parameters that are in scope. (If it doesn't, then any - /// judgements should be completely independent of the context, - /// and hence we can safely use the empty environment so as to - /// enable more sharing across functions.) + /// queries on the given value. When type-checking, this is simply + /// the pair of the environment plus value. But when reveal is set to + /// All, then if `value` does not reference any type parameters, we will + /// pair it with the empty environment. This improves caching and is generally + /// invisible. /// - /// NB: This is a mildly dubious thing to do, in that a function - /// (or other environment) might have wacky where-clauses like + /// NB: We preserve the environment when type-checking because it + /// is possible for the user to have wacky where-clauses like /// `where Box: Copy`, which are clearly never - /// satisfiable. The code will at present ignore these, - /// effectively, when type-checking the body of said - /// function. This preserves existing behavior in any - /// case. --nmatsakis + /// satisfiable. We generally want to behave as if they were true, + /// although the surrounding function is never reachable. pub fn and>(self, value: T) -> ParamEnvAnd<'tcx, T> { - assert!(!value.needs_infer()); - if value.has_param_types() || value.has_self_ty() { - ParamEnvAnd { - param_env: self, - value, + match self.reveal { + Reveal::UserFacing => { + ParamEnvAnd { + param_env: self, + value, + } } - } else { - ParamEnvAnd { - param_env: self.without_caller_bounds(), - value, + + Reveal::All => { + if value.needs_infer() || value.has_param_types() || value.has_self_ty() { + ParamEnvAnd { + param_env: self, + value, + } + } else { + ParamEnvAnd { + param_env: self.without_caller_bounds(), + value, + } + } } } } From 993c1488cc4b0826c3f1076393bf50eef09ba467 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 9 Feb 2018 10:39:36 -0500 Subject: [PATCH 12/27] add `canonicalize` method to `InferCtxt` [VIC] --- src/librustc/ich/impls_ty.rs | 59 +- src/librustc/infer/canonical.rs | 928 ++++++++++++++++++ src/librustc/infer/combine.rs | 1 + src/librustc/infer/error_reporting/mod.rs | 1 + src/librustc/infer/freshen.rs | 6 +- .../infer/lexical_region_resolve/mod.rs | 2 + src/librustc/infer/mod.rs | 8 +- src/librustc/lib.rs | 2 +- src/librustc/macros.rs | 19 + src/librustc/session/mod.rs | 5 + src/librustc/traits/select.rs | 15 +- src/librustc/ty/context.rs | 33 + src/librustc/ty/error.rs | 1 + src/librustc/ty/flags.rs | 12 +- src/librustc/ty/mod.rs | 9 +- src/librustc/ty/sty.rs | 12 + src/librustc/ty/subst.rs | 1 + src/librustc/ty/util.rs | 3 + src/librustc/util/ppaux.rs | 9 + src/librustc_borrowck/borrowck/check_loans.rs | 1 + .../borrowck/gather_loans/mod.rs | 1 + src/librustc_const_eval/lib.rs | 1 + src/librustc_data_structures/indexed_vec.rs | 2 +- src/librustc_metadata/lib.rs | 2 + .../borrow_check/error_reporting.rs | 1 + src/librustc_save_analysis/lib.rs | 1 + src/librustc_typeck/variance/constraints.rs | 1 + src/librustdoc/clean/mod.rs | 1 + 28 files changed, 1121 insertions(+), 16 deletions(-) create mode 100644 src/librustc/infer/canonical.rs diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index d927a151610ef..ae67d592ba30c 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -19,6 +19,7 @@ use std::cell::RefCell; use std::hash as std_hash; use std::mem; use middle::region; +use infer; use traits; use ty; use mir; @@ -85,6 +86,9 @@ for ty::RegionKind { ty::ReEmpty => { // No variant fields to hash for these ... } + ty::ReCanonical(c) => { + c.hash_stable(hcx, hasher); + } ty::ReLateBound(db, ty::BrAnon(i)) => { db.depth.hash_stable(hcx, hasher); i.hash_stable(hcx, hasher); @@ -130,6 +134,16 @@ impl<'a> HashStable> for ty::RegionVid { } } +impl<'gcx> HashStable> for ty::CanonicalVar { + #[inline] + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + use rustc_data_structures::indexed_vec::Idx; + self.index().hash_stable(hcx, hasher); + } +} + impl<'a, 'gcx> HashStable> for ty::adjustment::AutoBorrow<'gcx> { fn hash_stable(&self, @@ -1229,11 +1243,52 @@ for traits::VtableGeneratorData<'gcx, N> where N: HashStable HashStable> +impl<'a> HashStable> for ty::UniverseIndex { fn hash_stable(&self, - hcx: &mut StableHashingContext<'gcx>, + hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.depth().hash_stable(hcx, hasher); } } + +impl_stable_hash_for!( + impl<'tcx, V> for struct infer::canonical::Canonical<'tcx, V> { + variables, value + } +); + +impl_stable_hash_for!( + impl<'tcx> for struct infer::canonical::CanonicalVarValues<'tcx> { + var_values + } +); + +impl_stable_hash_for!(struct infer::canonical::CanonicalVarInfo { + kind +}); + +impl_stable_hash_for!(enum infer::canonical::CanonicalVarKind { + Ty(k), + Region +}); + +impl_stable_hash_for!(enum infer::canonical::CanonicalTyVarKind { + General, + Int, + Float +}); + +impl_stable_hash_for!( + impl<'tcx, R> for struct infer::canonical::QueryResult<'tcx, R> { + var_values, region_constraints, certainty, value + } +); + +impl_stable_hash_for!(struct infer::canonical::QueryRegionConstraints<'tcx> { + region_outlives, ty_outlives +}); + +impl_stable_hash_for!(enum infer::canonical::Certainty { + Proven, Ambiguous +}); diff --git a/src/librustc/infer/canonical.rs b/src/librustc/infer/canonical.rs new file mode 100644 index 0000000000000..c1c7337860e37 --- /dev/null +++ b/src/librustc/infer/canonical.rs @@ -0,0 +1,928 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! **Canonicalization** is the key to constructing a query in the +//! middle of type inference. Ordinarily, it is not possible to store +//! types from type inference in query keys, because they contain +//! references to inference variables whose lifetimes are too short +//! and so forth. Canonicalizing a value T1 using `canonicalize_query` +//! produces two things: +//! +//! - a value T2 where each unbound inference variable has been +//! replaced with a **canonical variable**; +//! - a map M (of type `CanonicalVarValues`) from those canonical +//! variables back to the original. +//! +//! We can then do queries using T2. These will give back constriants +//! on the canonical variables which can be translated, using the map +//! M, into constraints in our source context. This process of +//! translating the results back is done by the +//! `instantiate_query_result` method. + +use infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TypeVariableOrigin}; +use rustc_data_structures::indexed_vec::Idx; +use std::fmt::Debug; +use syntax::codemap::Span; +use traits::{Obligation, ObligationCause, PredicateObligation}; +use ty::{self, CanonicalVar, Lift, Region, Slice, Ty, TyCtxt, TypeFlags}; +use ty::subst::{Kind, UnpackedKind}; +use ty::fold::{TypeFoldable, TypeFolder}; +use util::common::CellUsizeExt; + +use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::fx::FxHashMap; + +/// A "canonicalized" type `V` is one where all free inference +/// variables have been rewriten to "canonical vars". These are +/// numbered starting from 0 in order of first appearance. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct Canonical<'gcx, V> { + pub variables: CanonicalVarInfos<'gcx>, + pub value: V, +} + +pub type CanonicalVarInfos<'gcx> = &'gcx Slice; + +/// A set of values corresponding to the canonical variables from some +/// `Canonical`. You can give these values to +/// `canonical_value.substitute` to substitute them into the canonical +/// value at the right places. +/// +/// When you canonicalize a value `V`, you get back one of these +/// vectors with the original values that were replaced by canonical +/// variables. +/// +/// You can also use `infcx.fresh_inference_vars_for_canonical_vars` +/// to get back a `CanonicalVarValues` containing fresh inference +/// variables. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct CanonicalVarValues<'tcx> { + pub var_values: IndexVec>, +} + +/// Information about a canonical variable that is included with the +/// canonical value. This is sufficient information for code to create +/// a copy of the canonical value in some other inference context, +/// with fresh inference variables replacing the canonical values. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct CanonicalVarInfo { + pub kind: CanonicalVarKind, +} + +/// Describes the "kind" of the canonical variable. This is a "kind" +/// in the type-theory sense of the term -- i.e., a "meta" type system +/// that analyzes type-like values. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum CanonicalVarKind { + /// Some kind of type inference variable. + Ty(CanonicalTyVarKind), + + /// Region variable `'?R`. + Region, +} + +/// Rust actually has more than one category of type variables; +/// notably, the type variables we create for literals (e.g., 22 or +/// 22.) can only be instantiated with integral/float types (e.g., +/// usize or f32). In order to faithfully reproduce a type, we need to +/// know what set of types a given type variable can be unified with. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum CanonicalTyVarKind { + /// General type variable `?T` that can be unified with arbitrary types. + General, + + /// Integral type variable `?I` (that can only be unified with integral types). + Int, + + /// Floating-point type variable `?F` (that can only be unified with float types). + Float, +} + +/// After we execute a query with a canonicalized key, we get back a +/// `Canonical>`. You can use +/// `instantiate_query_result` to access the data in this result. +#[derive(Clone, Debug)] +pub struct QueryResult<'tcx, R> { + pub var_values: CanonicalVarValues<'tcx>, + pub region_constraints: QueryRegionConstraints<'tcx>, + pub certainty: Certainty, + pub value: R, +} + +/// Indicates whether or not we were able to prove the query to be +/// true. +#[derive(Copy, Clone, Debug)] +pub enum Certainty { + /// The query is known to be true, presuming that you apply the + /// given `var_values` and the region-constraints are satisfied. + Proven, + + /// The query is not known to be true, but also not known to be + /// false. The `var_values` represent *either* values that must + /// hold in order for the query to be true, or helpful tips that + /// *might* make it true. Currently rustc's trait solver cannot + /// distinguish the two (e.g., due to our preference for where + /// clauses over impls). + /// + /// After some unifiations and things have been done, it makes + /// sense to try and prove again -- of course, at that point, the + /// canonical form will be different, making this a distinct + /// query. + Ambiguous, +} + +impl Certainty { + pub fn is_proven(&self) -> bool { + match self { + Certainty::Proven => true, + Certainty::Ambiguous => false, + } + } + + pub fn is_ambiguous(&self) -> bool { + !self.is_proven() + } +} + +impl<'tcx, R> QueryResult<'tcx, R> { + pub fn is_proven(&self) -> bool { + self.certainty.is_proven() + } + + pub fn is_ambiguous(&self) -> bool { + !self.is_proven() + } +} + +impl<'tcx, R> Canonical<'tcx, QueryResult<'tcx, R>> { + pub fn is_proven(&self) -> bool { + self.value.is_proven() + } + + pub fn is_ambiguous(&self) -> bool { + !self.is_proven() + } +} + +/// Subset of `RegionConstraintData` produced by trait query. +#[derive(Clone, Debug, Default)] +pub struct QueryRegionConstraints<'tcx> { + pub region_outlives: Vec<(Region<'tcx>, Region<'tcx>)>, + pub ty_outlives: Vec<(Ty<'tcx>, Region<'tcx>)>, +} + +/// Trait implemented by values that can be canonicalized. It mainly +/// serves to identify the interning table we will use. +pub trait Canonicalize<'gcx: 'tcx, 'tcx>: TypeFoldable<'tcx> + Lift<'gcx> { + type Canonicalized: 'gcx + Debug; + + /// After a value has been fully canonicalized and lifted, this + /// method will allocate it in a global arena. + fn intern( + gcx: TyCtxt<'_, 'gcx, 'gcx>, + value: Canonical<'gcx, Self::Lifted>, + ) -> Self::Canonicalized; +} + +impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { + /// Creates a substitution S for the canonical value with fresh + /// inference variables and applies it to the canonical value. + /// Returns both the instantiated result *and* the substitution S. + /// + /// This is useful at the start of a query: it basically brings + /// the canonical value "into scope" within your new infcx. At the + /// end of processing, the substitution S (once canonicalized) + /// then represents the values that you computed for each of the + /// canonical inputs to your query. + pub fn instantiate_canonical_with_fresh_inference_vars( + &self, + span: Span, + canonical: &Canonical<'tcx, T>, + ) -> (T, CanonicalVarValues<'tcx>) + where + T: TypeFoldable<'tcx>, + { + let canonical_inference_vars = + self.fresh_inference_vars_for_canonical_vars(span, canonical.variables); + let result = canonical.substitute(self.tcx, &canonical_inference_vars); + (result, canonical_inference_vars) + } + + /// Given the "infos" about the canonical variables from some + /// canonical, creates fresh inference variables with the same + /// characteristics. You can then use `substitute` to instantiate + /// the canonical variable with these inference variables. + pub fn fresh_inference_vars_for_canonical_vars( + &self, + span: Span, + variables: &Slice, + ) -> CanonicalVarValues<'tcx> { + let var_values: IndexVec> = variables + .iter() + .map(|info| self.fresh_inference_var_for_canonical_var(span, *info)) + .collect(); + + CanonicalVarValues { var_values } + } + + /// Given the "info" about a canonical variable, creates a fresh + /// inference variable with the same characteristics. + pub fn fresh_inference_var_for_canonical_var( + &self, + span: Span, + cv_info: CanonicalVarInfo, + ) -> Kind<'tcx> { + match cv_info.kind { + CanonicalVarKind::Ty(ty_kind) => { + let ty = match ty_kind { + CanonicalTyVarKind::General => { + self.next_ty_var( + // FIXME(#48696) this handling of universes is not right. + ty::UniverseIndex::ROOT, + TypeVariableOrigin::MiscVariable(span), + ) + } + + CanonicalTyVarKind::Int => self.tcx.mk_int_var(self.next_int_var_id()), + + CanonicalTyVarKind::Float => self.tcx.mk_float_var(self.next_float_var_id()), + }; + Kind::from(ty) + } + + CanonicalVarKind::Region => { + Kind::from(self.next_region_var(RegionVariableOrigin::MiscVariable(span))) + } + } + } + + /// Given the (canonicalized) result to a canonical query, + /// instantiates the result so it can be used, plugging in the + /// values from the canonical query. (Note that the result may + /// have been ambiguous; you should check the certainty level of + /// the query before applying this function.) + /// + /// It's easiest to explain what is happening here by + /// example. Imagine we start out with the query `?A: Foo<'static, + /// ?B>`. We would canonicalize that by introducing two variables: + /// + /// ?0: Foo<'?1, ?2> + /// + /// (Note that all regions get replaced with variables always, + /// even "known" regions like `'static`.) After canonicalization, + /// we also get back an array with the "original values" for each + /// canonicalized variable: + /// + /// [?A, 'static, ?B] + /// + /// Now we do the query and get back some result R. As part of that + /// result, we'll have an array of values for the canonical inputs. + /// For example, the canonical result might be: + /// + /// ``` + /// for<2> { + /// values = [ Vec, '1, ?0 ] + /// ^^ ^^ ^^ these are variables in the result! + /// ... + /// } + /// ``` + /// + /// Note that this result is itself canonical and may include some + /// variables (in this case, `?0`). + /// + /// What we want to do conceptually is to (a) instantiate each of the + /// canonical variables in the result with a fresh inference variable + /// and then (b) unify the values in the result with the original values. + /// Doing step (a) would yield a result of + /// + /// ``` + /// { + /// values = [ Vec, '?X, ?C ] + /// ^^ ^^^ fresh inference variables in `self` + /// .. + /// } + /// ``` + /// + /// Step (b) would then unify: + /// + /// ``` + /// ?A with Vec + /// 'static with '?X + /// ?B with ?C + /// ``` + /// + /// But what we actually do is a mildly optimized variant of + /// that. Rather than eagerly instantiating all of the canonical + /// values in the result with variables, we instead walk the + /// vector of values, looking for cases where the value is just a + /// canonical variable. In our example, `values[2]` is `?C`, so + /// that we means we can deduce that `?C := ?B and `'?X := + /// 'static`. This gives us a partial set of values. Anything for + /// which we do not find a value, we create an inference variable + /// for. **Then** we unify. + pub fn instantiate_query_result( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + original_values: &CanonicalVarValues<'tcx>, + query_result: &Canonical<'tcx, QueryResult<'tcx, R>>, + ) -> InferResult<'tcx, R> + where + R: Debug + TypeFoldable<'tcx>, + { + debug!( + "instantiate_query_result(original_values={:#?}, query_result={:#?})", + original_values, query_result, + ); + + // Every canonical query result includes values for each of + // the inputs to the query. Therefore, we begin by unifying + // these values with the original inputs that were + // canonicalized. + let result_values = &query_result.value.var_values; + assert_eq!(original_values.len(), result_values.len()); + + // Quickly try to find initial values for the canonical + // variables in the result in terms of the query. We do this + // by iterating down the values that the query gave to each of + // the canonical inputs. If we find that one of those values + // is directly equal to one of the canonical variables in the + // result, then we can type the corresponding value from the + // input. See the example above. + let mut opt_values: IndexVec>> = + IndexVec::from_elem_n(None, query_result.variables.len()); + + // In terms of our example above, we are iterating over pairs like: + // [(?A, Vec), ('static, '?1), (?B, ?0)] + for (original_value, result_value) in original_values.iter().zip(result_values) { + match result_value.unpack() { + UnpackedKind::Type(result_value) => { + // e.g., here `result_value` might be `?0` in the example above... + if let ty::TyInfer(ty::InferTy::CanonicalTy(index)) = result_value.sty { + // in which case we would set `canonical_vars[0]` to `Some(?U)`. + opt_values[index] = Some(original_value); + } + } + UnpackedKind::Lifetime(result_value) => { + // e.g., here `result_value` might be `'?1` in the example above... + if let &ty::RegionKind::ReCanonical(index) = result_value { + // in which case we would set `canonical_vars[0]` to `Some('static)`. + opt_values[index] = Some(original_value); + } + } + } + } + + // Create a result substitution: if we found a value for a + // given variable in the loop above, use that. Otherwise, use + // a fresh inference variable. + let result_subst = &CanonicalVarValues { + var_values: query_result + .variables + .iter() + .enumerate() + .map(|(index, info)| match opt_values[CanonicalVar::new(index)] { + Some(k) => k, + None => self.fresh_inference_var_for_canonical_var(cause.span, *info), + }) + .collect(), + }; + + // Apply the result substitution to the query. + let QueryResult { + var_values: query_values, + region_constraints: query_region_constraints, + certainty: _, + value: user_result, + } = query_result.substitute(self.tcx, result_subst); + + // Unify the original values for the canonical variables in + // the input with the value found in the query + // post-substitution. Often, but not always, this is a no-op, + // because we already found the mapping in the first step. + let mut obligations = + self.unify_canonical_vars(cause, param_env, original_values, &query_values)? + .into_obligations(); + + obligations.extend(self.query_region_constraints_into_obligations( + cause, + param_env, + query_region_constraints, + )); + + Ok(InferOk { + value: user_result, + obligations, + }) + } + + /// Converts the region constraints resulting from a query into an + /// iterator of obligations. + fn query_region_constraints_into_obligations<'a>( + &self, + cause: &'a ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + query_region_constraints: QueryRegionConstraints<'tcx>, + ) -> impl Iterator> + 'a { + let QueryRegionConstraints { + region_outlives, + ty_outlives, + } = query_region_constraints; + + let region_obligations = region_outlives.into_iter().map(move |(r1, r2)| { + Obligation::new( + cause.clone(), + param_env, + ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r1, r2))), + ) + }); + + let ty_obligations = ty_outlives.into_iter().map(move |(t1, r2)| { + Obligation::new( + cause.clone(), + param_env, + ty::Predicate::TypeOutlives(ty::Binder(ty::OutlivesPredicate(t1, r2))), + ) + }); + + region_obligations.chain(ty_obligations) + } + + /// Given two sets of values for the same set of canonical variables, unify them. + pub fn unify_canonical_vars( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + variables1: &CanonicalVarValues<'tcx>, + variables2: &CanonicalVarValues<'tcx>, + ) -> InferResult<'tcx, ()> { + assert_eq!(variables1.var_values.len(), variables2.var_values.len()); + self.commit_if_ok(|_| { + let mut obligations = vec![]; + for (value1, value2) in variables1.var_values.iter().zip(&variables2.var_values) { + match (value1.unpack(), value2.unpack()) { + (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => { + obligations + .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations()); + } + ( + UnpackedKind::Lifetime(ty::ReErased), + UnpackedKind::Lifetime(ty::ReErased), + ) => { + // no action needed + } + (UnpackedKind::Lifetime(v1), UnpackedKind::Lifetime(v2)) => { + obligations + .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations()); + } + _ => { + bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,); + } + } + } + Ok(InferOk { + value: (), + obligations, + }) + }) + } + + /// Canonicalizes a query value `V`. When we canonicalize a query, + /// we not only canonicalize unbound inference variables, but we + /// *also* replace all free regions whatsoever. So for example a + /// query like `T: Trait<'static>` would be canonicalized to + /// + /// T: Trait<'?0> + /// + /// with a mapping M that maps `'?0` to `'static`. + pub fn canonicalize_query(&self, value: &V) -> (V::Canonicalized, CanonicalVarValues<'tcx>) + where + V: Canonicalize<'gcx, 'tcx>, + { + self.tcx.sess.perf_stats.queries_canonicalized.increment(); + + Canonicalizer::canonicalize( + value, + Some(self), + self.tcx, + CanonicalizeAllFreeRegions(true), + ) + } + + /// Canonicalizes a query *response* `V`. When we canonicalize a + /// query response, we only canonicalize unbound inference + /// variables, and we leave other free regions alone. So, + /// continuing with the example from `canonicalize_query`, if + /// there was an input query `T: Trait<'static>`, it would have + /// been canonicalized to + /// + /// T: Trait<'?0> + /// + /// with a mapping M that maps `'?0` to `'static`. But if we found that there + /// exists only one possible impl of `Trait`, and it looks like + /// + /// impl Trait<'static> for T { .. } + /// + /// then we would prepare a query result R that (among other + /// things) includes a mapping to `'?0 := 'static`. When + /// canonicalizing this query result R, we would leave this + /// reference to `'static` alone. + pub fn canonicalize_response( + &self, + value: &V, + ) -> (V::Canonicalized, CanonicalVarValues<'tcx>) + where + V: Canonicalize<'gcx, 'tcx>, + { + Canonicalizer::canonicalize( + value, + Some(self), + self.tcx, + CanonicalizeAllFreeRegions(false), + ) + } +} + +impl<'cx, 'gcx> TyCtxt<'cx, 'gcx, 'gcx> { + /// Canonicalize a value that doesn't have any inference variables + /// or other things (and we know it). + pub fn canonicalize_global(self, value: &V) -> (V::Canonicalized, CanonicalVarValues<'gcx>) + where + V: Canonicalize<'gcx, 'gcx>, + { + Canonicalizer::canonicalize(value, None, self, CanonicalizeAllFreeRegions(false)) + } +} + +/// If this flag is true, then all free regions will be replaced with +/// a canonical var. This is used to make queries as generic as +/// possible. For example, the query `F: Foo<'static>` would be +/// canonicalized to `F: Foo<'0>`. +struct CanonicalizeAllFreeRegions(bool); + +struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + variables: IndexVec, + indices: FxHashMap, CanonicalVar>, + var_values: IndexVec>, + canonicalize_all_free_regions: CanonicalizeAllFreeRegions, + needs_canonical_flags: TypeFlags, +} + +impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { + self.tcx + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReLateBound(..) => { + // leave bound regions alone + r + } + + ty::ReVar(vid) => { + let r = self.infcx + .unwrap() + .borrow_region_constraints() + .opportunistic_resolve_var(self.tcx, vid); + let info = CanonicalVarInfo { + kind: CanonicalVarKind::Region, + }; + debug!( + "canonical: region var found with vid {:?}, \ + opportunistically resolved to {:?}", + vid, r + ); + let cvar = self.canonical_var(info, Kind::from(r)); + self.tcx().mk_region(ty::ReCanonical(cvar)) + } + + ty::ReStatic + | ty::ReEarlyBound(..) + | ty::ReFree(_) + | ty::ReScope(_) + | ty::ReSkolemized(..) + | ty::ReEmpty + | ty::ReErased => { + if self.canonicalize_all_free_regions.0 { + let info = CanonicalVarInfo { + kind: CanonicalVarKind::Region, + }; + let cvar = self.canonical_var(info, Kind::from(r)); + self.tcx().mk_region(ty::ReCanonical(cvar)) + } else { + r + } + } + + ty::ReClosureBound(..) | ty::ReCanonical(_) => { + bug!("canonical region encountered during canonicalization") + } + } + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.sty { + ty::TyInfer(ty::TyVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::General, t), + + ty::TyInfer(ty::IntVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Int, t), + + ty::TyInfer(ty::FloatVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Float, t), + + ty::TyInfer(ty::FreshTy(_)) + | ty::TyInfer(ty::FreshIntTy(_)) + | ty::TyInfer(ty::FreshFloatTy(_)) => { + bug!("encountered a fresh type during canonicalization") + } + + ty::TyInfer(ty::CanonicalTy(_)) => { + bug!("encountered a canonical type during canonicalization") + } + + // Replace a `()` that "would've fallen back" to `!` with just `()`. + ty::TyTuple(ref tys, true) => { + assert!(tys.is_empty()); + self.tcx().mk_nil() + } + + ty::TyClosure(..) + | ty::TyGenerator(..) + | ty::TyGeneratorWitness(..) + | ty::TyBool + | ty::TyChar + | ty::TyInt(..) + | ty::TyUint(..) + | ty::TyFloat(..) + | ty::TyAdt(..) + | ty::TyStr + | ty::TyError + | ty::TyArray(..) + | ty::TySlice(..) + | ty::TyRawPtr(..) + | ty::TyRef(..) + | ty::TyFnDef(..) + | ty::TyFnPtr(_) + | ty::TyDynamic(..) + | ty::TyNever + | ty::TyTuple(_, false) + | ty::TyProjection(..) + | ty::TyForeign(..) + | ty::TyParam(..) + | ty::TyAnon(..) => { + if t.flags.intersects(self.needs_canonical_flags) { + t.super_fold_with(self) + } else { + t + } + } + } + } +} + +impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { + /// The main `canonicalize` method, shared impl of + /// `canonicalize_query` and `canonicalize_response`. + fn canonicalize( + value: &V, + infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + canonicalize_all_free_regions: CanonicalizeAllFreeRegions, + ) -> (V::Canonicalized, CanonicalVarValues<'tcx>) + where + V: Canonicalize<'gcx, 'tcx>, + { + debug_assert!( + !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS), + "canonicalizing a canonical value: {:?}", + value, + ); + + let needs_canonical_flags = if canonicalize_all_free_regions.0 { + TypeFlags::HAS_FREE_REGIONS | TypeFlags::KEEP_IN_LOCAL_TCX + } else { + TypeFlags::KEEP_IN_LOCAL_TCX + }; + + let gcx = tcx.global_tcx(); + + // Fast path: nothing that needs to be canonicalized. + if !value.has_type_flags(needs_canonical_flags) { + let out_value = gcx.lift(value).unwrap(); + let canon_value = V::intern( + gcx, + Canonical { + variables: Slice::empty(), + value: out_value, + }, + ); + let values = CanonicalVarValues { var_values: IndexVec::default() }; + return (canon_value, values); + } + + let mut canonicalizer = Canonicalizer { + infcx, + tcx, + canonicalize_all_free_regions, + needs_canonical_flags, + variables: IndexVec::default(), + indices: FxHashMap::default(), + var_values: IndexVec::default(), + }; + let out_value = value.fold_with(&mut canonicalizer); + + // Once we have canonicalized `out_value`, it should not + // contain anything that ties it to this inference context + // anymore, so it should live in the global arena. + let out_value = gcx.lift(&out_value).unwrap_or_else(|| { + bug!( + "failed to lift `{:?}`, canonicalized from `{:?}`", + out_value, + value + ) + }); + + let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables.raw); + + let canonical_value = V::intern( + gcx, + Canonical { + variables: canonical_variables, + value: out_value, + }, + ); + let canonical_var_values = CanonicalVarValues { + var_values: canonicalizer.var_values, + }; + (canonical_value, canonical_var_values) + } + + /// Creates a canonical variable replacing `kind` from the input, + /// or returns an existing variable if `kind` has already been + /// seen. `kind` is expected to be an unbound variable (or + /// potentially a free region). + fn canonical_var(&mut self, info: CanonicalVarInfo, kind: Kind<'tcx>) -> CanonicalVar { + let Canonicalizer { + indices, + variables, + var_values, + .. + } = self; + + indices + .entry(kind) + .or_insert_with(|| { + let cvar1 = variables.push(info); + let cvar2 = var_values.push(kind); + assert_eq!(cvar1, cvar2); + cvar1 + }) + .clone() + } + + /// Given a type variable `ty_var` of the given kind, first check + /// if `ty_var` is bound to anything; if so, canonicalize + /// *that*. Otherwise, create a new canonical variable for + /// `ty_var`. + fn canonicalize_ty_var(&mut self, ty_kind: CanonicalTyVarKind, ty_var: Ty<'tcx>) -> Ty<'tcx> { + let infcx = self.infcx.expect("encountered ty-var without infcx"); + let bound_to = infcx.shallow_resolve(ty_var); + if bound_to != ty_var { + self.fold_ty(bound_to) + } else { + let info = CanonicalVarInfo { + kind: CanonicalVarKind::Ty(ty_kind), + }; + let cvar = self.canonical_var(info, Kind::from(ty_var)); + self.tcx().mk_infer(ty::InferTy::CanonicalTy(cvar)) + } + } +} + +impl<'tcx, V> Canonical<'tcx, V> { + /// Instantiate the wrapped value, replacing each canonical value + /// with the value given in `var_values`. + pub fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V + where + V: TypeFoldable<'tcx>, + { + assert_eq!(self.variables.len(), var_values.var_values.len()); + self.value + .fold_with(&mut CanonicalVarValuesSubst { tcx, var_values }) + } +} + +struct CanonicalVarValuesSubst<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + var_values: &'cx CanonicalVarValues<'tcx>, +} + +impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for CanonicalVarValuesSubst<'cx, 'gcx, 'tcx> { + fn tcx(&self) -> TyCtxt<'_, 'gcx, 'tcx> { + self.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.sty { + ty::TyInfer(ty::InferTy::CanonicalTy(c)) => { + match self.var_values.var_values[c].unpack() { + UnpackedKind::Type(ty) => ty, + r => bug!("{:?} is a type but value is {:?}", c, r), + } + } + _ => t.super_fold_with(self), + } + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match r { + ty::RegionKind::ReCanonical(c) => match self.var_values.var_values[*c].unpack() { + UnpackedKind::Lifetime(l) => l, + r => bug!("{:?} is a region but value is {:?}", c, r), + }, + _ => r.super_fold_with(self), + } + } +} + +CloneTypeFoldableAndLiftImpls! { + for <'tcx> { + ::infer::canonical::Certainty, + ::infer::canonical::CanonicalVarInfo, + ::infer::canonical::CanonicalVarInfos<'tcx>, + ::infer::canonical::CanonicalVarKind, + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx, C> TypeFoldable<'tcx> for Canonical<'tcx, C> { + variables, + value, + } where C: TypeFoldable<'tcx> +} + +impl<'tcx> CanonicalVarValues<'tcx> { + fn iter<'a>(&'a self) -> impl Iterator> + 'a { + self.var_values.iter().cloned() + } + + fn len(&self) -> usize { + self.var_values.len() + } +} + +impl<'a, 'tcx> IntoIterator for &'a CanonicalVarValues<'tcx> { + type Item = Kind<'tcx>; + type IntoIter = ::std::iter::Cloned<::std::slice::Iter<'a, Kind<'tcx>>>; + + fn into_iter(self) -> Self::IntoIter { + self.var_values.iter().cloned() + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for CanonicalVarValues<'a> { + type Lifted = CanonicalVarValues<'tcx>; + var_values, + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for CanonicalVarValues<'tcx> { + var_values, + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for QueryRegionConstraints<'tcx> { + region_outlives, ty_outlives + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for QueryRegionConstraints<'a> { + type Lifted = QueryRegionConstraints<'tcx>; + region_outlives, ty_outlives + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx, R> TypeFoldable<'tcx> for QueryResult<'tcx, R> { + var_values, region_constraints, certainty, value + } where R: TypeFoldable<'tcx>, +} + +BraceStructLiftImpl! { + impl<'a, 'tcx, R> Lift<'tcx> for QueryResult<'a, R> { + type Lifted = QueryResult<'tcx, R::Lifted>; + var_values, region_constraints, certainty, value + } where R: Lift<'tcx> +} diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 469cb4591124e..1c581c44464e7 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -476,6 +476,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, ' } } + ty::ReCanonical(..) | ty::ReClosureBound(..) => { span_bug!( self.span, diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 07551c4ebd319..96c2309882108 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -154,6 +154,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } // We shouldn't encounter an error message with ReClosureBound. + ty::ReCanonical(..) | ty::ReClosureBound(..) => { bug!("encountered unexpected ReClosureBound: {:?}", region,); } diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index ee0921f4b07ae..6074bfd083d46 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -114,9 +114,10 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { self.tcx().types.re_erased } + ty::ReCanonical(..) | ty::ReClosureBound(..) => { bug!( - "encountered unexpected ReClosureBound: {:?}", + "encountered unexpected region: {:?}", r, ); } @@ -170,6 +171,9 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { t } + ty::TyInfer(ty::CanonicalTy(..)) => + bug!("encountered canonical ty during freshening"), + ty::TyGenerator(..) | ty::TyBool | ty::TyChar | diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 3ac4ec5bee416..00b2ac7449f7e 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -258,6 +258,8 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { let tcx = self.region_rels.tcx; match (a, b) { + (&ty::ReCanonical(..), _) | + (_, &ty::ReCanonical(..)) | (&ty::ReClosureBound(..), _) | (_, &ty::ReClosureBound(..)) | (&ReLateBound(..), _) | diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 5f0c2d1e76bcc..5127807bf70dc 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -50,6 +50,7 @@ use self::unify_key::ToType; pub mod anon_types; pub mod at; +pub mod canonical; mod combine; mod equate; pub mod error_reporting; @@ -473,6 +474,12 @@ impl<'tcx, T> InferOk<'tcx, T> { } } +impl<'tcx> InferOk<'tcx, ()> { + pub fn into_obligations(self) -> PredicateObligations<'tcx> { + self.obligations + } +} + #[must_use = "once you start a snapshot, you should always consume it"] pub struct CombinedSnapshot<'a, 'tcx:'a> { projection_cache_snapshot: traits::ProjectionCacheSnapshot, @@ -1644,4 +1651,3 @@ impl<'tcx> fmt::Debug for RegionObligation<'tcx> { self.sup_type) } } - diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 2cf176036296b..77b3a87c0ed16 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -57,9 +57,9 @@ #![feature(inclusive_range)] #![feature(inclusive_range_syntax)] #![cfg_attr(windows, feature(libc))] +#![feature(match_default_bindings)] #![feature(macro_lifetime_matcher)] #![feature(macro_vis_matcher)] -#![feature(match_default_bindings)] #![feature(never_type)] #![feature(non_exhaustive)] #![feature(nonzero)] diff --git a/src/librustc/macros.rs b/src/librustc/macros.rs index 6013e3aef81e1..d8a723e184d2c 100644 --- a/src/librustc/macros.rs +++ b/src/librustc/macros.rs @@ -119,6 +119,25 @@ macro_rules! impl_stable_hash_for { } } }; + + (impl<$tcx:lifetime $(, $T:ident)*> for struct $struct_name:path { + $($field:ident),* $(,)* + }) => { + impl<'a, $tcx, $($T,)*> ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>> for $struct_name + where $($T: ::rustc_data_structures::stable_hasher::HashStable<$crate::ich::StableHashingContext<'a>>),* + { + #[inline] + fn hash_stable(&self, + __ctx: &mut $crate::ich::StableHashingContext<'a>, + __hasher: &mut ::rustc_data_structures::stable_hasher::StableHasher) { + let $struct_name { + $(ref $field),* + } = *self; + + $( $field.hash_stable(__ctx, __hasher));* + } + } + }; } #[macro_export] diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 01e1037b6222a..7b4211e487a0e 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -173,6 +173,8 @@ pub struct PerfStats { pub symbol_hash_time: Cell, /// The accumulated time spent decoding def path tables from metadata pub decode_def_path_tables_time: Cell, + /// Total number of values canonicalized queries constructed. + pub queries_canonicalized: Cell, } /// Enum to support dispatch of one-time diagnostics (in Session.diag_once) @@ -858,6 +860,8 @@ impl Session { "Total time spent decoding DefPath tables: {}", duration_to_secs_str(self.perf_stats.decode_def_path_tables_time.get()) ); + println!("Total queries canonicalized: {}", + self.perf_stats.queries_canonicalized.get()); } /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. @@ -1144,6 +1148,7 @@ pub fn build_session_( incr_comp_bytes_hashed: Cell::new(0), symbol_hash_time: Cell::new(Duration::from_secs(0)), decode_def_path_tables_time: Cell::new(Duration::from_secs(0)), + queries_canonicalized: Cell::new(0), }, code_stats: RefCell::new(CodeStats::new()), optimization_fuel_crate, diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 728702ef61fd1..f2f54dcedfd65 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -2083,9 +2083,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ty::TyProjection(_) | ty::TyParam(_) | ty::TyAnon(..) => None, ty::TyInfer(ty::TyVar(_)) => Ambiguous, - ty::TyInfer(ty::FreshTy(_)) - | ty::TyInfer(ty::FreshIntTy(_)) - | ty::TyInfer(ty::FreshFloatTy(_)) => { + ty::TyInfer(ty::CanonicalTy(_)) | + ty::TyInfer(ty::FreshTy(_)) | + ty::TyInfer(ty::FreshIntTy(_)) | + ty::TyInfer(ty::FreshFloatTy(_)) => { bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); } @@ -2154,9 +2155,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { Ambiguous } - ty::TyInfer(ty::FreshTy(_)) - | ty::TyInfer(ty::FreshIntTy(_)) - | ty::TyInfer(ty::FreshFloatTy(_)) => { + ty::TyInfer(ty::CanonicalTy(_)) | + ty::TyInfer(ty::FreshTy(_)) | + ty::TyInfer(ty::FreshIntTy(_)) | + ty::TyInfer(ty::FreshFloatTy(_)) => { bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); } @@ -2195,6 +2197,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ty::TyParam(..) | ty::TyForeign(..) | ty::TyProjection(..) | + ty::TyInfer(ty::CanonicalTy(_)) | ty::TyInfer(ty::TyVar(_)) | ty::TyInfer(ty::FreshTy(_)) | ty::TyInfer(ty::FreshIntTy(_)) | diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index e7d6bdd3383da..24d3b37f804ee 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -23,6 +23,7 @@ use hir::map as hir_map; use hir::map::DefPathHash; use lint::{self, Lint}; use ich::{StableHashingContext, NodeIdHashingMode}; +use infer::canonical::{CanonicalVarInfo, CanonicalVarInfos}; use infer::outlives::free_region_map::FreeRegionMap; use middle::const_val::ConstVal; use middle::cstore::{CrateStore, LinkMeta}; @@ -131,6 +132,7 @@ pub struct CtxtInterners<'tcx> { type_: RefCell>>>, type_list: RefCell>>>>, substs: RefCell>>>, + canonical_var_infos: RefCell>>>, region: RefCell>>, existential_predicates: RefCell>>>>, predicates: RefCell>>>>, @@ -146,6 +148,7 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> { substs: RefCell::new(FxHashSet()), region: RefCell::new(FxHashSet()), existential_predicates: RefCell::new(FxHashSet()), + canonical_var_infos: RefCell::new(FxHashSet()), predicates: RefCell::new(FxHashSet()), const_: RefCell::new(FxHashSet()), } @@ -1838,6 +1841,12 @@ impl<'tcx: 'lcx, 'lcx> Borrow<[Ty<'lcx>]> for Interned<'tcx, Slice>> { } } +impl<'tcx: 'lcx, 'lcx> Borrow<[CanonicalVarInfo]> for Interned<'tcx, Slice> { + fn borrow<'a>(&'a self) -> &'a [CanonicalVarInfo] { + &self.0[..] + } +} + impl<'tcx: 'lcx, 'lcx> Borrow<[Kind<'lcx>]> for Interned<'tcx, Substs<'tcx>> { fn borrow<'a>(&'a self) -> &'a [Kind<'lcx>] { &self.0[..] @@ -1970,6 +1979,22 @@ slice_interners!( substs: _intern_substs(Kind) ); +// This isn't a perfect fit: CanonicalVarInfo slices are always +// allocated in the global arena, so this `intern_method!` macro is +// overly general. But we just return false for the code that checks +// whether they belong in the thread-local arena, so no harm done, and +// seems better than open-coding the rest. +intern_method! { + 'tcx, + canonical_var_infos: _intern_canonical_var_infos( + &[CanonicalVarInfo], + alloc_slice, + Deref::deref, + |xs: &[CanonicalVarInfo]| -> &Slice { unsafe { mem::transmute(xs) } }, + |_xs: &[CanonicalVarInfo]| -> bool { false } + ) -> Slice +} + impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Given a `fn` type, returns an equivalent `unsafe fn` type; /// that is, a `fn` type that is equivalent in every way for being @@ -2257,6 +2282,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + pub fn intern_canonical_var_infos(self, ts: &[CanonicalVarInfo]) -> CanonicalVarInfos<'gcx> { + if ts.len() == 0 { + Slice::empty() + } else { + self.global_tcx()._intern_canonical_var_infos(ts) + } + } + pub fn mk_fn_sig(self, inputs: I, output: I::Item, diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index dcb70a8f86a8a..8a0253ed2f115 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -220,6 +220,7 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { ty::TyInfer(ty::TyVar(_)) => "inferred type".to_string(), ty::TyInfer(ty::IntVar(_)) => "integral variable".to_string(), ty::TyInfer(ty::FloatVar(_)) => "floating-point variable".to_string(), + ty::TyInfer(ty::CanonicalTy(_)) | ty::TyInfer(ty::FreshTy(_)) => "skolemized type".to_string(), ty::TyInfer(ty::FreshIntTy(_)) => "skolemized integral type".to_string(), ty::TyInfer(ty::FreshFloatTy(_)) => "skolemized floating-point type".to_string(), diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index f067789771c59..cae64fd4c95c6 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -112,8 +112,16 @@ impl FlagComputation { match infer { ty::FreshTy(_) | ty::FreshIntTy(_) | - ty::FreshFloatTy(_) => {} - _ => self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX) + ty::FreshFloatTy(_) | + ty::CanonicalTy(_) => { + self.add_flags(TypeFlags::HAS_CANONICAL_VARS); + } + + ty::TyVar(_) | + ty::IntVar(_) | + ty::FloatVar(_) => { + self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX) + } } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index e3405e0c3b360..93d1585cdc83d 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -60,7 +60,7 @@ use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, use hir; -pub use self::sty::{Binder, DebruijnIndex}; +pub use self::sty::{Binder, CanonicalVar, DebruijnIndex}; pub use self::sty::{FnSig, GenSig, PolyFnSig, PolyGenSig}; pub use self::sty::{InferTy, ParamTy, ProjectionTy, ExistentialPredicate}; pub use self::sty::{ClosureSubsts, GeneratorInterior, TypeAndMut}; @@ -452,6 +452,10 @@ bitflags! { // Currently we can't normalize projections w/ bound regions. const HAS_NORMALIZABLE_PROJECTION = 1 << 12; + // Set if this includes a "canonical" type or region var -- + // ought to be true only for the results of canonicalization. + const HAS_CANONICAL_VARS = 1 << 13; + const NEEDS_SUBST = TypeFlags::HAS_PARAMS.bits | TypeFlags::HAS_SELF.bits | TypeFlags::HAS_RE_EARLY_BOUND.bits; @@ -470,7 +474,8 @@ bitflags! { TypeFlags::HAS_PROJECTION.bits | TypeFlags::HAS_TY_CLOSURE.bits | TypeFlags::HAS_LOCAL_NAMES.bits | - TypeFlags::KEEP_IN_LOCAL_TCX.bits; + TypeFlags::KEEP_IN_LOCAL_TCX.bits | + TypeFlags::HAS_CANONICAL_VARS.bits; } } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 7bcc816b5f03d..109422564c84d 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1047,6 +1047,9 @@ pub enum RegionKind { /// `ClosureRegionRequirements` that are produced by MIR borrowck. /// See `ClosureRegionRequirements` for more details. ReClosureBound(RegionVid), + + /// Canonicalized region, used only when preparing a trait query. + ReCanonical(CanonicalVar), } impl<'tcx> serialize::UseSpecializedDecodable for Region<'tcx> {} @@ -1091,8 +1094,13 @@ pub enum InferTy { FreshTy(u32), FreshIntTy(u32), FreshFloatTy(u32), + + /// Canonicalized type variable, used only when preparing a trait query. + CanonicalTy(CanonicalVar), } +newtype_index!(CanonicalVar); + /// A `ProjectionPredicate` for an `ExistentialTraitRef`. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct ExistentialProjection<'tcx> { @@ -1213,6 +1221,10 @@ impl RegionKind { } ty::ReErased => { } + ty::ReCanonical(..) => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_CANONICAL_VARS; + } ty::ReClosureBound(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; } diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs index e70c72335aa36..a301049fe1c40 100644 --- a/src/librustc/ty/subst.rs +++ b/src/librustc/ty/subst.rs @@ -40,6 +40,7 @@ const TAG_MASK: usize = 0b11; const TYPE_TAG: usize = 0b00; const REGION_TAG: usize = 0b01; +#[derive(Debug)] pub enum UnpackedKind<'tcx> { Lifetime(ty::Region<'tcx>), Type(Ty<'tcx>), diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index a7ee8579fb989..e8ce49d39d299 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -834,6 +834,9 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W> ty::ReEmpty => { // No variant fields to hash for these ... } + ty::ReCanonical(c) => { + self.hash(c); + } ty::ReLateBound(db, ty::BrAnon(i)) => { self.hash(db.depth); self.hash(i); diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 5e2792ee6410e..efa53c775ae25 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -721,6 +721,9 @@ define_print! { ty::ReEarlyBound(ref data) => { write!(f, "{}", data.name) } + ty::ReCanonical(_) => { + write!(f, "'_") + } ty::ReLateBound(_, br) | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | ty::ReSkolemized(_, br) => { @@ -785,6 +788,10 @@ define_print! { write!(f, "{:?}", vid) } + ty::ReCanonical(c) => { + write!(f, "'?{}", c.index()) + } + ty::ReSkolemized(id, ref bound_region) => { write!(f, "ReSkolemized({:?}, {:?})", id, bound_region) } @@ -888,6 +895,7 @@ define_print! { ty::TyVar(_) => write!(f, "_"), ty::IntVar(_) => write!(f, "{}", "{integer}"), ty::FloatVar(_) => write!(f, "{}", "{float}"), + ty::CanonicalTy(_) => write!(f, "_"), ty::FreshTy(v) => write!(f, "FreshTy({})", v), ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v), ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v) @@ -899,6 +907,7 @@ define_print! { ty::TyVar(ref v) => write!(f, "{:?}", v), ty::IntVar(ref v) => write!(f, "{:?}", v), ty::FloatVar(ref v) => write!(f, "{:?}", v), + ty::CanonicalTy(v) => write!(f, "?{:?}", v.index()), ty::FreshTy(v) => write!(f, "FreshTy({:?})", v), ty::FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v), ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v) diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index 9888b2fffc779..a01b3cbf47bee 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -427,6 +427,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { // These cannot exist in borrowck RegionKind::ReVar(..) | + RegionKind::ReCanonical(..) | RegionKind::ReSkolemized(..) | RegionKind::ReClosureBound(..) | RegionKind::ReErased => span_bug!(borrow_span, diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index 5cbe2822e5c03..49234f4ed7fde 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -366,6 +366,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { ty::ReStatic => self.item_ub, + ty::ReCanonical(_) | ty::ReEmpty | ty::ReClosureBound(..) | ty::ReLateBound(..) | diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index 9d636b48bd0c5..459aa9ea488fd 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -23,6 +23,7 @@ #![feature(slice_patterns)] #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(macro_lifetime_matcher)] #![feature(i128_type)] #![feature(from_ref)] diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index 11c2bd7368760..a5b1a7e57ab46 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -330,7 +330,7 @@ macro_rules! newtype_index { ); } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct IndexVec { pub raw: Vec, _marker: PhantomData diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index da0da622d5214..f77c22bd89544 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -18,7 +18,9 @@ #![feature(fs_read_write)] #![feature(i128_type)] #![feature(libc)] +#![feature(macro_lifetime_matcher)] #![feature(proc_macro_internals)] +#![feature(macro_lifetime_matcher)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(specialization)] diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 7ab52e98a0ee1..84ba13674505f 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -457,6 +457,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { (RegionKind::ReLateBound(_, _), _) | (RegionKind::ReSkolemized(_, _), _) | (RegionKind::ReClosureBound(_), _) + | (RegionKind::ReCanonical(_), _) | (RegionKind::ReErased, _) => { span_bug!(drop_span, "region does not make sense in this context"); } diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 490dc4e5ac4a9..953747756517d 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -13,6 +13,7 @@ html_root_url = "https://doc.rust-lang.org/nightly/")] #![deny(warnings)] #![feature(custom_attribute)] +#![feature(macro_lifetime_matcher)] #![allow(unused_attributes)] #[macro_use] diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index 44ac7a10e8287..2a4e92034de84 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -423,6 +423,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { // way early-bound regions do, so we skip them here. } + ty::ReCanonical(_) | ty::ReFree(..) | ty::ReClosureBound(..) | ty::ReScope(..) | diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5d4addce2c439..ff281a53ab7e4 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1490,6 +1490,7 @@ impl Clean> for ty::RegionKind { ty::ReSkolemized(..) | ty::ReEmpty | ty::ReClosureBound(_) | + ty::ReCanonical(_) | ty::ReErased => None } } From 8c024fdafb6339c5375543ef400a33419d65a19b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 23 Feb 2018 15:30:27 -0500 Subject: [PATCH 13/27] in `Foo(X)` dep-nodes, allow X to be a `ty` not a `tt` Before, the identifier `X` was also used when generating a pattern to match against the dep-node. So `Foo(DefId)` would generate a match pattern like: match foo { Foo(DefId) => ... } This does not scale to more general types like `&'tcx Ty<'tcx>`. Therefore, we now require *exactly one* argument (the macro was internally tupling anyway, and no actual nodes use more than one argument), and then we can generate a fixed pattern like: match foo { Foo(arg) => ... } Huzzah. (Also, hygiene is nice.) --- src/librustc/dep_graph/dep_node.rs | 45 ++++++++++++++++-------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 7d8709a82f4d3..6a2c003179a12 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -80,6 +80,10 @@ macro_rules! erase { ($x:tt) => ({}) } +macro_rules! replace { + ($x:tt with $($y:tt)*) => ($($y)*) +} + macro_rules! is_anon_attr { (anon) => (true); ($attr:ident) => (false); @@ -111,7 +115,7 @@ macro_rules! define_dep_nodes { (<$tcx:tt> $( [$($attr:ident),* ] - $variant:ident $(( $($tuple_arg:tt),* ))* + $variant:ident $(( $tuple_arg_ty:ty $(,)* ))* $({ $($struct_arg_name:ident : $struct_arg_ty:ty),* })* ,)* ) => ( @@ -134,7 +138,7 @@ macro_rules! define_dep_nodes { // tuple args $({ - return <( $($tuple_arg,)* ) as DepNodeParams> + return <$tuple_arg_ty as DepNodeParams> ::CAN_RECONSTRUCT_QUERY_KEY; })* @@ -186,7 +190,7 @@ macro_rules! define_dep_nodes { DepKind :: $variant => { // tuple args $({ - $(erase!($tuple_arg);)* + erase!($tuple_arg_ty); return true; })* @@ -205,7 +209,7 @@ macro_rules! define_dep_nodes { pub enum DepConstructor<$tcx> { $( - $variant $(( $($tuple_arg),* ))* + $variant $(( $tuple_arg_ty ))* $({ $($struct_arg_name : $struct_arg_ty),* })* ),* } @@ -227,15 +231,14 @@ macro_rules! define_dep_nodes { { match dep { $( - DepConstructor :: $variant $(( $($tuple_arg),* ))* + DepConstructor :: $variant $(( replace!(($tuple_arg_ty) with arg) ))* $({ $($struct_arg_name),* })* => { // tuple args $({ - let tupled_args = ( $($tuple_arg,)* ); - let hash = DepNodeParams::to_fingerprint(&tupled_args, - tcx); + erase!($tuple_arg_ty); + let hash = DepNodeParams::to_fingerprint(&arg, tcx); let dep_node = DepNode { kind: DepKind::$variant, hash @@ -247,7 +250,7 @@ macro_rules! define_dep_nodes { tcx.sess.opts.debugging_opts.query_dep_graph) { tcx.dep_graph.register_dep_node_debug_str(dep_node, || { - tupled_args.to_debug_str(tcx) + arg.to_debug_str(tcx) }); } @@ -679,43 +682,43 @@ impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a, T> DepNodeParams<'a, 'gcx, 'tcx> for T } } -impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (DefId,) { +impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for DefId { const CAN_RECONSTRUCT_QUERY_KEY: bool = true; fn to_fingerprint(&self, tcx: TyCtxt) -> Fingerprint { - tcx.def_path_hash(self.0).0 + tcx.def_path_hash(*self).0 } fn to_debug_str(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> String { - tcx.item_path_str(self.0) + tcx.item_path_str(*self) } } -impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (DefIndex,) { +impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for DefIndex { const CAN_RECONSTRUCT_QUERY_KEY: bool = true; fn to_fingerprint(&self, tcx: TyCtxt) -> Fingerprint { - tcx.hir.definitions().def_path_hash(self.0).0 + tcx.hir.definitions().def_path_hash(*self).0 } fn to_debug_str(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> String { - tcx.item_path_str(DefId::local(self.0)) + tcx.item_path_str(DefId::local(*self)) } } -impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (CrateNum,) { +impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for CrateNum { const CAN_RECONSTRUCT_QUERY_KEY: bool = true; fn to_fingerprint(&self, tcx: TyCtxt) -> Fingerprint { let def_id = DefId { - krate: self.0, + krate: *self, index: CRATE_DEF_INDEX, }; tcx.def_path_hash(def_id).0 } fn to_debug_str(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> String { - tcx.crate_name(self.0).as_str().to_string() + tcx.crate_name(*self).as_str().to_string() } } @@ -743,17 +746,17 @@ impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (DefId, De } } -impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (HirId,) { +impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for HirId { const CAN_RECONSTRUCT_QUERY_KEY: bool = false; // We actually would not need to specialize the implementation of this // method but it's faster to combine the hashes than to instantiate a full // hashing context and stable-hashing state. fn to_fingerprint(&self, tcx: TyCtxt) -> Fingerprint { - let (HirId { + let HirId { owner, local_id: ItemLocalId(local_id), - },) = *self; + } = *self; let def_path_hash = tcx.def_path_hash(DefId::local(owner)); let local_id = Fingerprint::from_smaller_hash(local_id as u64); From 3a50b41da4cbb135fc74cdc8ebf2b09edb396f87 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 25 Feb 2018 10:58:54 -0500 Subject: [PATCH 14/27] introduce `infcx.at(..).normalize(..)` operation [VIC] It is backed by the new `normalize_projection_ty` query, which uses canonicalization. --- src/Cargo.lock | 14 + src/librustc/dep_graph/dep_node.rs | 6 +- src/librustc/infer/at.rs | 6 +- src/librustc/infer/mod.rs | 11 + src/librustc/infer/outlives/obligations.rs | 10 + src/librustc/infer/region_constraints/mod.rs | 4 + src/librustc/session/mod.rs | 11 + src/librustc/traits/mod.rs | 2 + src/librustc/traits/query/mod.rs | 31 ++ src/librustc/traits/query/normalize.rs | 268 ++++++++++++++++++ src/librustc/ty/context.rs | 1 + src/librustc/ty/maps/config.rs | 10 + src/librustc/ty/maps/keys.rs | 11 + src/librustc/ty/maps/mod.rs | 12 + src/librustc/ty/maps/plumbing.rs | 1 + src/librustc_driver/Cargo.toml | 1 + src/librustc_driver/driver.rs | 2 + src/librustc_driver/lib.rs | 1 + .../borrow_check/nll/type_check/mod.rs | 18 +- src/librustc_traits/Cargo.toml | 18 ++ src/librustc_traits/lib.rs | 37 +++ .../normalize_projection_ty.rs | 55 ++++ src/librustc_traits/util.rs | 117 ++++++++ 23 files changed, 637 insertions(+), 10 deletions(-) create mode 100644 src/librustc/traits/query/mod.rs create mode 100644 src/librustc/traits/query/normalize.rs create mode 100644 src/librustc_traits/Cargo.toml create mode 100644 src/librustc_traits/lib.rs create mode 100644 src/librustc_traits/normalize_projection_ty.rs create mode 100644 src/librustc_traits/util.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index f8c04fe9272a5..ed32984bb5844 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1884,6 +1884,7 @@ dependencies = [ "rustc_privacy 0.0.0", "rustc_resolve 0.0.0", "rustc_save_analysis 0.0.0", + "rustc_traits 0.0.0", "rustc_trans_utils 0.0.0", "rustc_typeck 0.0.0", "serialize 0.0.0", @@ -2068,6 +2069,19 @@ dependencies = [ "syntax_pos 0.0.0", ] +[[package]] +name = "rustc_traits" +version = "0.0.0" +dependencies = [ + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "graphviz 0.0.0", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc 0.0.0", + "rustc_data_structures 0.0.0", + "syntax 0.0.0", + "syntax_pos 0.0.0", +] + [[package]] name = "rustc_trans" version = "0.0.0" diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 6a2c003179a12..caef3a8acc44c 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -67,11 +67,12 @@ use hir::{HirId, ItemLocalId}; use ich::{Fingerprint, StableHashingContext}; use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; -use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; -use ty::subst::Substs; use std::fmt; use std::hash::Hash; use syntax_pos::symbol::InternedString; +use traits::query::CanonicalProjectionGoal; +use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; +use ty::subst::Substs; // erase!() just makes tokens go away. It's used to specify which macro argument // is repeated (i.e. which sub-expression of the macro we are in) but don't need @@ -635,6 +636,7 @@ define_dep_nodes!( <'tcx> [] CompileCodegenUnit(InternedString), [input] OutputFilenames, [anon] NormalizeTy, + [] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>), [] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) }, diff --git a/src/librustc/infer/at.rs b/src/librustc/infer/at.rs index d9fbf4aa51482..89dbc76c8a65c 100644 --- a/src/librustc/infer/at.rs +++ b/src/librustc/infer/at.rs @@ -40,9 +40,9 @@ use super::*; use ty::relate::{Relate, TypeRelation}; pub struct At<'a, 'gcx: 'tcx, 'tcx: 'a> { - infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - cause: &'a ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, + pub infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + pub cause: &'a ObligationCause<'tcx>, + pub param_env: ty::ParamEnv<'tcx>, } pub struct Trace<'a, 'gcx: 'tcx, 'tcx: 'a> { diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 5127807bf70dc..2c58e17b2833c 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -69,6 +69,7 @@ pub mod type_variable; pub mod unify_key; #[must_use] +#[derive(Debug)] pub struct InferOk<'tcx, T> { pub value: T, pub obligations: PredicateObligations<'tcx>, @@ -1224,6 +1225,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.borrow_region_constraints().take_and_reset_data() } + /// Gives temporary access to the region constraint data. + #[allow(non_camel_case_types)] // bug with impl trait + pub fn with_region_constraints( + &self, + op: impl FnOnce(&RegionConstraintData<'tcx>) -> R, + ) -> R { + let region_constraints = self.borrow_region_constraints(); + op(region_constraints.data()) + } + /// Takes ownership of the list of variable regions. This implies /// that all the region constriants have already been taken, and /// hence that `resolve_regions_and_report_errors` can never be diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 36e657f78b4b2..e5461685bd470 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -99,6 +99,16 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { .push((body_id, obligation)); } + /// Trait queries just want to pass back type obligations "as is" + pub fn take_registered_region_obligations( + &self, + ) -> Vec<(ast::NodeId, RegionObligation<'tcx>)> { + ::std::mem::replace( + &mut *self.region_obligations.borrow_mut(), + vec![], + ) + } + /// Process the region obligations that must be proven (during /// `regionck`) for the given `body_id`, given information about /// the region bounds in scope and so forth. This function must be diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index ed89d1d2f57a5..0c8e49fda1840 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -350,6 +350,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> { mem::replace(data, RegionConstraintData::default()) } + pub fn data(&self) -> &RegionConstraintData<'tcx> { + &self.data + } + fn in_snapshot(&self) -> bool { !self.undo_log.is_empty() } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 7b4211e487a0e..b986445ff849d 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -175,6 +175,11 @@ pub struct PerfStats { pub decode_def_path_tables_time: Cell, /// Total number of values canonicalized queries constructed. pub queries_canonicalized: Cell, + /// Number of times we canonicalized a value and found that the + /// result had already been canonicalized. + pub canonicalized_values_allocated: Cell, + /// Number of times this query is invoked. + pub normalize_projection_ty: Cell, } /// Enum to support dispatch of one-time diagnostics (in Session.diag_once) @@ -862,6 +867,10 @@ impl Session { ); println!("Total queries canonicalized: {}", self.perf_stats.queries_canonicalized.get()); + println!("Total canonical values interned: {}", + self.perf_stats.canonicalized_values_allocated.get()); + println!("normalize_projection_ty: {}", + self.perf_stats.normalize_projection_ty.get()); } /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. @@ -1149,6 +1158,8 @@ pub fn build_session_( symbol_hash_time: Cell::new(Duration::from_secs(0)), decode_def_path_tables_time: Cell::new(Duration::from_secs(0)), queries_canonicalized: Cell::new(0), + canonicalized_values_allocated: Cell::new(0), + normalize_projection_ty: Cell::new(0), }, code_stats: RefCell::new(CodeStats::new()), optimization_fuel_crate, diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 4450c60b68d3b..3e23e3823470c 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -63,6 +63,8 @@ mod structural_impls; pub mod trans; mod util; +pub mod query; + // Whether to enable bug compatibility with issue #43355 #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum IntercrateMode { diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs new file mode 100644 index 0000000000000..bba2c1555832e --- /dev/null +++ b/src/librustc/traits/query/mod.rs @@ -0,0 +1,31 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Experimental types for the trait query interface. The methods +//! defined in this module are all based on **canonicalization**, +//! which makes a canonical query by replacing unbound inference +//! variables and regions, so that results can be reused more broadly. +//! The providers for the queries defined here can be found in +//! `librustc_traits`. + +use infer::canonical::Canonical; +use ty; + +pub mod normalize; + +pub type CanonicalProjectionGoal<'tcx> = + Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>>>; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct NoSolution; + +pub type Fallible = Result; + +impl_stable_hash_for!(struct NoSolution { }); diff --git a/src/librustc/traits/query/normalize.rs b/src/librustc/traits/query/normalize.rs new file mode 100644 index 0000000000000..030e630f23f83 --- /dev/null +++ b/src/librustc/traits/query/normalize.rs @@ -0,0 +1,268 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Code for the 'normalization' query. This consists of a wrapper +//! which folds deeply, invoking the underlying +//! `normalize_projection_ty` query when it encounters projections. + +use infer::{InferCtxt, InferOk}; +use infer::at::At; +use infer::canonical::{Canonical, Canonicalize, QueryResult}; +use middle::const_val::ConstVal; +use mir::interpret::GlobalId; +use std::rc::Rc; +use traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; +use traits::query::CanonicalProjectionGoal; +use traits::project::Normalized; +use ty::{self, Ty, TyCtxt}; +use ty::fold::{TypeFoldable, TypeFolder}; +use ty::subst::{Subst, Substs}; + +use super::NoSolution; + +impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { + /// Normalize `value` in the context of the inference context, + /// yielding a resulting type, or an error if `value` cannot be + /// normalized. If you don't care about regions, you should prefer + /// `normalize_erasing_regions`, which is more efficient. + /// + /// If the normalization succeeds and is unambigious, returns back + /// the normalized value along with various outlives relations (in + /// the form of obligations that must be discharged). + /// + /// NB. This will *eventually* be the main means of + /// normalizing, but for now should be used only when we actually + /// know that normalization will succeed, since error reporting + /// and other details are still "under development". + pub fn normalize(&self, value: &T) -> Result, NoSolution> + where + T: TypeFoldable<'tcx>, + { + let mut normalizer = QueryNormalizer { + infcx: self.infcx, + cause: self.cause, + param_env: self.param_env, + obligations: vec![], + error: false, + anon_depth: 0, + }; + if !value.has_projections() { + return Ok(Normalized { + value: value.clone(), + obligations: vec![], + }); + } + + let value1 = value.fold_with(&mut normalizer); + if normalizer.error { + Err(NoSolution) + } else { + Ok(Normalized { + value: value1, + obligations: normalizer.obligations, + }) + } + } +} + +/// Result from the `normalize_projection_ty` query. +#[derive(Clone, Debug)] +pub struct NormalizationResult<'tcx> { + /// Result of normalization. + pub normalized_ty: Ty<'tcx>, +} + +struct QueryNormalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + cause: &'cx ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + obligations: Vec>, + error: bool, + anon_depth: usize, +} + +impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for QueryNormalizer<'cx, 'gcx, 'tcx> { + fn tcx<'c>(&'c self) -> TyCtxt<'c, 'gcx, 'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + let ty = ty.super_fold_with(self); + match ty.sty { + ty::TyAnon(def_id, substs) if !substs.has_escaping_regions() => { + // (*) + // Only normalize `impl Trait` after type-checking, usually in trans. + match self.param_env.reveal { + Reveal::UserFacing => ty, + + Reveal::All => { + let recursion_limit = self.tcx().sess.recursion_limit.get(); + if self.anon_depth >= recursion_limit { + let obligation = Obligation::with_depth( + self.cause.clone(), + recursion_limit, + self.param_env, + ty, + ); + self.infcx.report_overflow_error(&obligation, true); + } + + let generic_ty = self.tcx().type_of(def_id); + let concrete_ty = generic_ty.subst(self.tcx(), substs); + self.anon_depth += 1; + let folded_ty = self.fold_ty(concrete_ty); + self.anon_depth -= 1; + folded_ty + } + } + } + + ty::TyProjection(ref data) if !data.has_escaping_regions() => { + // (*) + // (*) This is kind of hacky -- we need to be able to + // handle normalization within binders because + // otherwise we wind up a need to normalize when doing + // trait matching (since you can have a trait + // obligation like `for<'a> T::B : Fn(&'a int)`), but + // we can't normalize with bound regions in scope. So + // far now we just ignore binders but only normalize + // if all bound regions are gone (and then we still + // have to renormalize whenever we instantiate a + // binder). It would be better to normalize in a + // binding-aware fashion. + + let gcx = self.infcx.tcx.global_tcx(); + + let (c_data, orig_values) = + self.infcx.canonicalize_query(&self.param_env.and(*data)); + debug!("QueryNormalizer: c_data = {:#?}", c_data); + debug!("QueryNormalizer: orig_values = {:#?}", orig_values); + match gcx.normalize_projection_ty(c_data) { + Ok(result) => { + // We don't expect ambiguity. + if result.is_ambiguous() { + self.error = true; + return ty; + } + + match self.infcx.instantiate_query_result( + self.cause, + self.param_env, + &orig_values, + &result, + ) { + Ok(InferOk { + value: result, + obligations, + }) => { + debug!("QueryNormalizer: result = {:#?}", result); + debug!("QueryNormalizer: obligations = {:#?}", obligations); + self.obligations.extend(obligations); + return result.normalized_ty; + } + + Err(_) => { + self.error = true; + return ty; + } + } + } + + Err(NoSolution) => { + self.error = true; + ty + } + } + } + + _ => ty, + } + } + + fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + if let ConstVal::Unevaluated(def_id, substs) = constant.val { + let tcx = self.infcx.tcx.global_tcx(); + if let Some(param_env) = self.tcx().lift_to_global(&self.param_env) { + if substs.needs_infer() { + let identity_substs = Substs::identity_for_item(tcx, def_id); + let instance = ty::Instance::resolve(tcx, param_env, def_id, identity_substs); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None, + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(evaluated) => { + let evaluated = evaluated.subst(self.tcx(), substs); + return self.fold_const(evaluated); + } + Err(_) => {} + } + } + } else { + if let Some(substs) = self.tcx().lift_to_global(&substs) { + let instance = ty::Instance::resolve(tcx, param_env, def_id, substs); + if let Some(instance) = instance { + let cid = GlobalId { + instance, + promoted: None, + }; + match tcx.const_eval(param_env.and(cid)) { + Ok(evaluated) => return self.fold_const(evaluated), + Err(_) => {} + } + } + } + } + } + } + constant + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for NormalizationResult<'tcx> { + normalized_ty + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for NormalizationResult<'a> { + type Lifted = NormalizationResult<'tcx>; + normalized_ty + } +} + +impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>> { + type Canonicalized = CanonicalProjectionGoal<'gcx>; + + fn intern( + _gcx: TyCtxt<'_, 'gcx, 'gcx>, + value: Canonical<'gcx, Self::Lifted>, + ) -> Self::Canonicalized { + value + } +} + +impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for QueryResult<'tcx, NormalizationResult<'tcx>> { + // we ought to intern this, but I'm too lazy just now + type Canonicalized = Rc>>>; + + fn intern( + _gcx: TyCtxt<'_, 'gcx, 'gcx>, + value: Canonical<'gcx, Self::Lifted>, + ) -> Self::Canonicalized { + Rc::new(value) + } +} + +impl_stable_hash_for!(struct NormalizationResult<'tcx> { + normalized_ty +}); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 24d3b37f804ee..d85a95e87ea8e 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -106,6 +106,7 @@ pub struct GlobalArenas<'tcx> { tables: TypedArena>, /// miri allocations const_allocs: TypedArena, + } impl<'tcx> GlobalArenas<'tcx> { diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index 21affcbc9ede7..95865d5eab0bc 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -11,6 +11,7 @@ use dep_graph::SerializedDepNodeIndex; use hir::def_id::{CrateNum, DefId, DefIndex}; use mir::interpret::{GlobalId}; +use traits::query::CanonicalProjectionGoal; use ty::{self, Ty, TyCtxt}; use ty::subst::Substs; use ty::maps::queries; @@ -51,6 +52,15 @@ impl<'tcx, M: QueryConfig> QueryDescription<'tcx> for M { } } +impl<'tcx> QueryDescription<'tcx> for queries::normalize_projection_ty<'tcx> { + fn describe( + _tcx: TyCtxt, + goal: CanonicalProjectionGoal<'tcx>, + ) -> String { + format!("normalizing `{:?}`", goal) + } +} + impl<'tcx> QueryDescription<'tcx> for queries::is_copy_raw<'tcx> { fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { format!("computing whether `{}` is `Copy`", env.value) diff --git a/src/librustc/ty/maps/keys.rs b/src/librustc/ty/maps/keys.rs index 8fb1ad0da823b..1f040522fda98 100644 --- a/src/librustc/ty/maps/keys.rs +++ b/src/librustc/ty/maps/keys.rs @@ -11,6 +11,7 @@ //! Defines the set of legal keys that can be used in queries. use hir::def_id::{CrateNum, DefId, LOCAL_CRATE, DefIndex}; +use traits::query::CanonicalProjectionGoal; use ty::{self, Ty, TyCtxt}; use ty::subst::Substs; use ty::fast_reject::SimplifiedType; @@ -170,3 +171,13 @@ impl Key for InternedString { DUMMY_SP } } + +impl<'tcx> Key for CanonicalProjectionGoal<'tcx> { + fn map_crate(&self) -> CrateNum { + LOCAL_CRATE + } + + fn default_span(&self, _tcx: TyCtxt) -> Span { + DUMMY_SP + } +} diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 0ded759fec730..2dc6cd7a4eb41 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -14,6 +14,7 @@ use hir::def_id::{CrateNum, DefId, DefIndex}; use hir::def::{Def, Export}; use hir::{self, TraitCandidate, ItemLocalId, TransFnAttrs}; use hir::svh::Svh; +use infer::canonical::{Canonical, QueryResult}; use lint; use middle::borrowck::BorrowCheckResult; use middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary, @@ -33,6 +34,8 @@ use mir::interpret::{GlobalId}; use session::{CompileResult, CrateDisambiguator}; use session::config::OutputFilenames; use traits::Vtable; +use traits::query::{CanonicalProjectionGoal, NoSolution}; +use traits::query::normalize::NormalizationResult; use traits::specialization_graph; use ty::{self, CrateInherentImpls, Ty, TyCtxt}; use ty::steal::Steal; @@ -380,6 +383,14 @@ define_maps! { <'tcx> [] fn erase_regions_ty: erase_regions_ty(Ty<'tcx>) -> Ty<'tcx>, [] fn fully_normalize_monormophic_ty: normalize_ty_node(Ty<'tcx>) -> Ty<'tcx>, + /// Do not call this query directly: invoke `normalize` instead. + [] fn normalize_projection_ty: NormalizeProjectionTy( + CanonicalProjectionGoal<'tcx> + ) -> Result< + Lrc>>>, + NoSolution, + >, + [] fn substitute_normalize_and_test_predicates: substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool, @@ -537,6 +548,7 @@ fn output_filenames_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { fn vtable_methods_node<'tcx>(trait_ref: ty::PolyTraitRef<'tcx>) -> DepConstructor<'tcx> { DepConstructor::VtableMethods{ trait_ref } } + fn normalize_ty_node<'tcx>(_: Ty<'tcx>) -> DepConstructor<'tcx> { DepConstructor::NormalizeTy } diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 68d1088890205..2124b6296aab1 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -773,6 +773,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::VtableMethods | DepKind::EraseRegionsTy | DepKind::NormalizeTy | + DepKind::NormalizeProjectionTy | DepKind::SubstituteNormalizeAndTestPredicates | DepKind::InstanceDefSizeEstimate | diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 6a1d9e5653428..3bff79ed3a6fc 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -28,6 +28,7 @@ rustc_plugin = { path = "../librustc_plugin" } rustc_privacy = { path = "../librustc_privacy" } rustc_resolve = { path = "../librustc_resolve" } rustc_save_analysis = { path = "../librustc_save_analysis" } +rustc_traits = { path = "../librustc_traits" } rustc_trans_utils = { path = "../librustc_trans_utils" } rustc_typeck = { path = "../librustc_typeck" } serialize = { path = "../libserialize" } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 485ee1130d306..542f818c3818a 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -31,6 +31,7 @@ use rustc_incremental; use rustc_resolve::{MakeGlobMap, Resolver, ResolverArenas}; use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::{self, CStore}; +use rustc_traits; use rustc_trans_utils::trans_crate::TransCrate; use rustc_typeck as typeck; use rustc_privacy; @@ -942,6 +943,7 @@ pub fn default_provide(providers: &mut ty::maps::Providers) { traits::provide(providers); reachable::provide(providers); rustc_passes::provide(providers); + rustc_traits::provide(providers); middle::region::provide(providers); cstore::provide(providers); lint::provide(providers); diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index f6aa58213fc90..746f2db4767f9 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -46,6 +46,7 @@ extern crate rustc_metadata; extern crate rustc_mir; extern crate rustc_resolve; extern crate rustc_save_analysis; +extern crate rustc_traits; extern crate rustc_trans_utils; extern crate rustc_typeck; extern crate serialize; diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index c474cabdb3c5b..06e6be5cd56a3 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -20,7 +20,8 @@ use dataflow::move_paths::MoveData; use rustc::hir::def_id::DefId; use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; use rustc::infer::region_constraints::{GenericKind, RegionConstraintData}; -use rustc::traits::{self, FulfillmentContext}; +use rustc::traits::{self, Normalized, FulfillmentContext}; +use rustc::traits::query::NoSolution; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; @@ -1553,10 +1554,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { { debug!("normalize(value={:?}, location={:?})", value, location); self.fully_perform_op(location.at_self(), |this| { - let mut selcx = traits::SelectionContext::new(this.infcx); - let cause = this.misc(this.last_span); - let traits::Normalized { value, obligations } = - traits::normalize(&mut selcx, this.param_env, cause, value); + let Normalized { value, obligations } = this.infcx + .at(&this.misc(this.last_span), this.param_env) + .normalize(value) + .unwrap_or_else(|NoSolution| { + span_bug!( + this.last_span, + "normalization of `{:?}` failed at {:?}", + value, + location, + ); + }); Ok(InferOk { value, obligations }) }).unwrap() } diff --git a/src/librustc_traits/Cargo.toml b/src/librustc_traits/Cargo.toml new file mode 100644 index 0000000000000..dc2a21cdab256 --- /dev/null +++ b/src/librustc_traits/Cargo.toml @@ -0,0 +1,18 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_traits" +version = "0.0.0" + +[lib] +name = "rustc_traits" +path = "lib.rs" +crate-type = ["dylib"] + +[dependencies] +bitflags = "1.0" +graphviz = { path = "../libgraphviz" } +log = { version = "0.4" } +rustc = { path = "../librustc" } +rustc_data_structures = { path = "../librustc_data_structures" } +syntax = { path = "../libsyntax" } +syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs new file mode 100644 index 0000000000000..0d92404d24b08 --- /dev/null +++ b/src/librustc_traits/lib.rs @@ -0,0 +1,37 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! New recursive solver modeled on Chalk's recursive solver. Most of +//! the guts are broken up into modules; see the comments in those modules. + +#![deny(warnings)] + +#![feature(crate_visibility_modifier)] +#![feature(match_default_bindings)] +#![feature(underscore_lifetimes)] + +#[macro_use] +extern crate log; +extern crate rustc; +extern crate rustc_data_structures; +extern crate syntax; +extern crate syntax_pos; + +mod normalize_projection_ty; +mod util; + +use rustc::ty::maps::Providers; + +pub fn provide(p: &mut Providers) { + *p = Providers { + normalize_projection_ty: normalize_projection_ty::normalize_projection_ty, + ..*p + }; +} diff --git a/src/librustc_traits/normalize_projection_ty.rs b/src/librustc_traits/normalize_projection_ty.rs new file mode 100644 index 0000000000000..55785d9586cc3 --- /dev/null +++ b/src/librustc_traits/normalize_projection_ty.rs @@ -0,0 +1,55 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::infer::canonical::{Canonical, QueryResult}; +use rustc::traits::{self, FulfillmentContext, Normalized, ObligationCause, + SelectionContext}; +use rustc::traits::query::{CanonicalProjectionGoal, NoSolution, normalize::NormalizationResult}; +use rustc::ty::{ParamEnvAnd, TyCtxt}; +use rustc::util::common::CellUsizeExt; +use std::rc::Rc; +use syntax::ast::DUMMY_NODE_ID; +use syntax_pos::DUMMY_SP; +use util; + +crate fn normalize_projection_ty<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + goal: CanonicalProjectionGoal<'tcx>, +) -> Result>>>, NoSolution> { + debug!("normalize_provider(goal={:#?})", goal); + + tcx.sess.perf_stats.normalize_projection_ty.increment(); + tcx.infer_ctxt().enter(|ref infcx| { + let ( + ParamEnvAnd { + param_env, + value: goal, + }, + canonical_inference_vars, + ) = infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &goal); + let fulfill_cx = &mut FulfillmentContext::new(); + let selcx = &mut SelectionContext::new(infcx); + let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID); + let Normalized { + value: answer, + obligations, + } = traits::normalize_projection_type(selcx, param_env, goal, cause, 0); + fulfill_cx.register_predicate_obligations(infcx, obligations); + + // Now that we have fulfilled as much as we can, create a solution + // from what we've learned. + util::make_query_response( + infcx, + canonical_inference_vars, + NormalizationResult { normalized_ty: answer }, + fulfill_cx, + ) + }) +} diff --git a/src/librustc_traits/util.rs b/src/librustc_traits/util.rs new file mode 100644 index 0000000000000..976eb442a0d13 --- /dev/null +++ b/src/librustc_traits/util.rs @@ -0,0 +1,117 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::infer::InferCtxt; +use rustc::infer::canonical::{CanonicalVarValues, Canonicalize, Certainty, QueryRegionConstraints, + QueryResult}; +use rustc::infer::region_constraints::{Constraint, RegionConstraintData}; +use rustc::traits::FulfillmentContext; +use rustc::traits::query::NoSolution; +use rustc::ty; +use std::fmt::Debug; + +/// The canonicalization form of `QueryResult<'tcx, T>`. +type CanonicalizedQueryResult<'gcx, 'tcx, T> = + as Canonicalize<'gcx, 'tcx>>::Canonicalized; + +crate fn make_query_response<'gcx, 'tcx, T>( + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + inference_vars: CanonicalVarValues<'tcx>, + answer: T, + fulfill_cx: &mut FulfillmentContext<'tcx>, +) -> Result, NoSolution> +where + T: Debug, + QueryResult<'tcx, T>: Canonicalize<'gcx, 'tcx>, +{ + let tcx = infcx.tcx; + + debug!( + "make_query_response(\ + inference_vars={:?}, \ + answer={:?})", + inference_vars, answer, + ); + + // Select everything, returning errors. + let true_errors = match fulfill_cx.select_where_possible(infcx) { + Ok(()) => vec![], + Err(errors) => errors, + }; + debug!("true_errors = {:#?}", true_errors); + + if !true_errors.is_empty() { + // FIXME -- we don't indicate *why* we failed to solve + debug!("make_query_response: true_errors={:#?}", true_errors); + return Err(NoSolution); + } + + // Anything left unselected *now* must be an ambiguity. + let ambig_errors = match fulfill_cx.select_all_or_error(infcx) { + Ok(()) => vec![], + Err(errors) => errors, + }; + debug!("ambig_errors = {:#?}", ambig_errors); + + let region_obligations = infcx.take_registered_region_obligations(); + + let (region_outlives, ty_outlives) = infcx.with_region_constraints(|region_constraints| { + let RegionConstraintData { + constraints, + verifys, + givens, + } = region_constraints; + + assert!(verifys.is_empty()); + assert!(givens.is_empty()); + + let region_outlives: Vec<_> = constraints + .into_iter() + .map(|(k, _)| match *k { + Constraint::VarSubVar(v1, v2) => { + (tcx.mk_region(ty::ReVar(v1)), tcx.mk_region(ty::ReVar(v2))) + } + Constraint::VarSubReg(v1, r2) => (tcx.mk_region(ty::ReVar(v1)), r2), + Constraint::RegSubVar(r1, v2) => (r1, tcx.mk_region(ty::ReVar(v2))), + Constraint::RegSubReg(r1, r2) => (r1, r2), + }) + .collect(); + + let ty_outlives: Vec<_> = region_obligations + .into_iter() + .map(|(_, r_o)| (r_o.sup_type, r_o.sub_region)) + .collect(); + + (region_outlives, ty_outlives) + }); + + let certainty = if ambig_errors.is_empty() { + Certainty::Proven + } else { + Certainty::Ambiguous + }; + + let (canonical_result, _) = infcx.canonicalize_response(&QueryResult { + var_values: inference_vars, + region_constraints: QueryRegionConstraints { + region_outlives, + ty_outlives, + }, + certainty, + value: answer, + }); + + debug!( + "make_query_response: canonical_result = {:#?}", + canonical_result + ); + + Ok(canonical_result) +} From ca87d24467c46c07961f1b6450dabfb9674913da Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Feb 2018 10:55:16 -0500 Subject: [PATCH 15/27] introduce `infcx.at(..).dropck_outlives(..)` operaton [VIC] Backed by a canonicalized query. This computes all the types/regions that need to be live when the destructor runs (i.e., that the dtor may access). --- src/librustc/dep_graph/dep_node.rs | 4 +- src/librustc/ich/impls_ty.rs | 6 - src/librustc/traits/query/dropck_outlives.rs | 193 ++++++++++++ src/librustc/traits/query/mod.rs | 5 +- src/librustc/ty/context.rs | 1 - src/librustc/ty/maps/config.rs | 8 +- src/librustc/ty/maps/keys.rs | 12 +- src/librustc/ty/maps/mod.rs | 15 +- src/librustc/ty/maps/plumbing.rs | 1 + src/librustc/ty/maps/values.rs | 6 - src/librustc/ty/mod.rs | 80 +---- src/librustc/ty/util.rs | 95 +----- .../borrow_check/nll/type_check/liveness.rs | 91 ++---- src/librustc_traits/dropck_outlives.rs | 285 ++++++++++++++++++ src/librustc_traits/lib.rs | 3 + src/librustc_typeck/check/dropck.rs | 50 +-- src/librustc_typeck/check/regionck.rs | 6 +- 17 files changed, 560 insertions(+), 301 deletions(-) create mode 100644 src/librustc/traits/query/dropck_outlives.rs create mode 100644 src/librustc_traits/dropck_outlives.rs diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index caef3a8acc44c..130fbc192677f 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -70,7 +70,7 @@ use rustc_data_structures::stable_hasher::{StableHasher, HashStable}; use std::fmt; use std::hash::Hash; use syntax_pos::symbol::InternedString; -use traits::query::CanonicalProjectionGoal; +use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal}; use ty::{TyCtxt, Instance, InstanceDef, ParamEnv, ParamEnvAnd, PolyTraitRef, Ty}; use ty::subst::Substs; @@ -637,6 +637,8 @@ define_dep_nodes!( <'tcx> [input] OutputFilenames, [anon] NormalizeTy, [] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>), + [] NormalizeTyAfterErasingRegions(ParamEnvAnd<'tcx, Ty<'tcx>>), + [] DropckOutlives(CanonicalTyGoal<'tcx>), [] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) }, diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index ae67d592ba30c..4eb4f0edafe40 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -1017,12 +1017,6 @@ impl_stable_hash_for!(struct ty::Destructor { did }); -impl_stable_hash_for!(struct ty::DtorckConstraint<'tcx> { - outlives, - dtorck_types -}); - - impl<'a> HashStable> for ty::CrateVariancesMap { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs new file mode 100644 index 0000000000000..5c964f6559a3f --- /dev/null +++ b/src/librustc/traits/query/dropck_outlives.rs @@ -0,0 +1,193 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use infer::at::At; +use infer::canonical::{Canonical, Canonicalize, QueryResult}; +use infer::InferOk; +use std::iter::FromIterator; +use traits::query::CanonicalTyGoal; +use ty::{self, Ty, TyCtxt}; +use ty::subst::Kind; +use std::rc::Rc; + +impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { + /// Given a type `ty` of some value being dropped, computes a set + /// of "kinds" (types, regions) that must be outlive the execution + /// of the destructor. These basically correspond to data that the + /// destructor might access. This is used during regionck to + /// impose "outlives" constraints on any lifetimes referenced + /// within. + /// + /// The rules here are given by the "dropck" RFCs, notably [#1238] + /// and [#1327]. This is a fixed-point computation, where we + /// explore all the data that will be dropped (transitively) when + /// a value of type `ty` is dropped. For each type T that will be + /// dropped and which has a destructor, we must assume that all + /// the types/regions of T are live during the destructor, unless + /// they are marked with a special attribute (`#[may_dangle]`). + /// + /// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md + /// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md + pub fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>> { + debug!( + "dropck_outlives(ty={:?}, param_env={:?})", + ty, self.param_env, + ); + + let tcx = self.infcx.tcx; + let gcx = tcx.global_tcx(); + let (c_ty, orig_values) = self.infcx.canonicalize_query(&self.param_env.and(ty)); + let span = self.cause.span; + match &gcx.dropck_outlives(c_ty) { + Ok(result) if result.is_proven() => { + match self.infcx.instantiate_query_result( + self.cause, + self.param_env, + &orig_values, + result, + ) { + Ok(InferOk { + value: DropckOutlivesResult { kinds, overflows }, + obligations, + }) => { + for overflow_ty in overflows.into_iter().take(1) { + let mut err = struct_span_err!( + tcx.sess, + span, + E0320, + "overflow while adding drop-check rules for {}", + self.infcx.resolve_type_vars_if_possible(&ty), + ); + err.note(&format!("overflowed on {}", overflow_ty)); + err.emit(); + } + + return InferOk { + value: kinds, + obligations, + }; + } + + Err(_) => { /* fallthrough to error-handling code below */ } + } + } + + _ => { /* fallthrough to error-handling code below */ } + } + + // Errors and ambiuity in dropck occur in two cases: + // - unresolved inference variables at the end of typeck + // - non well-formed types where projections cannot be resolved + // Either of these should hvae created an error before. + tcx.sess + .delay_span_bug(span, "dtorck encountered internal error"); + return InferOk { + value: vec![], + obligations: vec![], + }; + } +} + +#[derive(Clone, Debug)] +pub struct DropckOutlivesResult<'tcx> { + pub kinds: Vec>, + pub overflows: Vec>, +} + +/// A set of constraints that need to be satisfied in order for +/// a type to be valid for destruction. +#[derive(Clone, Debug)] +pub struct DtorckConstraint<'tcx> { + /// Types that are required to be alive in order for this + /// type to be valid for destruction. + pub outlives: Vec>, + + /// Types that could not be resolved: projections and params. + pub dtorck_types: Vec>, + + /// If, during the computation of the dtorck constraint, we + /// overflow, that gets recorded here. The caller is expected to + /// report an error. + pub overflows: Vec>, +} + +impl<'tcx> DtorckConstraint<'tcx> { + pub fn empty() -> DtorckConstraint<'tcx> { + DtorckConstraint { + outlives: vec![], + dtorck_types: vec![], + overflows: vec![], + } + } +} + +impl<'tcx> FromIterator> for DtorckConstraint<'tcx> { + fn from_iter>>(iter: I) -> Self { + let mut result = Self::empty(); + + for DtorckConstraint { + outlives, + dtorck_types, + overflows, + } in iter + { + result.outlives.extend(outlives); + result.dtorck_types.extend(dtorck_types); + result.overflows.extend(overflows); + } + + result + } +} +impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for ty::ParamEnvAnd<'tcx, Ty<'tcx>> { + type Canonicalized = CanonicalTyGoal<'gcx>; + + fn intern( + _gcx: TyCtxt<'_, 'gcx, 'gcx>, + value: Canonical<'gcx, Self::Lifted>, + ) -> Self::Canonicalized { + value + } +} + +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for DropckOutlivesResult<'tcx> { + kinds, overflows + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for DropckOutlivesResult<'a> { + type Lifted = DropckOutlivesResult<'tcx>; + kinds, overflows + } +} + +impl_stable_hash_for!(struct DropckOutlivesResult<'tcx> { + kinds, overflows +}); + +impl<'gcx: 'tcx, 'tcx> Canonicalize<'gcx, 'tcx> for QueryResult<'tcx, DropckOutlivesResult<'tcx>> { + // we ought to intern this, but I'm too lazy just now + type Canonicalized = Rc>>>; + + fn intern( + _gcx: TyCtxt<'_, 'gcx, 'gcx>, + value: Canonical<'gcx, Self::Lifted>, + ) -> Self::Canonicalized { + Rc::new(value) + } +} + +impl_stable_hash_for!(struct DtorckConstraint<'tcx> { + outlives, + dtorck_types, + overflows +}); diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index bba2c1555832e..607344f9c6781 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -16,13 +16,16 @@ //! `librustc_traits`. use infer::canonical::Canonical; -use ty; +use ty::{self, Ty}; +pub mod dropck_outlives; pub mod normalize; pub type CanonicalProjectionGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>>>; +pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct NoSolution; diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index d85a95e87ea8e..24d3b37f804ee 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -106,7 +106,6 @@ pub struct GlobalArenas<'tcx> { tables: TypedArena>, /// miri allocations const_allocs: TypedArena, - } impl<'tcx> GlobalArenas<'tcx> { diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index 95865d5eab0bc..bcd6a5ace627d 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -11,7 +11,7 @@ use dep_graph::SerializedDepNodeIndex; use hir::def_id::{CrateNum, DefId, DefIndex}; use mir::interpret::{GlobalId}; -use traits::query::CanonicalProjectionGoal; +use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal}; use ty::{self, Ty, TyCtxt}; use ty::subst::Substs; use ty::maps::queries; @@ -61,6 +61,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::normalize_projection_ty<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::dropck_outlives<'tcx> { + fn describe(_tcx: TyCtxt, goal: CanonicalTyGoal<'tcx>) -> String { + format!("computing dropck types for `{:?}`", goal) + } +} + impl<'tcx> QueryDescription<'tcx> for queries::is_copy_raw<'tcx> { fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { format!("computing whether `{}` is `Copy`", env.value) diff --git a/src/librustc/ty/maps/keys.rs b/src/librustc/ty/maps/keys.rs index 1f040522fda98..b8167ec918638 100644 --- a/src/librustc/ty/maps/keys.rs +++ b/src/librustc/ty/maps/keys.rs @@ -11,7 +11,7 @@ //! Defines the set of legal keys that can be used in queries. use hir::def_id::{CrateNum, DefId, LOCAL_CRATE, DefIndex}; -use traits::query::CanonicalProjectionGoal; +use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal}; use ty::{self, Ty, TyCtxt}; use ty::subst::Substs; use ty::fast_reject::SimplifiedType; @@ -181,3 +181,13 @@ impl<'tcx> Key for CanonicalProjectionGoal<'tcx> { DUMMY_SP } } + +impl<'tcx> Key for CanonicalTyGoal<'tcx> { + fn map_crate(&self) -> CrateNum { + LOCAL_CRATE + } + + fn default_span(&self, _tcx: TyCtxt) -> Span { + DUMMY_SP + } +} diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 2dc6cd7a4eb41..4df15b2e76be8 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -34,7 +34,8 @@ use mir::interpret::{GlobalId}; use session::{CompileResult, CrateDisambiguator}; use session::config::OutputFilenames; use traits::Vtable; -use traits::query::{CanonicalProjectionGoal, NoSolution}; +use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal, NoSolution}; +use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult}; use traits::query::normalize::NormalizationResult; use traits::specialization_graph; use ty::{self, CrateInherentImpls, Ty, TyCtxt}; @@ -114,7 +115,9 @@ define_maps! { <'tcx> [] fn adt_def: AdtDefOfItem(DefId) -> &'tcx ty::AdtDef, [] fn adt_destructor: AdtDestructor(DefId) -> Option, [] fn adt_sized_constraint: SizedConstraint(DefId) -> &'tcx [Ty<'tcx>], - [] fn adt_dtorck_constraint: DtorckConstraint(DefId) -> ty::DtorckConstraint<'tcx>, + [] fn adt_dtorck_constraint: DtorckConstraint( + DefId + ) -> Result, NoSolution>, /// True if this is a const fn [] fn is_const_fn: IsConstFn(DefId) -> bool, @@ -391,6 +394,14 @@ define_maps! { <'tcx> NoSolution, >, + /// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead. + [] fn dropck_outlives: DropckOutlives( + CanonicalTyGoal<'tcx> + ) -> Result< + Lrc>>>, + NoSolution, + >, + [] fn substitute_normalize_and_test_predicates: substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool, diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 2124b6296aab1..64b17922049a1 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -774,6 +774,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::EraseRegionsTy | DepKind::NormalizeTy | DepKind::NormalizeProjectionTy | + DepKind::DropckOutlives | DepKind::SubstituteNormalizeAndTestPredicates | DepKind::InstanceDefSizeEstimate | diff --git a/src/librustc/ty/maps/values.rs b/src/librustc/ty/maps/values.rs index 165798d19f196..8d38d7dbbbbff 100644 --- a/src/librustc/ty/maps/values.rs +++ b/src/librustc/ty/maps/values.rs @@ -35,12 +35,6 @@ impl<'tcx> Value<'tcx> for Ty<'tcx> { } } -impl<'tcx> Value<'tcx> for ty::DtorckConstraint<'tcx> { - fn from_cycle_error<'a>(_: TyCtxt<'a, 'tcx, 'tcx>) -> Self { - Self::empty() - } -} - impl<'tcx> Value<'tcx> for ty::SymbolName { fn from_cycle_error<'a>(_: TyCtxt<'a, 'tcx, 'tcx>) -> Self { ty::SymbolName { name: Symbol::intern("").as_str() } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 93d1585cdc83d..fc1d26b0e0910 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -34,15 +34,13 @@ use ty; use ty::subst::{Subst, Substs}; use ty::util::{IntTypeExt, Discr}; use ty::walk::TypeWalker; -use util::common::ErrorReported; -use util::nodemap::{NodeSet, DefIdMap, FxHashMap, FxHashSet}; +use util::nodemap::{NodeSet, DefIdMap, FxHashMap}; use serialize::{self, Encodable, Encoder}; use std::cell::RefCell; use std::cmp; use std::fmt; use std::hash::{Hash, Hasher}; -use std::iter::FromIterator; use std::ops::Deref; use rustc_data_structures::sync::Lrc; use std::slice; @@ -2661,38 +2659,6 @@ fn adt_sized_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, result } -/// Calculates the dtorck constraint for a type. -fn adt_dtorck_constraint<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> DtorckConstraint<'tcx> { - let def = tcx.adt_def(def_id); - let span = tcx.def_span(def_id); - debug!("dtorck_constraint: {:?}", def); - - if def.is_phantom_data() { - let result = DtorckConstraint { - outlives: vec![], - dtorck_types: vec![ - tcx.mk_param_from_def(&tcx.generics_of(def_id).types[0]) - ] - }; - debug!("dtorck_constraint: {:?} => {:?}", def, result); - return result; - } - - let mut result = def.all_fields() - .map(|field| tcx.type_of(field.did)) - .map(|fty| tcx.dtorck_constraint_for_ty(span, fty, 0, fty)) - .collect::>() - .unwrap_or(DtorckConstraint::empty()); - result.outlives.extend(tcx.destructor_constraints(def)); - result.dedup(); - - debug!("dtorck_constraint: {:?} => {:?}", def, result); - - result -} - fn associated_item_def_ids<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Lrc> { @@ -2808,7 +2774,6 @@ pub fn provide(providers: &mut ty::maps::Providers) { associated_item, associated_item_def_ids, adt_sized_constraint, - adt_dtorck_constraint, def_span, param_env, trait_of_item, @@ -2831,49 +2796,6 @@ pub struct CrateInherentImpls { pub inherent_impls: DefIdMap>>, } -/// A set of constraints that need to be satisfied in order for -/// a type to be valid for destruction. -#[derive(Clone, Debug)] -pub struct DtorckConstraint<'tcx> { - /// Types that are required to be alive in order for this - /// type to be valid for destruction. - pub outlives: Vec>, - /// Types that could not be resolved: projections and params. - pub dtorck_types: Vec>, -} - -impl<'tcx> FromIterator> for DtorckConstraint<'tcx> -{ - fn from_iter>>(iter: I) -> Self { - let mut result = Self::empty(); - - for constraint in iter { - result.outlives.extend(constraint.outlives); - result.dtorck_types.extend(constraint.dtorck_types); - } - - result - } -} - - -impl<'tcx> DtorckConstraint<'tcx> { - fn empty() -> DtorckConstraint<'tcx> { - DtorckConstraint { - outlives: vec![], - dtorck_types: vec![] - } - } - - fn dedup<'a>(&mut self) { - let mut outlives = FxHashSet(); - let mut dtorck_types = FxHashSet(); - - self.outlives.retain(|&val| outlives.replace(val).is_none()); - self.dtorck_types.retain(|&val| dtorck_types.replace(val).is_none()); - } -} - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)] pub struct SymbolName { // FIXME: we don't rely on interning or equality here - better have diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index e8ce49d39d299..753f89d8cd293 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -19,7 +19,7 @@ use middle::const_val::ConstVal; use traits; use ty::{self, Ty, TyCtxt, TypeFoldable}; use ty::fold::TypeVisitor; -use ty::subst::{Subst, UnpackedKind}; +use ty::subst::UnpackedKind; use ty::maps::TyCtxtAt; use ty::TypeVariants::*; use ty::layout::Integer; @@ -537,99 +537,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { result } - /// Return a set of constraints that needs to be satisfied in - /// order for `ty` to be valid for destruction. - pub fn dtorck_constraint_for_ty(self, - span: Span, - for_ty: Ty<'tcx>, - depth: usize, - ty: Ty<'tcx>) - -> Result, ErrorReported> - { - debug!("dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})", - span, for_ty, depth, ty); - - if depth >= self.sess.recursion_limit.get() { - let mut err = struct_span_err!( - self.sess, span, E0320, - "overflow while adding drop-check rules for {}", for_ty); - err.note(&format!("overflowed on {}", ty)); - err.emit(); - return Err(ErrorReported); - } - - let result = match ty.sty { - ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) | - ty::TyFloat(_) | ty::TyStr | ty::TyNever | ty::TyForeign(..) | - ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) | - ty::TyGeneratorWitness(..) => { - // these types never have a destructor - Ok(ty::DtorckConstraint::empty()) - } - - ty::TyArray(ety, _) | ty::TySlice(ety) => { - // single-element containers, behave like their element - self.dtorck_constraint_for_ty(span, for_ty, depth+1, ety) - } - - ty::TyTuple(tys, _) => { - tys.iter().map(|ty| { - self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty) - }).collect() - } - - ty::TyClosure(def_id, substs) => { - substs.upvar_tys(def_id, self).map(|ty| { - self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty) - }).collect() - } - - ty::TyGenerator(def_id, substs, _) => { - // Note that the interior types are ignored here. - // Any type reachable inside the interior must also be reachable - // through the upvars. - substs.upvar_tys(def_id, self).map(|ty| { - self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty) - }).collect() - } - - ty::TyAdt(def, substs) => { - let ty::DtorckConstraint { - dtorck_types, outlives - } = self.at(span).adt_dtorck_constraint(def.did); - Ok(ty::DtorckConstraint { - // FIXME: we can try to recursively `dtorck_constraint_on_ty` - // there, but that needs some way to handle cycles. - dtorck_types: dtorck_types.subst(self, substs), - outlives: outlives.subst(self, substs) - }) - } - - // Objects must be alive in order for their destructor - // to be called. - ty::TyDynamic(..) => Ok(ty::DtorckConstraint { - outlives: vec![ty.into()], - dtorck_types: vec![], - }), - - // Types that can't be resolved. Pass them forward. - ty::TyProjection(..) | ty::TyAnon(..) | ty::TyParam(..) => { - Ok(ty::DtorckConstraint { - outlives: vec![], - dtorck_types: vec![ty], - }) - } - - ty::TyInfer(..) | ty::TyError => { - self.sess.delay_span_bug(span, "unresolved type in dtorck"); - Err(ErrorReported) - } - }; - - debug!("dtorck_constraint_for_ty({:?}) = {:?}", ty, result); - result - } - pub fn is_closure(self, def_id: DefId) -> bool { self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr } diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs index 5b3f439e0ebb9..d19fd2bb5969d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -14,13 +14,9 @@ use dataflow::MaybeInitializedPlaces; use dataflow::move_paths::{HasMoveData, MoveData}; use rustc::mir::{BasicBlock, Location, Mir}; use rustc::mir::Local; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::traits; +use rustc::ty::{Ty, TyCtxt, TypeFoldable}; use rustc::infer::InferOk; -use rustc::util::common::ErrorReported; use borrow_check::nll::type_check::AtLocation; -use rustc_data_structures::fx::FxHashSet; -use syntax::codemap::DUMMY_SP; use util::liveness::LivenessResults; use super::TypeChecker; @@ -193,73 +189,34 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo // // For this reason, we avoid calling TypeChecker.normalize, instead doing all normalization // ourselves in one large 'fully_perform_op' callback. - let (type_constraints, kind_constraints) = self.cx.fully_perform_op(location.at_self(), - |cx| { - - let tcx = cx.infcx.tcx; - let mut selcx = traits::SelectionContext::new(cx.infcx); - let cause = cx.misc(cx.last_span); - - let mut types = vec![(dropped_ty, 0)]; - let mut final_obligations = Vec::new(); - let mut type_constraints = Vec::new(); - let mut kind_constraints = Vec::new(); - - let mut known = FxHashSet(); - - while let Some((ty, depth)) = types.pop() { - let span = DUMMY_SP; // FIXME - let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) { - Ok(result) => result, - Err(ErrorReported) => { - continue; - } - }; - - let ty::DtorckConstraint { - outlives, - dtorck_types, - } = result; - - // All things in the `outlives` array may be touched by - // the destructor and must be live at this point. - for outlive in outlives { + let kind_constraints = self.cx + .fully_perform_op(location.at_self(), |cx| { + let span = cx.last_span; + + let mut final_obligations = Vec::new(); + let mut kind_constraints = Vec::new(); + + let InferOk { + value: kinds, + obligations, + } = cx.infcx + .at(&cx.misc(span), cx.param_env) + .dropck_outlives(dropped_ty); + for kind in kinds { + // All things in the `outlives` array may be touched by + // the destructor and must be live at this point. let cause = Cause::DropVar(dropped_local, location); - kind_constraints.push((outlive, location, cause)); + kind_constraints.push((kind, location, cause)); } - // However, there may also be some types that - // `dtorck_constraint_for_ty` could not resolve (e.g., - // associated types and parameters). We need to normalize - // associated types here and possibly recursively process. - for ty in dtorck_types { - let traits::Normalized { value: ty, obligations } = - traits::normalize(&mut selcx, cx.param_env, cause.clone(), &ty); - - final_obligations.extend(obligations); + final_obligations.extend(obligations); - let ty = cx.infcx.resolve_type_and_region_vars_if_possible(&ty); - match ty.sty { - ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => { - let cause = Cause::DropVar(dropped_local, location); - type_constraints.push((ty, location, cause)); - } - - _ => if known.insert(ty) { - types.push((ty, depth + 1)); - }, - } - } - } - - Ok(InferOk { - value: (type_constraints, kind_constraints), obligations: final_obligations + Ok(InferOk { + value: kind_constraints, + obligations: final_obligations, + }) }) - }).unwrap(); - - for (ty, location, cause) in type_constraints { - self.push_type_live_constraint(ty, location, cause); - } + .unwrap(); for (kind, location, cause) in kind_constraints { self.push_type_live_constraint(kind, location, cause); diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs new file mode 100644 index 0000000000000..2274f3942bdbc --- /dev/null +++ b/src/librustc_traits/dropck_outlives.rs @@ -0,0 +1,285 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::infer::canonical::{Canonical, QueryResult}; +use rustc::hir::def_id::DefId; +use rustc::traits::{FulfillmentContext, Normalized, ObligationCause}; +use rustc::traits::query::{CanonicalTyGoal, NoSolution}; +use rustc::traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult}; +use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use rustc::ty::subst::Subst; +use rustc::util::nodemap::FxHashSet; +use std::rc::Rc; +use syntax::codemap::{Span, DUMMY_SP}; +use util; + +crate fn dropck_outlives<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + goal: CanonicalTyGoal<'tcx>, +) -> Result>>>, NoSolution> { + debug!("dropck_outlives(goal={:#?})", goal); + + tcx.infer_ctxt().enter(|ref infcx| { + let tcx = infcx.tcx; + let ( + ParamEnvAnd { + param_env, + value: for_ty, + }, + canonical_inference_vars, + ) = infcx.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &goal); + + let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; + + // A stack of types left to process. Each round, we pop + // something from the stack and invoke + // `dtorck_constraint_for_ty`. This may produce new types that + // have to be pushed on the stack. This continues until we have explored + // all the reachable types from the type `for_ty`. + // + // Example: Imagine that we have the following code: + // + // ```rust + // struct A { + // value: B, + // children: Vec, + // } + // + // struct B { + // value: u32 + // } + // + // fn f() { + // let a: A = ...; + // .. + // } // here, `a` is dropped + // ``` + // + // at the point where `a` is dropped, we need to figure out + // which types inside of `a` contain region data that may be + // accessed by any destructors in `a`. We begin by pushing `A` + // onto the stack, as that is the type of `a`. We will then + // invoke `dtorck_constraint_for_ty` which will expand `A` + // into the types of its fields `(B, Vec)`. These will get + // pushed onto the stack. Eventually, expanding `Vec` will + // lead to us trying to push `A` a second time -- to prevent + // infinite recusion, we notice that `A` was already pushed + // once and stop. + let mut ty_stack = vec![(for_ty, 0)]; + + // Set used to detect infinite recursion. + let mut ty_set = FxHashSet(); + + let fulfill_cx = &mut FulfillmentContext::new(); + + let cause = ObligationCause::dummy(); + while let Some((ty, depth)) = ty_stack.pop() { + let DtorckConstraint { + dtorck_types, + outlives, + overflows, + } = dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty)?; + + // "outlives" represent types/regions that may be touched + // by a destructor. + result.kinds.extend(outlives); + result.overflows.extend(overflows); + + // dtorck types are "types that will get dropped but which + // do not themselves define a destructor", more or less. We have + // to push them onto the stack to be expanded. + for ty in dtorck_types { + match infcx.at(&cause, param_env).normalize(&ty) { + Ok(Normalized { + value: ty, + obligations, + }) => { + fulfill_cx.register_predicate_obligations(infcx, obligations); + + debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); + + match ty.sty { + // All parameters live for the duration of the + // function. + ty::TyParam(..) => {} + + // A projection that we couldn't resolve - it + // might have a destructor. + ty::TyProjection(..) | ty::TyAnon(..) => { + result.kinds.push(ty.into()); + } + + _ => { + if ty_set.insert(ty) { + ty_stack.push((ty, depth + 1)); + } + } + } + } + + // We don't actually expect to fail to normalize. + // That implies a WF error somewhere else. + Err(NoSolution) => { + return Err(NoSolution); + } + } + } + } + + debug!("dropck_outlives: result = {:#?}", result); + + util::make_query_response(infcx, canonical_inference_vars, result, fulfill_cx) + }) +} + +/// Return a set of constraints that needs to be satisfied in +/// order for `ty` to be valid for destruction. +fn dtorck_constraint_for_ty<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + span: Span, + for_ty: Ty<'tcx>, + depth: usize, + ty: Ty<'tcx>, +) -> Result, NoSolution> { + debug!( + "dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})", + span, for_ty, depth, ty + ); + + if depth >= tcx.sess.recursion_limit.get() { + return Ok(DtorckConstraint { + outlives: vec![], + dtorck_types: vec![], + overflows: vec![ty], + }); + } + + let result = match ty.sty { + ty::TyBool + | ty::TyChar + | ty::TyInt(_) + | ty::TyUint(_) + | ty::TyFloat(_) + | ty::TyStr + | ty::TyNever + | ty::TyForeign(..) + | ty::TyRawPtr(..) + | ty::TyRef(..) + | ty::TyFnDef(..) + | ty::TyFnPtr(_) + | ty::TyGeneratorWitness(..) => { + // these types never have a destructor + Ok(DtorckConstraint::empty()) + } + + ty::TyArray(ety, _) | ty::TySlice(ety) => { + // single-element containers, behave like their element + dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety) + } + + ty::TyTuple(tys, _) => tys.iter() + .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty)) + .collect(), + + ty::TyClosure(def_id, substs) => substs + .upvar_tys(def_id, tcx) + .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty)) + .collect(), + + ty::TyGenerator(def_id, substs, _) => { + // Note that the interior types are ignored here. + // Any type reachable inside the interior must also be reachable + // through the upvars. + substs + .upvar_tys(def_id, tcx) + .map(|ty| dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty)) + .collect() + } + + ty::TyAdt(def, substs) => { + let DtorckConstraint { + dtorck_types, + outlives, + overflows, + } = tcx.at(span).adt_dtorck_constraint(def.did)?; + Ok(DtorckConstraint { + // FIXME: we can try to recursively `dtorck_constraint_on_ty` + // there, but that needs some way to handle cycles. + dtorck_types: dtorck_types.subst(tcx, substs), + outlives: outlives.subst(tcx, substs), + overflows: overflows.subst(tcx, substs), + }) + } + + // Objects must be alive in order for their destructor + // to be called. + ty::TyDynamic(..) => Ok(DtorckConstraint { + outlives: vec![ty.into()], + dtorck_types: vec![], + overflows: vec![], + }), + + // Types that can't be resolved. Pass them forward. + ty::TyProjection(..) | ty::TyAnon(..) | ty::TyParam(..) => Ok(DtorckConstraint { + outlives: vec![], + dtorck_types: vec![ty], + overflows: vec![], + }), + + ty::TyInfer(..) | ty::TyError => { + // By the time this code runs, all type variables ought to + // be fully resolved. + Err(NoSolution) + } + }; + + debug!("dtorck_constraint_for_ty({:?}) = {:?}", ty, result); + result +} + +/// Calculates the dtorck constraint for a type. +crate fn adt_dtorck_constraint<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, +) -> Result, NoSolution> { + let def = tcx.adt_def(def_id); + let span = tcx.def_span(def_id); + debug!("dtorck_constraint: {:?}", def); + + if def.is_phantom_data() { + let result = DtorckConstraint { + outlives: vec![], + dtorck_types: vec![tcx.mk_param_from_def(&tcx.generics_of(def_id).types[0])], + overflows: vec![], + }; + debug!("dtorck_constraint: {:?} => {:?}", def, result); + return Ok(result); + } + + let mut result = def.all_fields() + .map(|field| tcx.type_of(field.did)) + .map(|fty| dtorck_constraint_for_ty(tcx, span, fty, 0, fty)) + .collect::>()?; + result.outlives.extend(tcx.destructor_constraints(def)); + dedup_dtorck_constraint(&mut result); + + debug!("dtorck_constraint: {:?} => {:?}", def, result); + + Ok(result) +} + +fn dedup_dtorck_constraint<'tcx>(c: &mut DtorckConstraint<'tcx>) { + let mut outlives = FxHashSet(); + let mut dtorck_types = FxHashSet(); + + c.outlives.retain(|&val| outlives.replace(val).is_none()); + c.dtorck_types + .retain(|&val| dtorck_types.replace(val).is_none()); +} diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index 0d92404d24b08..59083dcfbf0f4 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -24,6 +24,7 @@ extern crate rustc_data_structures; extern crate syntax; extern crate syntax_pos; +mod dropck_outlives; mod normalize_projection_ty; mod util; @@ -31,6 +32,8 @@ use rustc::ty::maps::Providers; pub fn provide(p: &mut Providers) { *p = Providers { + dropck_outlives: dropck_outlives::dropck_outlives, + adt_dtorck_constraint: dropck_outlives::adt_dtorck_constraint, normalize_projection_ty: normalize_projection_ty::normalize_projection_ty, ..*p }; diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index f33536227a0ba..67c9832cbf9f4 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -18,8 +18,8 @@ use rustc::ty::subst::{Subst, Substs, UnpackedKind}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::traits::{self, ObligationCause}; use util::common::ErrorReported; -use util::nodemap::FxHashSet; +use syntax::ast; use syntax_pos::Span; /// check_drop_impl confirms that the Drop implementation identified by @@ -282,6 +282,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>( rcx: &mut RegionCtxt<'a, 'gcx, 'tcx>, ty: Ty<'tcx>, span: Span, + body_id: ast::NodeId, scope: region::Scope) -> Result<(), ErrorReported> { @@ -297,46 +298,15 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>( }; let parent_scope = rcx.tcx.mk_region(ty::ReScope(parent_scope)); let origin = || infer::SubregionOrigin::SafeDestructor(span); - - let ty = rcx.fcx.resolve_type_vars_if_possible(&ty); - let for_ty = ty; - let mut types = vec![(ty, 0)]; - let mut known = FxHashSet(); - while let Some((ty, depth)) = types.pop() { - let ty::DtorckConstraint { - dtorck_types, outlives - } = rcx.tcx.dtorck_constraint_for_ty(span, for_ty, depth, ty)?; - - for ty in dtorck_types { - let ty = rcx.fcx.normalize_associated_types_in(span, &ty); - let ty = rcx.fcx.resolve_type_vars_with_obligations(ty); - let ty = rcx.fcx.resolve_type_and_region_vars_if_possible(&ty); - match ty.sty { - // All parameters live for the duration of the - // function. - ty::TyParam(..) => {} - - // A projection that we couldn't resolve - it - // might have a destructor. - ty::TyProjection(..) | ty::TyAnon(..) => { - rcx.type_must_outlive(origin(), ty, parent_scope); - } - - _ => { - if let None = known.replace(ty) { - types.push((ty, depth+1)); - } - } - } - } - - for outlive in outlives { - match outlive.unpack() { - UnpackedKind::Lifetime(lt) => rcx.sub_regions(origin(), parent_scope, lt), - UnpackedKind::Type(ty) => rcx.type_must_outlive(origin(), ty, parent_scope), - } + let cause = &ObligationCause::misc(span, body_id); + let infer_ok = rcx.infcx.at(cause, rcx.fcx.param_env).dropck_outlives(ty); + debug!("dropck_outlives = {:#?}", infer_ok); + let kinds = rcx.fcx.register_infer_ok_obligations(infer_ok); + for kind in kinds { + match kind.unpack() { + UnpackedKind::Lifetime(r) => rcx.sub_regions(origin(), parent_scope, r), + UnpackedKind::Type(ty) => rcx.type_must_outlive(origin(), ty, parent_scope), } } - Ok(()) } diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index cfe8aa99bfa08..9ed4ab45a1ba7 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -411,8 +411,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_of_node_must_outlive(origin, hir_id, var_region); let typ = self.resolve_node_type(hir_id); + let body_id = self.body_id; let _ = dropck::check_safety_of_destructor_if_necessary( - self, typ, span, var_scope); + self, typ, span, body_id, var_scope); }) } } @@ -884,8 +885,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { match *region { ty::ReScope(rvalue_scope) => { let typ = self.resolve_type(cmt.ty); + let body_id = self.body_id; let _ = dropck::check_safety_of_destructor_if_necessary( - self, typ, span, rvalue_scope); + self, typ, span, body_id, rvalue_scope); } ty::ReStatic => {} _ => { From 211d9ad7db19fcb23f0200786595b8b170382609 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Feb 2018 11:24:13 -0500 Subject: [PATCH 16/27] introduce `tcx.normalize_erasing_regions(..)` operaton [VIC] --- src/librustc/session/mod.rs | 5 ++ src/librustc/traits/query/mod.rs | 1 + .../traits/query/normalize_erasing_regions.rs | 81 +++++++++++++++++++ src/librustc/ty/maps/config.rs | 8 +- src/librustc/ty/maps/mod.rs | 7 +- src/librustc/ty/maps/plumbing.rs | 3 +- src/librustc_traits/lib.rs | 4 + .../normalize_erasing_regions.rs | 37 +++++++++ 8 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 src/librustc/traits/query/normalize_erasing_regions.rs create mode 100644 src/librustc_traits/normalize_erasing_regions.rs diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index b986445ff849d..3f52ecfc0999b 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -179,6 +179,8 @@ pub struct PerfStats { /// result had already been canonicalized. pub canonicalized_values_allocated: Cell, /// Number of times this query is invoked. + pub normalize_ty_after_erasing_regions: Cell, + /// Number of times this query is invoked. pub normalize_projection_ty: Cell, } @@ -869,6 +871,8 @@ impl Session { self.perf_stats.queries_canonicalized.get()); println!("Total canonical values interned: {}", self.perf_stats.canonicalized_values_allocated.get()); + println!("normalize_ty_after_erasing_regions: {}", + self.perf_stats.normalize_ty_after_erasing_regions.get()); println!("normalize_projection_ty: {}", self.perf_stats.normalize_projection_ty.get()); } @@ -1159,6 +1163,7 @@ pub fn build_session_( decode_def_path_tables_time: Cell::new(Duration::from_secs(0)), queries_canonicalized: Cell::new(0), canonicalized_values_allocated: Cell::new(0), + normalize_ty_after_erasing_regions: Cell::new(0), normalize_projection_ty: Cell::new(0), }, code_stats: RefCell::new(CodeStats::new()), diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index 607344f9c6781..f1f9256f82537 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -20,6 +20,7 @@ use ty::{self, Ty}; pub mod dropck_outlives; pub mod normalize; +pub mod normalize_erasing_regions; pub type CanonicalProjectionGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::ProjectionTy<'tcx>>>; diff --git a/src/librustc/traits/query/normalize_erasing_regions.rs b/src/librustc/traits/query/normalize_erasing_regions.rs new file mode 100644 index 0000000000000..d2d8da88e2de5 --- /dev/null +++ b/src/librustc/traits/query/normalize_erasing_regions.rs @@ -0,0 +1,81 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Methods for normalizing when you don't care about regions (and +//! aren't doing type inference). If either of those things don't +//! apply to you, use `infcx.normalize(...)`. +//! +//! The methods in this file use a `TypeFolder` to recursively process +//! contents, invoking the underlying +//! `normalize_ty_after_erasing_regions` query for each type found +//! within. (This underlying query is what is cached.) + +use ty::{self, Ty, TyCtxt}; +use ty::fold::{TypeFoldable, TypeFolder}; + +impl<'cx, 'tcx> TyCtxt<'cx, 'tcx, 'tcx> { + /// Erase the regions in `value` and then fully normalize all the + /// types found within. The result will also have regions erased. + /// + /// This is appropriate to use only after type-check: it assumes + /// that normalization will succeed, for example. + pub fn normalize_erasing_regions(self, param_env: ty::ParamEnv<'tcx>, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + // Erase first before we do the real query -- this keeps the + // cache from being too polluted. + let value = self.erase_regions(&value); + if !value.has_projections() { + value + } else { + value.fold_with(&mut NormalizeAfterErasingRegionsFolder { + tcx: self, + param_env: param_env, + }) + } + } + + /// If you have a `Binder`, you can do this to strip out the + /// late-bound regions and then normalize the result, yielding up + /// a `T` (with regions erased). This is appropriate when the + /// binder is being instantiated at the call site. + /// + /// NB. Currently, higher-ranked type bounds inhibit + /// normalization. Therefore, each time we erase them in + /// translation, we need to normalize the contents. + pub fn normalize_erasing_late_bound_regions( + self, + param_env: ty::ParamEnv<'tcx>, + value: &ty::Binder, + ) -> T + where + T: TypeFoldable<'tcx>, + { + assert!(!value.needs_subst()); + let value = self.erase_late_bound_regions(value); + self.normalize_erasing_regions(param_env, value) + } +} + +struct NormalizeAfterErasingRegionsFolder<'cx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'tcx, 'tcx>, + param_env: ty::ParamEnv<'tcx>, +} + +impl<'cx, 'tcx> TypeFolder<'tcx, 'tcx> for NormalizeAfterErasingRegionsFolder<'cx, 'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.tcx.normalize_ty_after_erasing_regions(self.param_env.and(ty)) + } +} diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index bcd6a5ace627d..487bcf1f96ac8 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -12,7 +12,7 @@ use dep_graph::SerializedDepNodeIndex; use hir::def_id::{CrateNum, DefId, DefIndex}; use mir::interpret::{GlobalId}; use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal}; -use ty::{self, Ty, TyCtxt}; +use ty::{self, ParamEnvAnd, Ty, TyCtxt}; use ty::subst::Substs; use ty::maps::queries; @@ -67,6 +67,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::dropck_outlives<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::normalize_ty_after_erasing_regions<'tcx> { + fn describe(_tcx: TyCtxt, goal: ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { + format!("normalizing `{:?}`", goal) + } +} + impl<'tcx> QueryDescription<'tcx> for queries::is_copy_raw<'tcx> { fn describe(_tcx: TyCtxt, env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> String { format!("computing whether `{}` is `Copy`", env.value) diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 4df15b2e76be8..15e309c13d532 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -38,7 +38,7 @@ use traits::query::{CanonicalProjectionGoal, CanonicalTyGoal, NoSolution}; use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult}; use traits::query::normalize::NormalizationResult; use traits::specialization_graph; -use ty::{self, CrateInherentImpls, Ty, TyCtxt}; +use ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; use ty::steal::Steal; use ty::subst::Substs; use util::nodemap::{DefIdSet, DefIdMap, ItemLocalSet}; @@ -394,6 +394,11 @@ define_maps! { <'tcx> NoSolution, >, + /// Do not call this query directly: invoke `normalize_erasing_regions` instead. + [] fn normalize_ty_after_erasing_regions: NormalizeTyAfterErasingRegions( + ParamEnvAnd<'tcx, Ty<'tcx>> + ) -> Ty<'tcx>, + /// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead. [] fn dropck_outlives: DropckOutlives( CanonicalTyGoal<'tcx> diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 64b17922049a1..53a3bcdba3369 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -772,8 +772,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::FulfillObligation | DepKind::VtableMethods | DepKind::EraseRegionsTy | - DepKind::NormalizeTy | DepKind::NormalizeProjectionTy | + DepKind::NormalizeTyAfterErasingRegions | + DepKind::NormalizeTy | DepKind::DropckOutlives | DepKind::SubstituteNormalizeAndTestPredicates | DepKind::InstanceDefSizeEstimate | diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index 59083dcfbf0f4..45d23a2733a2a 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -19,6 +19,7 @@ #[macro_use] extern crate log; +#[macro_use] extern crate rustc; extern crate rustc_data_structures; extern crate syntax; @@ -26,6 +27,7 @@ extern crate syntax_pos; mod dropck_outlives; mod normalize_projection_ty; +mod normalize_erasing_regions; mod util; use rustc::ty::maps::Providers; @@ -35,6 +37,8 @@ pub fn provide(p: &mut Providers) { dropck_outlives: dropck_outlives::dropck_outlives, adt_dtorck_constraint: dropck_outlives::adt_dtorck_constraint, normalize_projection_ty: normalize_projection_ty::normalize_projection_ty, + normalize_ty_after_erasing_regions: + normalize_erasing_regions::normalize_ty_after_erasing_regions, ..*p }; } diff --git a/src/librustc_traits/normalize_erasing_regions.rs b/src/librustc_traits/normalize_erasing_regions.rs new file mode 100644 index 0000000000000..805bf1030b3f6 --- /dev/null +++ b/src/librustc_traits/normalize_erasing_regions.rs @@ -0,0 +1,37 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::traits::{Normalized, ObligationCause}; +use rustc::traits::query::NoSolution; +use rustc::ty::{ParamEnvAnd, Ty, TyCtxt}; +use rustc::util::common::CellUsizeExt; + +crate fn normalize_ty_after_erasing_regions<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + goal: ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> Ty<'tcx> { + let ParamEnvAnd { param_env, value } = goal; + tcx.sess.perf_stats.normalize_ty_after_erasing_regions.increment(); + tcx.infer_ctxt().enter(|infcx| { + let cause = ObligationCause::dummy(); + match infcx.at(&cause, param_env).normalize(&value) { + Ok(Normalized { value: normalized_value, obligations: _ }) => { + // ^^^^^^^^^^^ + // We don't care about the `obligations`, + // they are always only region relations, + // and we are about to erase those anyway. + let normalized_value = infcx.resolve_type_vars_if_possible(&normalized_value); + let normalized_value = infcx.tcx.erase_regions(&normalized_value); + tcx.lift_to_global(&normalized_value).unwrap() + } + Err(NoSolution) => bug!("could not fully normalize `{:?}`", value), + } + }) +} From e4728e494e50a3c07ff3a7d3f16369903ac70d49 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 3 Mar 2018 08:23:28 -0500 Subject: [PATCH 17/27] transition various normalization functions to the new methods In particular: - `fully_normalize_monormophic_ty` => `normalize_erasing_regions` - `normalize_associated_type_in_env` => `normalize_erasing_regions` - `fully_normalize_associated_types_in` => `normalize_erasing_regions` - `erase_late_bound_regions_and_normalize` => `normalize_erasing_late_bound_regions` --- src/librustc/dep_graph/dep_node.rs | 1 - src/librustc/infer/mod.rs | 136 +------------------ src/librustc/traits/mod.rs | 5 +- src/librustc/traits/trans/mod.rs | 80 +---------- src/librustc/ty/context.rs | 3 - src/librustc/ty/instance.rs | 2 +- src/librustc/ty/layout.rs | 12 +- src/librustc/ty/maps/config.rs | 6 - src/librustc/ty/maps/mod.rs | 5 - src/librustc/ty/maps/plumbing.rs | 1 - src/librustc_lint/types.rs | 19 +-- src/librustc_mir/borrow_check/mod.rs | 2 +- src/librustc_mir/interpret/eval_context.rs | 6 +- src/librustc_mir/interpret/terminator/mod.rs | 15 +- src/librustc_mir/monomorphize/item.rs | 5 +- src/librustc_mir/monomorphize/mod.rs | 2 +- src/librustc_mir/shim.rs | 9 +- src/librustc_mir/util/elaborate_drops.rs | 9 +- src/librustc_trans/abi.rs | 2 +- src/librustc_trans/base.rs | 2 +- src/librustc_trans/debuginfo/metadata.rs | 9 +- src/librustc_trans/debuginfo/mod.rs | 6 +- src/librustc_trans/debuginfo/type_names.rs | 8 +- src/librustc_trans/declare.rs | 4 +- src/librustc_trans/intrinsic.rs | 7 +- src/librustc_trans/mir/block.rs | 10 +- src/librustc_trans/mir/mod.rs | 3 +- src/librustc_trans/type_of.rs | 5 +- 28 files changed, 91 insertions(+), 283 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 130fbc192677f..8d7fef90b754e 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -635,7 +635,6 @@ define_dep_nodes!( <'tcx> [] CodegenUnit(InternedString), [] CompileCodegenUnit(InternedString), [input] OutputFilenames, - [anon] NormalizeTy, [] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>), [] NormalizeTyAfterErasingRegions(ParamEnvAnd<'tcx, Ty<'tcx>>), [] DropckOutlives(CanonicalTyGoal<'tcx>), diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 2c58e17b2833c..217a157cb33ee 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -21,7 +21,6 @@ use hir::def_id::DefId; use middle::free_region::RegionRelations; use middle::region; use middle::lang_items; -use mir::tcx::PlaceTy; use ty::subst::Substs; use ty::{TyVid, IntVid, FloatVid}; use ty::{self, Ty, TyCtxt}; @@ -35,7 +34,7 @@ use std::collections::BTreeMap; use std::fmt; use syntax::ast; use errors::DiagnosticBuilder; -use syntax_pos::{self, Span, DUMMY_SP}; +use syntax_pos::{self, Span}; use util::nodemap::FxHashMap; use arena::DroplessArena; @@ -493,140 +492,7 @@ pub struct CombinedSnapshot<'a, 'tcx:'a> { _in_progress_tables: Option>>, } -/// Helper trait for shortening the lifetimes inside a -/// value for post-type-checking normalization. -/// -/// This trait offers a normalization method where the inputs and -/// outputs both have the `'gcx` lifetime; the implementations -/// internally create inference contexts and/or lift as needed. -pub trait TransNormalize<'gcx>: TypeFoldable<'gcx> { - fn trans_normalize<'a, 'tcx>(&self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) - -> Self; -} - -macro_rules! items { ($($item:item)+) => ($($item)+) } -macro_rules! impl_trans_normalize { - ($lt_gcx:tt, $($ty:ty),+) => { - items!($(impl<$lt_gcx> TransNormalize<$lt_gcx> for $ty { - fn trans_normalize<'a, 'tcx>(&self, - infcx: &InferCtxt<'a, $lt_gcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) - -> Self { - infcx.normalize_projections_in(param_env, self) - } - })+); - } -} - -impl_trans_normalize!('gcx, - Ty<'gcx>, - &'gcx ty::Const<'gcx>, - &'gcx Substs<'gcx>, - ty::FnSig<'gcx>, - ty::PolyFnSig<'gcx>, - ty::ClosureSubsts<'gcx>, - ty::PolyTraitRef<'gcx>, - ty::ExistentialTraitRef<'gcx> -); - -impl<'gcx> TransNormalize<'gcx> for PlaceTy<'gcx> { - fn trans_normalize<'a, 'tcx>(&self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>) - -> Self { - match *self { - PlaceTy::Ty { ty } => PlaceTy::Ty { ty: ty.trans_normalize(infcx, param_env) }, - PlaceTy::Downcast { adt_def, substs, variant_index } => { - PlaceTy::Downcast { - adt_def, - substs: substs.trans_normalize(infcx, param_env), - variant_index, - } - } - } - } -} - -// NOTE: Callable from trans only! -impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { - /// Currently, higher-ranked type bounds inhibit normalization. Therefore, - /// each time we erase them in translation, we need to normalize - /// the contents. - pub fn erase_late_bound_regions_and_normalize(self, value: &ty::Binder) - -> T - where T: TransNormalize<'tcx> - { - assert!(!value.needs_subst()); - let value = self.erase_late_bound_regions(value); - self.fully_normalize_associated_types_in(&value) - } - - /// Fully normalizes any associated types in `value`, using an - /// empty environment and `Reveal::All` mode (therefore, suitable - /// only for monomorphized code during trans, basically). - pub fn fully_normalize_associated_types_in(self, value: &T) -> T - where T: TransNormalize<'tcx> - { - debug!("fully_normalize_associated_types_in(t={:?})", value); - - let param_env = ty::ParamEnv::reveal_all(); - let value = self.erase_regions(value); - - if !value.has_projections() { - return value; - } - - self.infer_ctxt().enter(|infcx| { - value.trans_normalize(&infcx, param_env) - }) - } - - /// Does a best-effort to normalize any associated types in - /// `value`; this includes revealing specializable types, so this - /// should be not be used during type-checking, but only during - /// optimization and code generation. - pub fn normalize_associated_type_in_env( - self, value: &T, env: ty::ParamEnv<'tcx> - ) -> T - where T: TransNormalize<'tcx> - { - debug!("normalize_associated_type_in_env(t={:?})", value); - - let value = self.erase_regions(value); - - if !value.has_projections() { - return value; - } - - self.infer_ctxt().enter(|infcx| { - value.trans_normalize(&infcx, env.with_reveal_all()) - }) - } -} - impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { - fn normalize_projections_in(&self, param_env: ty::ParamEnv<'tcx>, value: &T) -> T::Lifted - where T: TypeFoldable<'tcx> + ty::Lift<'gcx> - { - let mut selcx = traits::SelectionContext::new(self); - let cause = traits::ObligationCause::dummy(); - let traits::Normalized { value: result, obligations } = - traits::normalize(&mut selcx, param_env, cause, value); - - debug!("normalize_projections_in: result={:?} obligations={:?}", - result, obligations); - - let mut fulfill_cx = traits::FulfillmentContext::new(); - - for obligation in obligations { - fulfill_cx.register_predicate_obligation(self, obligation); - } - - self.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &result) - } - /// Finishes processes any obligations that remain in the /// fulfillment context, and then returns the result with all type /// variables removed and regions erased. Because this is intended diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 3e23e3823470c..a2a5aa246cf77 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -770,7 +770,10 @@ fn vtable_methods<'a, 'tcx>( // the trait type may have higher-ranked lifetimes in it; // so erase them if they appear, so that we get the type // at some particular call site - let substs = tcx.erase_late_bound_regions_and_normalize(&ty::Binder(substs)); + let substs = tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &ty::Binder(substs), + ); // It's possible that the method relies on where clauses that // do not hold for this particular set of type parameters. diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs index c873580e3ad6c..c97f6f199d2f5 100644 --- a/src/librustc/traits/trans/mod.rs +++ b/src/librustc/traits/trans/mod.rs @@ -14,14 +14,13 @@ // general routines. use dep_graph::{DepKind, DepTrackingMapConfig}; -use infer::TransNormalize; use std::marker::PhantomData; use syntax_pos::DUMMY_SP; use hir::def_id::DefId; use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, Vtable}; use ty::{self, Ty, TyCtxt}; use ty::subst::{Subst, Substs}; -use ty::fold::{TypeFoldable, TypeFolder}; +use ty::fold::TypeFoldable; /// Attempts to resolve an obligation to a vtable.. The result is /// a shallow vtable resolution -- meaning that we do not @@ -93,12 +92,11 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { param_substs: &Substs<'tcx>, value: &T) -> T - where T: TransNormalize<'tcx> + where T: TypeFoldable<'tcx> { debug!("apply_param_substs(param_substs={:?}, value={:?})", param_substs, value); let substituted = value.subst(self, param_substs); - let substituted = self.erase_regions(&substituted); - AssociatedTypeNormalizer::new(self).fold(&substituted) + self.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substituted) } pub fn trans_apply_param_substs_env( @@ -108,7 +106,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { value: &T, ) -> T where - T: TransNormalize<'tcx>, + T: TypeFoldable<'tcx>, { debug!( "apply_param_substs_env(param_substs={:?}, value={:?}, param_env={:?})", @@ -117,8 +115,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { param_env, ); let substituted = value.subst(self, param_substs); - let substituted = self.erase_regions(&substituted); - AssociatedTypeNormalizerEnv::new(self, param_env).fold(&substituted) + self.normalize_erasing_regions(param_env, substituted) } pub fn trans_impl_self_ty(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) @@ -128,73 +125,6 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { } } -struct AssociatedTypeNormalizer<'a, 'gcx: 'a> { - tcx: TyCtxt<'a, 'gcx, 'gcx>, -} - -impl<'a, 'gcx> AssociatedTypeNormalizer<'a, 'gcx> { - fn new(tcx: TyCtxt<'a, 'gcx, 'gcx>) -> Self { - AssociatedTypeNormalizer { tcx } - } - - fn fold>(&mut self, value: &T) -> T { - if !value.has_projections() { - value.clone() - } else { - value.fold_with(self) - } - } -} - -impl<'a, 'gcx> TypeFolder<'gcx, 'gcx> for AssociatedTypeNormalizer<'a, 'gcx> { - fn tcx<'c>(&'c self) -> TyCtxt<'c, 'gcx, 'gcx> { - self.tcx - } - - fn fold_ty(&mut self, ty: Ty<'gcx>) -> Ty<'gcx> { - if !ty.has_projections() { - ty - } else { - debug!("AssociatedTypeNormalizer: ty={:?}", ty); - self.tcx.fully_normalize_monormophic_ty(ty) - } - } -} - -struct AssociatedTypeNormalizerEnv<'a, 'gcx: 'a> { - tcx: TyCtxt<'a, 'gcx, 'gcx>, - param_env: ty::ParamEnv<'gcx>, -} - -impl<'a, 'gcx> AssociatedTypeNormalizerEnv<'a, 'gcx> { - fn new(tcx: TyCtxt<'a, 'gcx, 'gcx>, param_env: ty::ParamEnv<'gcx>) -> Self { - Self { tcx, param_env } - } - - fn fold>(&mut self, value: &T) -> T { - if !value.has_projections() { - value.clone() - } else { - value.fold_with(self) - } - } -} - -impl<'a, 'gcx> TypeFolder<'gcx, 'gcx> for AssociatedTypeNormalizerEnv<'a, 'gcx> { - fn tcx<'c>(&'c self) -> TyCtxt<'c, 'gcx, 'gcx> { - self.tcx - } - - fn fold_ty(&mut self, ty: Ty<'gcx>) -> Ty<'gcx> { - if !ty.has_projections() { - ty - } else { - debug!("AssociatedTypeNormalizerEnv: ty={:?}", ty); - self.tcx.normalize_associated_type_in_env(&ty, self.param_env) - } - } -} - // Implement DepTrackingMapConfig for `trait_cache` pub struct TraitSelectionCache<'tcx> { data: PhantomData<&'tcx ()> diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 24d3b37f804ee..9a687028b582c 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -2537,9 +2537,6 @@ pub fn provide(providers: &mut ty::maps::Providers) { assert_eq!(cnum, LOCAL_CRATE); tcx.features().clone_closures }; - providers.fully_normalize_monormophic_ty = |tcx, ty| { - tcx.fully_normalize_associated_types_in(&ty) - }; providers.features_query = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); Lrc::new(tcx.sess.features_untracked().clone()) diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index ebb058c5d2929..180fb9cabd3f5 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -352,7 +352,7 @@ fn fn_once_adapter_instance<'a, 'tcx>( closure_did, substs); let sig = substs.closure_sig(closure_did, tcx); - let sig = tcx.erase_late_bound_regions_and_normalize(&sig); + let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); assert_eq!(sig.inputs().len(), 1); let substs = tcx.mk_substs([Kind::from(self_ty), sig.inputs()[0].into()].iter().cloned()); diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 9ce232ba17303..04353d2ece192 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -1213,7 +1213,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { data_ptr.valid_range.start = 1; } - let pointee = tcx.normalize_associated_type_in_env(&pointee, param_env); + let pointee = tcx.normalize_erasing_regions(param_env, pointee); if pointee.is_sized(tcx.at(DUMMY_SP), param_env) { return Ok(tcx.intern_layout(LayoutDetails::scalar(self, data_ptr))); } @@ -1241,7 +1241,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { // Arrays and slices. ty::TyArray(element, mut count) => { if count.has_projections() { - count = tcx.normalize_associated_type_in_env(&count, param_env); + count = tcx.normalize_erasing_regions(param_env, count); if count.has_projections() { return Err(LayoutError::Unknown(ty)); } @@ -1686,7 +1686,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { // Types with no meaningful known layout. ty::TyProjection(_) | ty::TyAnon(..) => { - let normalized = tcx.normalize_associated_type_in_env(&ty, param_env); + let normalized = tcx.normalize_erasing_regions(param_env, ty); if ty == normalized { return Err(LayoutError::Unknown(ty)); } @@ -1953,7 +1953,7 @@ impl<'a, 'tcx> SizeSkeleton<'tcx> { } ty::TyProjection(_) | ty::TyAnon(..) => { - let normalized = tcx.normalize_associated_type_in_env(&ty, param_env); + let normalized = tcx.normalize_erasing_regions(param_env, ty); if ty == normalized { Err(err) } else { @@ -2059,7 +2059,7 @@ impl<'a, 'tcx> LayoutOf> for LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { /// executes in "reveal all" mode. fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { let param_env = self.param_env.with_reveal_all(); - let ty = self.tcx.normalize_associated_type_in_env(&ty, param_env); + let ty = self.tcx.normalize_erasing_regions(param_env, ty); let details = self.tcx.layout_raw(param_env.and(ty))?; let layout = TyLayout { ty, @@ -2085,7 +2085,7 @@ impl<'a, 'tcx> LayoutOf> for LayoutCx<'tcx, ty::maps::TyCtxtAt<'a, 'tcx /// executes in "reveal all" mode. fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { let param_env = self.param_env.with_reveal_all(); - let ty = self.tcx.normalize_associated_type_in_env(&ty, param_env); + let ty = self.tcx.normalize_erasing_regions(param_env, ty); let details = self.tcx.layout_raw(param_env.and(ty))?; let layout = TyLayout { ty, diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index 487bcf1f96ac8..dbfe7770bbde0 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -610,12 +610,6 @@ impl<'tcx> QueryDescription<'tcx> for queries::has_copy_closures<'tcx> { } } -impl<'tcx> QueryDescription<'tcx> for queries::fully_normalize_monormophic_ty<'tcx> { - fn describe(_tcx: TyCtxt, _: Ty) -> String { - format!("normalizing types") - } -} - impl<'tcx> QueryDescription<'tcx> for queries::features_query<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { format!("looking up enabled feature gates") diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 15e309c13d532..7d726d2e3cd5d 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -384,7 +384,6 @@ define_maps! { <'tcx> // Normally you would just use `tcx.erase_regions(&value)`, // however, which uses this query as a kind of cache. [] fn erase_regions_ty: erase_regions_ty(Ty<'tcx>) -> Ty<'tcx>, - [] fn fully_normalize_monormophic_ty: normalize_ty_node(Ty<'tcx>) -> Ty<'tcx>, /// Do not call this query directly: invoke `normalize` instead. [] fn normalize_projection_ty: NormalizeProjectionTy( @@ -565,10 +564,6 @@ fn vtable_methods_node<'tcx>(trait_ref: ty::PolyTraitRef<'tcx>) -> DepConstructo DepConstructor::VtableMethods{ trait_ref } } -fn normalize_ty_node<'tcx>(_: Ty<'tcx>) -> DepConstructor<'tcx> { - DepConstructor::NormalizeTy -} - fn substitute_normalize_and_test_predicates_node<'tcx>(key: (DefId, &'tcx Substs<'tcx>)) -> DepConstructor<'tcx> { DepConstructor::SubstituteNormalizeAndTestPredicates { key } diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 53a3bcdba3369..bc7186f781a82 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -774,7 +774,6 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::EraseRegionsTy | DepKind::NormalizeProjectionTy | DepKind::NormalizeTyAfterErasingRegions | - DepKind::NormalizeTy | DepKind::DropckOutlives | DepKind::SubstituteNormalizeAndTestPredicates | DepKind::InstanceDefSizeEstimate | diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 266f322e39777..445fe0cc40197 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -12,7 +12,7 @@ use rustc::hir::map as hir_map; use rustc::ty::subst::Substs; -use rustc::ty::{self, AdtKind, Ty, TyCtxt}; +use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; use rustc::ty::layout::{self, LayoutOf}; use util::nodemap::FxHashSet; use lint::{LateContext, LintContext, LintArray}; @@ -509,8 +509,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // make sure the fields are actually safe. let mut all_phantom = true; for field in &def.non_enum_variant().fields { - let field_ty = cx.fully_normalize_associated_types_in( - &field.ty(cx, substs) + let field_ty = cx.normalize_erasing_regions( + ParamEnv::reveal_all(), + field.ty(cx, substs), ); // repr(transparent) types are allowed to have arbitrary ZSTs, not just // PhantomData -- skip checking all ZST fields @@ -556,8 +557,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let mut all_phantom = true; for field in &def.non_enum_variant().fields { - let field_ty = cx.fully_normalize_associated_types_in( - &field.ty(cx, substs) + let field_ty = cx.normalize_erasing_regions( + ParamEnv::reveal_all(), + field.ty(cx, substs), ); let r = self.check_type_for_ffi(cache, field_ty); match r { @@ -596,8 +598,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Check the contained variants. for variant in &def.variants { for field in &variant.fields { - let arg = cx.fully_normalize_associated_types_in( - &field.ty(cx, substs) + let arg = cx.normalize_erasing_regions( + ParamEnv::reveal_all(), + field.ty(cx, substs), ); let r = self.check_type_for_ffi(cache, arg); match r { @@ -716,7 +719,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>) { // it is only OK to use this function because extern fns cannot have // any generic types right now: - let ty = self.cx.tcx.fully_normalize_associated_types_in(&ty); + let ty = self.cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); match self.check_type_for_ffi(&mut FxHashSet(), ty) { FfiResult::FfiSafe => {} diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 3f111ebcb7802..8da7c497973e7 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -732,7 +732,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { for (index, field) in def.all_fields().enumerate() { let gcx = self.tcx.global_tcx(); let field_ty = field.ty(gcx, substs); - let field_ty = gcx.normalize_associated_type_in_env(&field_ty, self.param_env); + let field_ty = gcx.normalize_erasing_regions(self.param_env, field_ty); let place = drop_place.clone().field(Field::new(index), field_ty); self.visit_terminator_drop(loc, term, flow_state, &place, field_ty, span); diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index d766ea3e2bd0d..abe45267a1f9e 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -285,10 +285,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M pub fn monomorphize(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { // miri doesn't care about lifetimes, and will choke on some crazy ones // let's simply get rid of them - let without_lifetimes = self.tcx.erase_regions(&ty); - let substituted = without_lifetimes.subst(*self.tcx, substs); - let substituted = self.tcx.fully_normalize_monormophic_ty(&substituted); - substituted + let substituted = ty.subst(*self.tcx, substs); + self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substituted) } /// Return the size and aligment of the value at the given type. diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index babc784701475..a729e3a5dda82 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -75,8 +75,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { match instance_ty.sty { ty::TyFnDef(..) => { let real_sig = instance_ty.fn_sig(*self.tcx); - let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig); - let real_sig = self.tcx.erase_late_bound_regions_and_normalize(&real_sig); + let sig = self.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); + let real_sig = self.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &real_sig, + ); if !self.check_sig_compat(sig, real_sig)? { return err!(FunctionPointerTyMismatch(real_sig, sig)); } @@ -95,7 +101,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } }; let args = self.operands_to_args(args)?; - let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig); + let sig = self.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); self.eval_fn_call( fn_def, destination, diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs index 1f7f1237ba789..10e2a84038de1 100644 --- a/src/librustc_mir/monomorphize/item.rs +++ b/src/librustc_mir/monomorphize/item.rs @@ -347,7 +347,10 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> { output.push_str("fn("); - let sig = self.tcx.erase_late_bound_regions_and_normalize(&sig); + let sig = self.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); if !sig.inputs().is_empty() { for ¶meter_type in sig.inputs() { diff --git a/src/librustc_mir/monomorphize/mod.rs b/src/librustc_mir/monomorphize/mod.rs index 4c9789782a6e1..5c38735d92034 100644 --- a/src/librustc_mir/monomorphize/mod.rs +++ b/src/librustc_mir/monomorphize/mod.rs @@ -88,7 +88,7 @@ fn fn_once_adapter_instance<'a, 'tcx>( closure_did, substs); let sig = substs.closure_sig(closure_did, tcx); - let sig = tcx.erase_late_bound_regions_and_normalize(&sig); + let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); assert_eq!(sig.inputs().len(), 1); let substs = tcx.mk_substs([ Kind::from(self_ty), diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 9aff7fa2a2c71..62af09cc49109 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -832,14 +832,11 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, let tcx = infcx.tcx; let gcx = tcx.global_tcx(); let def_id = tcx.hir.local_def_id(ctor_id); - let sig = gcx.fn_sig(def_id).no_late_bound_regions() - .expect("LBR in ADT constructor signature"); - let sig = gcx.erase_regions(&sig); let param_env = gcx.param_env(def_id); - // Normalize the sig now that we have liberated the late-bound - // regions. - let sig = gcx.normalize_associated_type_in_env(&sig, param_env); + // Normalize the sig. + let sig = gcx.fn_sig(def_id).no_late_bound_regions().expect("LBR in ADT constructor signature"); + let sig = gcx.normalize_erasing_regions(param_env, sig); let (adt_def, substs) = match sig.output().sty { ty::TyAdt(adt_def, substs) => (adt_def, substs), diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index 458dd488409e4..3f5208dd2d4b8 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -206,11 +206,10 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let field = Field::new(i); let subpath = self.elaborator.field_subpath(variant_path, field); - let field_ty = - self.tcx().normalize_associated_type_in_env( - &f.ty(self.tcx(), substs), - self.elaborator.param_env() - ); + let field_ty = self.tcx().normalize_erasing_regions( + self.elaborator.param_env(), + f.ty(self.tcx(), substs), + ); (base_place.clone().field(field, field_ty), subpath) }).collect() } diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index ee0f2415bd808..c1dc8c6684a17 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -650,7 +650,7 @@ impl<'a, 'tcx> FnType<'tcx> { -> Self { let fn_ty = instance.ty(cx.tcx); let sig = ty_fn_sig(cx, fn_ty); - let sig = cx.tcx.erase_late_bound_regions_and_normalize(&sig); + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); FnType::new(cx, sig, &[]) } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 76e05ae7dcb80..4da082e9d50f1 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -462,7 +462,7 @@ pub fn trans_instance<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, instance: Instance<'tc let fn_ty = instance.ty(cx.tcx); let sig = common::ty_fn_sig(cx, fn_ty); - let sig = cx.tcx.erase_late_bound_regions_and_normalize(&sig); + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); let lldecl = match cx.instances.borrow().get(&instance) { Some(&val) => val, diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index d20b51ca0fdf4..20cc57522b5d7 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -30,7 +30,7 @@ use rustc::ty::util::TypeIdHasher; use rustc::ich::Fingerprint; use rustc::ty::Instance; use common::CodegenCx; -use rustc::ty::{self, AdtKind, Ty, TyCtxt}; +use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout}; use rustc::session::config; use rustc::util::nodemap::FxHashMap; @@ -353,7 +353,10 @@ fn subroutine_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, span: Span) -> MetadataCreationResult { - let signature = cx.tcx.erase_late_bound_regions_and_normalize(&signature); + let signature = cx.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &signature, + ); let mut signature_metadata: Vec = Vec::with_capacity(signature.inputs().len() + 1); @@ -589,7 +592,7 @@ pub fn type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, } ty::TyGenerator(def_id, substs, _) => { let upvar_tys : Vec<_> = substs.field_tys(def_id, cx.tcx).map(|t| { - cx.tcx.fully_normalize_associated_types_in(&t) + cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t) }).collect(); prepare_tuple_metadata(cx, t, diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index 16279f31836a9..683e1835968f8 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -30,7 +30,7 @@ use abi::Abi; use common::CodegenCx; use builder::Builder; use monomorphize::Instance; -use rustc::ty::{self, Ty}; +use rustc::ty::{self, ParamEnv, Ty}; use rustc::mir; use rustc::session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; use rustc::util::nodemap::{DefIdMap, FxHashMap, FxHashSet}; @@ -378,7 +378,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, name_to_append_suffix_to.push_str(","); } - let actual_type = cx.tcx.fully_normalize_associated_types_in(&actual_type); + let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), actual_type); // Add actual type name to <...> clause of function name let actual_type_name = compute_debuginfo_type_name(cx, actual_type, @@ -391,7 +391,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, let template_params: Vec<_> = if cx.sess().opts.debuginfo == FullDebugInfo { let names = get_type_parameter_names(cx, generics); substs.types().zip(names).map(|(ty, name)| { - let actual_type = cx.tcx.fully_normalize_associated_types_in(&ty); + let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); let actual_type_metadata = type_metadata(cx, actual_type, syntax_pos::DUMMY_SP); let name = CString::new(name.as_str().as_bytes()).unwrap(); unsafe { diff --git a/src/librustc_trans/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs index a88eb9ae35471..211de95c96ede 100644 --- a/src/librustc_trans/debuginfo/type_names.rs +++ b/src/librustc_trans/debuginfo/type_names.rs @@ -117,8 +117,10 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, }, ty::TyDynamic(ref trait_data, ..) => { if let Some(principal) = trait_data.principal() { - let principal = cx.tcx.erase_late_bound_regions_and_normalize( - &principal); + let principal = cx.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &principal, + ); push_item_name(cx, principal.def_id, false, output); push_type_params(cx, principal.substs, output); } @@ -138,7 +140,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, output.push_str("fn("); - let sig = cx.tcx.erase_late_bound_regions_and_normalize(&sig); + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); if !sig.inputs().is_empty() { for ¶meter_type in sig.inputs() { push_debuginfo_type_name(cx, parameter_type, true, output); diff --git a/src/librustc_trans/declare.rs b/src/librustc_trans/declare.rs index aa1cd0c27e795..c2010feb1b638 100644 --- a/src/librustc_trans/declare.rs +++ b/src/librustc_trans/declare.rs @@ -22,7 +22,7 @@ use llvm::{self, ValueRef}; use llvm::AttributePlace::Function; -use rustc::ty::Ty; +use rustc::ty::{self, Ty}; use rustc::session::config::Sanitizer; use rustc_back::PanicStrategy; use abi::{Abi, FnType}; @@ -127,7 +127,7 @@ pub fn declare_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, name: &str, fn_type: Ty<'tcx>) -> ValueRef { debug!("declare_rust_fn(name={:?}, fn_type={:?})", name, fn_type); let sig = common::ty_fn_sig(cx, fn_type); - let sig = cx.tcx.erase_late_bound_regions_and_normalize(&sig); + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); debug!("declare_rust_fn (after region erasure) sig={:?}", sig); let fty = FnType::new(cx, sig, &[]); diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 3f87ce7e04792..4cef7470c62d7 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -100,7 +100,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>, }; let sig = callee_ty.fn_sig(tcx); - let sig = tcx.erase_late_bound_regions_and_normalize(&sig); + let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); let arg_tys = sig.inputs(); let ret_ty = sig.output(); let name = &*tcx.item_name(def_id); @@ -1035,7 +1035,10 @@ fn generic_simd_intrinsic<'a, 'tcx>( let tcx = bx.tcx(); - let sig = tcx.erase_late_bound_regions_and_normalize(&callee_ty.fn_sig(tcx)); + let sig = tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &callee_ty.fn_sig(tcx), + ); let arg_tys = sig.inputs(); // every intrinsic takes a SIMD vector as its first argument diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 606c1396c1daf..96c5bb3b91d2e 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -281,7 +281,10 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { ty::TyDynamic(..) => { let fn_ty = drop_fn.ty(bx.cx.tcx); let sig = common::ty_fn_sig(bx.cx, fn_ty); - let sig = bx.tcx().erase_late_bound_regions_and_normalize(&sig); + let sig = bx.tcx().normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); let fn_ty = FnType::new_vtable(bx.cx, sig, &[]); args = &args[..1]; (meth::DESTRUCTOR.get_fn(&bx, place.llextra, &fn_ty), fn_ty) @@ -430,7 +433,10 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { }; let def = instance.map(|i| i.def); let sig = callee.layout.ty.fn_sig(bx.tcx()); - let sig = bx.tcx().erase_late_bound_regions_and_normalize(&sig); + let sig = bx.tcx().normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); let abi = sig.abi; // Handle intrinsics old trans wants Expr's for, ourselves. diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index a1044ac87e4c5..a92a59edfde1f 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -16,7 +16,6 @@ use rustc::ty::{self, TypeFoldable}; use rustc::ty::layout::{LayoutOf, TyLayout}; use rustc::mir::{self, Mir}; use rustc::ty::subst::Substs; -use rustc::infer::TransNormalize; use rustc::session::config::FullDebugInfo; use base; use builder::Builder; @@ -108,7 +107,7 @@ pub struct FunctionCx<'a, 'tcx:'a> { impl<'a, 'tcx> FunctionCx<'a, 'tcx> { pub fn monomorphize(&self, value: &T) -> T - where T: TransNormalize<'tcx> + where T: TypeFoldable<'tcx> { self.cx.tcx.trans_apply_param_substs(self.param_substs, value) } diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs index af957500f7002..f37114ee4acd1 100644 --- a/src/librustc_trans/type_of.rs +++ b/src/librustc_trans/type_of.rs @@ -258,7 +258,10 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { cx.layout_of(self.ty.boxed_ty()).llvm_type(cx).ptr_to() } ty::TyFnPtr(sig) => { - let sig = cx.tcx.erase_late_bound_regions_and_normalize(&sig); + let sig = cx.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); FnType::new(cx, sig, &[]).llvm_type(cx).ptr_to() } _ => self.scalar_llvm_type_at(cx, scalar, Size::from_bytes(0)) From 0a2ac85e3fa8c1c05a7e2b5b412c073ce7853f23 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 13 Feb 2018 09:15:01 -0500 Subject: [PATCH 18/27] move `drain_fulfillment_cx_or_panic` to be private to traits::trans --- src/librustc/infer/mod.rs | 40 ----------------------------- src/librustc/traits/trans/mod.rs | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 217a157cb33ee..fe919775da0b4 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -493,46 +493,6 @@ pub struct CombinedSnapshot<'a, 'tcx:'a> { } impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { - /// Finishes processes any obligations that remain in the - /// fulfillment context, and then returns the result with all type - /// variables removed and regions erased. Because this is intended - /// for use after type-check has completed, if any errors occur, - /// it will panic. It is used during normalization and other cases - /// where processing the obligations in `fulfill_cx` may cause - /// type inference variables that appear in `result` to be - /// unified, and hence we need to process those obligations to get - /// the complete picture of the type. - pub fn drain_fulfillment_cx_or_panic(&self, - span: Span, - fulfill_cx: &mut traits::FulfillmentContext<'tcx>, - result: &T) - -> T::Lifted - where T: TypeFoldable<'tcx> + ty::Lift<'gcx> - { - debug!("drain_fulfillment_cx_or_panic()"); - - // In principle, we only need to do this so long as `result` - // contains unbound type parameters. It could be a slight - // optimization to stop iterating early. - match fulfill_cx.select_all_or_error(self) { - Ok(()) => { } - Err(errors) => { - span_bug!(span, "Encountered errors `{:?}` resolving bounds after type-checking", - errors); - } - } - - let result = self.resolve_type_vars_if_possible(result); - let result = self.tcx.erase_regions(&result); - - match self.tcx.lift_to_global(&result) { - Some(result) => result, - None => { - span_bug!(span, "Uninferred types/regions in `{:?}`", result); - } - } - } - pub fn is_in_snapshot(&self) -> bool { self.in_snapshot.get() } diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs index c97f6f199d2f5..86f9d168ad304 100644 --- a/src/librustc/traits/trans/mod.rs +++ b/src/librustc/traits/trans/mod.rs @@ -17,6 +17,8 @@ use dep_graph::{DepKind, DepTrackingMapConfig}; use std::marker::PhantomData; use syntax_pos::DUMMY_SP; use hir::def_id::DefId; +use infer::InferCtxt; +use syntax_pos::Span; use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, Vtable}; use ty::{self, Ty, TyCtxt}; use ty::subst::{Subst, Substs}; @@ -151,3 +153,45 @@ impl<'gcx> DepTrackingMapConfig for ProjectionCache<'gcx> { DepKind::TraitSelect } } + +impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { + /// Finishes processes any obligations that remain in the + /// fulfillment context, and then returns the result with all type + /// variables removed and regions erased. Because this is intended + /// for use after type-check has completed, if any errors occur, + /// it will panic. It is used during normalization and other cases + /// where processing the obligations in `fulfill_cx` may cause + /// type inference variables that appear in `result` to be + /// unified, and hence we need to process those obligations to get + /// the complete picture of the type. + fn drain_fulfillment_cx_or_panic(&self, + span: Span, + fulfill_cx: &mut FulfillmentContext<'tcx>, + result: &T) + -> T::Lifted + where T: TypeFoldable<'tcx> + ty::Lift<'gcx> + { + debug!("drain_fulfillment_cx_or_panic()"); + + // In principle, we only need to do this so long as `result` + // contains unbound type parameters. It could be a slight + // optimization to stop iterating early. + match fulfill_cx.select_all_or_error(self) { + Ok(()) => { } + Err(errors) => { + span_bug!(span, "Encountered errors `{:?}` resolving bounds after type-checking", + errors); + } + } + + let result = self.resolve_type_vars_if_possible(result); + let result = self.tcx.erase_regions(&result); + + match self.tcx.lift_to_global(&result) { + Some(result) => result, + None => { + span_bug!(span, "Uninferred types/regions in `{:?}`", result); + } + } + } +} From 36e5092dfa7fcc676c7d6b63f8dd692edd3399a5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 26 Feb 2018 11:14:16 -0500 Subject: [PATCH 19/27] add some debug output --- src/librustc/traits/query/dropck_outlives.rs | 3 ++- src/librustc/traits/query/normalize.rs | 6 ++++++ src/librustc/traits/query/normalize_erasing_regions.rs | 7 +++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs index 5c964f6559a3f..cfcd951d4906c 100644 --- a/src/librustc/traits/query/dropck_outlives.rs +++ b/src/librustc/traits/query/dropck_outlives.rs @@ -38,7 +38,8 @@ impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { pub fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>> { debug!( "dropck_outlives(ty={:?}, param_env={:?})", - ty, self.param_env, + ty, + self.param_env, ); let tcx = self.infcx.tcx; diff --git a/src/librustc/traits/query/normalize.rs b/src/librustc/traits/query/normalize.rs index 030e630f23f83..70c5cf5f39029 100644 --- a/src/librustc/traits/query/normalize.rs +++ b/src/librustc/traits/query/normalize.rs @@ -45,6 +45,12 @@ impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { where T: TypeFoldable<'tcx>, { + debug!( + "normalize::<{}>(value={:?}, param_env={:?})", + unsafe { ::std::intrinsics::type_name::() }, + value, + self.param_env, + ); let mut normalizer = QueryNormalizer { infcx: self.infcx, cause: self.cause, diff --git a/src/librustc/traits/query/normalize_erasing_regions.rs b/src/librustc/traits/query/normalize_erasing_regions.rs index d2d8da88e2de5..a9734e9c2986e 100644 --- a/src/librustc/traits/query/normalize_erasing_regions.rs +++ b/src/librustc/traits/query/normalize_erasing_regions.rs @@ -30,6 +30,13 @@ impl<'cx, 'tcx> TyCtxt<'cx, 'tcx, 'tcx> { where T: TypeFoldable<'tcx>, { + debug!( + "normalize_erasing_regions::<{}>(value={:?}, param_env={:?})", + unsafe { ::std::intrinsics::type_name::() }, + value, + param_env, + ); + // Erase first before we do the real query -- this keeps the // cache from being too polluted. let value = self.erase_regions(&value); From 1e4e632ad8be2be00a1894c6c5946b730e12a5bd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 27 Feb 2018 16:14:41 -0500 Subject: [PATCH 20/27] add regression tests for various MIR bugs that get fixed Fixes #31567 Fixes #47470 Fixes #48132 Fixes #48179 --- src/test/ui/issue-48132.rs | 42 ++++++++++++++++++++++++ src/test/ui/issue-48179.rs | 51 ++++++++++++++++++++++++++++++ src/test/ui/nll/issue-31567.rs | 37 ++++++++++++++++++++++ src/test/ui/nll/issue-31567.stderr | 18 +++++++++++ src/test/ui/nll/issue-47470.rs | 34 ++++++++++++++++++++ src/test/ui/nll/issue-47470.stderr | 17 ++++++++++ 6 files changed, 199 insertions(+) create mode 100644 src/test/ui/issue-48132.rs create mode 100644 src/test/ui/issue-48179.rs create mode 100644 src/test/ui/nll/issue-31567.rs create mode 100644 src/test/ui/nll/issue-31567.stderr create mode 100644 src/test/ui/nll/issue-47470.rs create mode 100644 src/test/ui/nll/issue-47470.stderr diff --git a/src/test/ui/issue-48132.rs b/src/test/ui/issue-48132.rs new file mode 100644 index 0000000000000..87fee986de768 --- /dev/null +++ b/src/test/ui/issue-48132.rs @@ -0,0 +1,42 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for #48132. This was failing due to problems around +// the projection caching and dropck type enumeration. + +// run-pass + +#![feature(nll)] +#![allow(warnings)] + +struct Inner { + iterator: I, + item: V, +} + +struct Outer { + inner: Inner, +} + +fn outer(iterator: I) -> Outer +where I: Iterator, + I::Item: Default, +{ + Outer { + inner: Inner { + iterator: iterator, + item: Default::default(), + } + } +} + +fn main() { + outer(std::iter::once(&1).cloned()); +} diff --git a/src/test/ui/issue-48179.rs b/src/test/ui/issue-48179.rs new file mode 100644 index 0000000000000..745b59e0658f1 --- /dev/null +++ b/src/test/ui/issue-48179.rs @@ -0,0 +1,51 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for #48132. This was failing due to problems around +// the projection caching and dropck type enumeration. + +// run-pass + +#![feature(nll)] +#![allow(warnings)] + +pub struct Container { + value: Option, +} + +impl Container { + pub fn new(iter: T) -> Self { + panic!() + } +} + +pub struct Wrapper<'a> { + content: &'a Content, +} + +impl<'a, 'de> Wrapper<'a> { + pub fn new(content: &'a Content) -> Self { + Wrapper { + content: content, + } + } +} + +pub struct Content; + +fn crash_it(content: Content) { + let items = vec![content]; + let map = items.iter().map(|ref o| Wrapper::new(o)); + + let mut map_visitor = Container::new(map); + +} + +fn main() {} diff --git a/src/test/ui/nll/issue-31567.rs b/src/test/ui/nll/issue-31567.rs new file mode 100644 index 0000000000000..a0d1faf1f0e84 --- /dev/null +++ b/src/test/ui/nll/issue-31567.rs @@ -0,0 +1,37 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for #31567: cached results of projections were +// causing region relations not to be enforced at all the places where +// they have to be enforced. + +#![feature(nll)] + +struct VecWrapper<'a>(&'a mut S); + +struct S(Box); + +fn get_dangling<'a>(v: VecWrapper<'a>) -> &'a u32 { + let s_inner: &'a S = &*v.0; //~ ERROR `*v.0` does not live long enough + &s_inner.0 +} + +impl<'a> Drop for VecWrapper<'a> { + fn drop(&mut self) { + *self.0 = S(Box::new(0)); + } +} + +fn main() { + let mut s = S(Box::new(11)); + let vw = VecWrapper(&mut s); + let dangling = get_dangling(vw); + println!("{}", dangling); +} diff --git a/src/test/ui/nll/issue-31567.stderr b/src/test/ui/nll/issue-31567.stderr new file mode 100644 index 0000000000000..e0ff653e2b4d4 --- /dev/null +++ b/src/test/ui/nll/issue-31567.stderr @@ -0,0 +1,18 @@ +error[E0597]: `*v.0` does not live long enough + --> $DIR/issue-31567.rs:22:26 + | +LL | let s_inner: &'a S = &*v.0; //~ ERROR `*v.0` does not live long enough + | ^^^^^ borrowed value does not live long enough +LL | &s_inner.0 +LL | } + | - borrowed value only lives until here + | +note: borrowed value must be valid for the lifetime 'a as defined on the function body at 21:1... + --> $DIR/issue-31567.rs:21:1 + | +LL | fn get_dangling<'a>(v: VecWrapper<'a>) -> &'a u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +If you want more information on this error, try using "rustc --explain E0597" diff --git a/src/test/ui/nll/issue-47470.rs b/src/test/ui/nll/issue-47470.rs new file mode 100644 index 0000000000000..c962f193cd5b0 --- /dev/null +++ b/src/test/ui/nll/issue-47470.rs @@ -0,0 +1,34 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for #47470: cached results of projections were +// causing region relations not to be enforced at all the places where +// they have to be enforced. + +#![feature(nll)] + +struct Foo<'a>(&'a ()); +trait Bar { + type Assoc; + fn get(self) -> Self::Assoc; +} + +impl<'a> Bar for Foo<'a> { + type Assoc = &'a u32; + fn get(self) -> Self::Assoc { + let local = 42; + &local //~ ERROR `local` does not live long enough + } +} + +fn main() { + let f = Foo(&()).get(); + println!("{}", f); +} diff --git a/src/test/ui/nll/issue-47470.stderr b/src/test/ui/nll/issue-47470.stderr new file mode 100644 index 0000000000000..1356461a6e410 --- /dev/null +++ b/src/test/ui/nll/issue-47470.stderr @@ -0,0 +1,17 @@ +error[E0597]: `local` does not live long enough + --> $DIR/issue-47470.rs:27:9 + | +LL | &local //~ ERROR `local` does not live long enough + | ^^^^^^ borrowed value does not live long enough +LL | } + | - borrowed value only lives until here + | +note: borrowed value must be valid for the lifetime 'a as defined on the impl at 23:1... + --> $DIR/issue-47470.rs:23:1 + | +LL | impl<'a> Bar for Foo<'a> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +If you want more information on this error, try using "rustc --explain E0597" From 03c5428be37eedd09f1f3b627c03c3de1beb0674 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 7 Mar 2018 13:34:48 -0500 Subject: [PATCH 21/27] short-circuit `dropck_outlives` for simple cases --- src/librustc/traits/query/dropck_outlives.rs | 74 +++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/src/librustc/traits/query/dropck_outlives.rs b/src/librustc/traits/query/dropck_outlives.rs index cfcd951d4906c..0fe4daa36ed4a 100644 --- a/src/librustc/traits/query/dropck_outlives.rs +++ b/src/librustc/traits/query/dropck_outlives.rs @@ -38,11 +38,16 @@ impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> { pub fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec>> { debug!( "dropck_outlives(ty={:?}, param_env={:?})", - ty, - self.param_env, + ty, self.param_env, ); + // Quick check: there are a number of cases that we know do not require + // any destructor. let tcx = self.infcx.tcx; + if trivial_dropck_outlives(tcx, ty) { + return InferOk { value: vec![], obligations: vec![] }; + } + let gcx = tcx.global_tcx(); let (c_ty, orig_values) = self.infcx.canonicalize_query(&self.param_env.and(ty)); let span = self.cause.span; @@ -192,3 +197,68 @@ impl_stable_hash_for!(struct DtorckConstraint<'tcx> { dtorck_types, overflows }); + +/// This returns true if the type `ty` is "trivial" for +/// dropck-outlives -- that is, if it doesn't require any types to +/// outlive. This is similar but not *quite* the same as the +/// `needs_drop` test in the compiler already -- that is, for every +/// type T for which this function return true, needs-drop would +/// return false. But the reverse does not hold: in particular, +/// `needs_drop` returns false for `PhantomData`, but it is not +/// trivial for dropck-outlives. +/// +/// Note also that `needs_drop` requires a "global" type (i.e., one +/// with erased regions), but this funtcion does not. +fn trivial_dropck_outlives<'cx, 'tcx>(tcx: TyCtxt<'cx, '_, 'tcx>, ty: Ty<'tcx>) -> bool { + match ty.sty { + // None of these types have a destructor and hence they do not + // require anything in particular to outlive the dtor's + // execution. + ty::TyInfer(ty::FreshIntTy(_)) + | ty::TyInfer(ty::FreshFloatTy(_)) + | ty::TyBool + | ty::TyInt(_) + | ty::TyUint(_) + | ty::TyFloat(_) + | ty::TyNever + | ty::TyFnDef(..) + | ty::TyFnPtr(_) + | ty::TyChar + | ty::TyGeneratorWitness(..) + | ty::TyRawPtr(_) + | ty::TyRef(..) + | ty::TyStr + | ty::TyForeign(..) + | ty::TyError => true, + + // [T; N] and [T] have same properties as T. + ty::TyArray(ty, _) | ty::TySlice(ty) => trivial_dropck_outlives(tcx, ty), + + // (T1..Tn) and closures have same properties as T1..Tn -- + // check if *any* of those are trivial. + ty::TyTuple(ref tys, _) => tys.iter().cloned().all(|t| trivial_dropck_outlives(tcx, t)), + ty::TyClosure(def_id, ref substs) => substs + .upvar_tys(def_id, tcx) + .all(|t| trivial_dropck_outlives(tcx, t)), + + ty::TyAdt(def, _) => { + if def.is_union() { + // Unions never run have a dtor. + true + } else { + // Other types might. Moreover, PhantomData doesn't + // have a dtor, but it is considered to own its + // content, so it is non-trivial. + false + } + } + + // The following *might* require a destructor: it would deeper inspection to tell. + ty::TyDynamic(..) + | ty::TyProjection(..) + | ty::TyParam(_) + | ty::TyAnon(..) + | ty::TyInfer(_) + | ty::TyGenerator(..) => false, + } +} From 0d17f95465ae3f0c865153fc902eae55ea94e2a9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 7 Mar 2018 14:08:21 -0500 Subject: [PATCH 22/27] short-circuit work when instantiating query responses Also, perform substitution in smaller parts. --- src/librustc/infer/canonical.rs | 108 +++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 24 deletions(-) diff --git a/src/librustc/infer/canonical.rs b/src/librustc/infer/canonical.rs index c1c7337860e37..83194b5e504e5 100644 --- a/src/librustc/infer/canonical.rs +++ b/src/librustc/infer/canonical.rs @@ -29,6 +29,7 @@ use infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TypeVariableOrigin}; use rustc_data_structures::indexed_vec::Idx; use std::fmt::Debug; +use std::ops::Index; use syntax::codemap::Span; use traits::{Obligation, ObligationCause, PredicateObligation}; use ty::{self, CanonicalVar, Lift, Region, Slice, Ty, TyCtxt, TypeFlags}; @@ -395,28 +396,27 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { .collect(), }; - // Apply the result substitution to the query. - let QueryResult { - var_values: query_values, - region_constraints: query_region_constraints, - certainty: _, - value: user_result, - } = query_result.substitute(self.tcx, result_subst); - // Unify the original values for the canonical variables in // the input with the value found in the query // post-substitution. Often, but not always, this is a no-op, // because we already found the mapping in the first step. + let substituted_values = |index: CanonicalVar| -> Kind<'tcx> { + query_result.substitute_projected(self.tcx, result_subst, |v| &v.var_values[index]) + }; let mut obligations = - self.unify_canonical_vars(cause, param_env, original_values, &query_values)? + self.unify_canonical_vars(cause, param_env, original_values, substituted_values)? .into_obligations(); obligations.extend(self.query_region_constraints_into_obligations( cause, param_env, - query_region_constraints, + &query_result.value.region_constraints, + result_subst, )); + let user_result: R = + query_result.substitute_projected(self.tcx, result_subst, |q_r| &q_r.value); + Ok(InferOk { value: user_result, obligations, @@ -426,17 +426,20 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// Converts the region constraints resulting from a query into an /// iterator of obligations. fn query_region_constraints_into_obligations<'a>( - &self, + &'a self, cause: &'a ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, - query_region_constraints: QueryRegionConstraints<'tcx>, + unsubstituted_region_constraints: &'a QueryRegionConstraints<'tcx>, + result_subst: &'a CanonicalVarValues<'tcx>, ) -> impl Iterator> + 'a { let QueryRegionConstraints { region_outlives, ty_outlives, - } = query_region_constraints; + } = unsubstituted_region_constraints; - let region_obligations = region_outlives.into_iter().map(move |(r1, r2)| { + let region_obligations = region_outlives.iter().map(move |(r1, r2)| { + let r1 = substitute_value(self.tcx, result_subst, r1); + let r2 = substitute_value(self.tcx, result_subst, r2); Obligation::new( cause.clone(), param_env, @@ -444,7 +447,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ) }); - let ty_obligations = ty_outlives.into_iter().map(move |(t1, r2)| { + let ty_obligations = ty_outlives.iter().map(move |(t1, r2)| { + let t1 = substitute_value(self.tcx, result_subst, t1); + let r2 = substitute_value(self.tcx, result_subst, r2); Obligation::new( cause.clone(), param_env, @@ -456,17 +461,19 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { } /// Given two sets of values for the same set of canonical variables, unify them. - pub fn unify_canonical_vars( + /// The second set is produced lazilly by supplying indices from the first set. + fn unify_canonical_vars( &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, variables1: &CanonicalVarValues<'tcx>, - variables2: &CanonicalVarValues<'tcx>, + variables2: impl Fn(CanonicalVar) -> Kind<'tcx>, ) -> InferResult<'tcx, ()> { - assert_eq!(variables1.var_values.len(), variables2.var_values.len()); self.commit_if_ok(|_| { let mut obligations = vec![]; - for (value1, value2) in variables1.var_values.iter().zip(&variables2.var_values) { + for (index, value1) in variables1.var_values.iter_enumerated() { + let value2 = variables2(index); + match (value1.unpack(), value2.unpack()) { (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => { obligations @@ -724,7 +731,9 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { value: out_value, }, ); - let values = CanonicalVarValues { var_values: IndexVec::default() }; + let values = CanonicalVarValues { + var_values: IndexVec::default(), + }; return (canon_value, values); } @@ -810,13 +819,50 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> { impl<'tcx, V> Canonical<'tcx, V> { /// Instantiate the wrapped value, replacing each canonical value /// with the value given in `var_values`. - pub fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V + fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V where V: TypeFoldable<'tcx>, + { + self.substitute_projected(tcx, var_values, |value| value) + } + + /// Invoke `projection_fn` with `self.value` to get a value V that + /// is expressed in terms of the same canonical variables bound in + /// `self`. Apply the substitution `var_values` to this value V, + /// replacing each of the canonical variables. + fn substitute_projected( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + var_values: &CanonicalVarValues<'tcx>, + projection_fn: impl FnOnce(&V) -> &T, + ) -> T + where + T: TypeFoldable<'tcx>, { assert_eq!(self.variables.len(), var_values.var_values.len()); - self.value - .fold_with(&mut CanonicalVarValuesSubst { tcx, var_values }) + let value = projection_fn(&self.value); + substitute_value(tcx, var_values, value) + } +} + +/// Substitute the values from `var_values` into `value`. `var_values` +/// must be values for the set of cnaonical variables that appear in +/// `value`. +fn substitute_value<'a, 'tcx, T>( + tcx: TyCtxt<'_, '_, 'tcx>, + var_values: &CanonicalVarValues<'tcx>, + value: &'a T, +) -> T +where + T: TypeFoldable<'tcx>, +{ + if var_values.var_values.is_empty() { + debug_assert!(!value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS)); + value.clone() + } else if !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) { + value.clone() + } else { + value.fold_with(&mut CanonicalVarValuesSubst { tcx, var_values }) } } @@ -838,7 +884,13 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for CanonicalVarValuesSubst<'cx, 'g r => bug!("{:?} is a type but value is {:?}", c, r), } } - _ => t.super_fold_with(self), + _ => { + if !t.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) { + t + } else { + t.super_fold_with(self) + } + } } } @@ -926,3 +978,11 @@ BraceStructLiftImpl! { var_values, region_constraints, certainty, value } where R: Lift<'tcx> } + +impl<'tcx> Index for CanonicalVarValues<'tcx> { + type Output = Kind<'tcx>; + + fn index(&self, value: CanonicalVar) -> &Kind<'tcx> { + &self.var_values[value] + } +} From 6288faa3a31844a38b703b2a20a7f3cb9e15ed31 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 9 Mar 2018 12:53:17 -0500 Subject: [PATCH 23/27] `trans_apply_param_substs` => `subst_and_normalize_erasing_regions` Consolidate `trans_apply_param_substs` and `trans_apply_param_substs_env`. Also remove `trans_impl_self_ty` --- src/librustc/traits/trans/mod.rs | 32 ++++--------- src/librustc/ty/instance.rs | 12 ++++- src/librustc_mir/interpret/eval_context.rs | 12 ++++- src/librustc_mir/interpret/terminator/mod.rs | 6 ++- src/librustc_mir/monomorphize/collector.rs | 47 ++++++++++++++----- src/librustc_mir/monomorphize/partitioning.rs | 6 ++- src/librustc_mir/transform/inline.rs | 34 +++----------- src/librustc_trans/debuginfo/mod.rs | 6 ++- src/librustc_trans/mir/mod.rs | 6 ++- 9 files changed, 91 insertions(+), 70 deletions(-) diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs index 86f9d168ad304..cc8b74e0ee23a 100644 --- a/src/librustc/traits/trans/mod.rs +++ b/src/librustc/traits/trans/mod.rs @@ -16,7 +16,6 @@ use dep_graph::{DepKind, DepTrackingMapConfig}; use std::marker::PhantomData; use syntax_pos::DUMMY_SP; -use hir::def_id::DefId; use infer::InferCtxt; use syntax_pos::Span; use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, Vtable}; @@ -88,30 +87,23 @@ pub fn trans_fulfill_obligation<'a, 'tcx>(ty: TyCtxt<'a, 'tcx, 'tcx>, } impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { - /// Monomorphizes a type from the AST by first applying the in-scope - /// substitutions and then normalizing any associated types. - pub fn trans_apply_param_substs(self, - param_substs: &Substs<'tcx>, - value: &T) - -> T - where T: TypeFoldable<'tcx> - { - debug!("apply_param_substs(param_substs={:?}, value={:?})", param_substs, value); - let substituted = value.subst(self, param_substs); - self.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substituted) - } - - pub fn trans_apply_param_substs_env( + /// Monomorphizes a type from the AST by first applying the + /// in-scope substitutions and then normalizing any associated + /// types. + pub fn subst_and_normalize_erasing_regions( self, param_substs: &Substs<'tcx>, param_env: ty::ParamEnv<'tcx>, - value: &T, + value: &T ) -> T where T: TypeFoldable<'tcx>, { debug!( - "apply_param_substs_env(param_substs={:?}, value={:?}, param_env={:?})", + "subst_and_normalize_erasing_regions(\ + param_substs={:?}, \ + value={:?}, \ + param_env={:?})", param_substs, value, param_env, @@ -119,12 +111,6 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { let substituted = value.subst(self, param_substs); self.normalize_erasing_regions(param_env, substituted) } - - pub fn trans_impl_self_ty(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) - -> Ty<'tcx> - { - self.trans_apply_param_substs(substs, &self.type_of(def_id)) - } } // Implement DepTrackingMapConfig for `trait_cache` diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index 180fb9cabd3f5..76f7a0b59a2a5 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -51,7 +51,11 @@ impl<'a, 'tcx> Instance<'tcx> { -> Ty<'tcx> { let ty = tcx.type_of(self.def.def_id()); - tcx.trans_apply_param_substs(self.substs, &ty) + tcx.subst_and_normalize_erasing_regions( + self.substs, + ty::ParamEnv::reveal_all(), + &ty, + ) } } @@ -184,7 +188,11 @@ impl<'a, 'b, 'tcx> Instance<'tcx> { resolve_associated_item(tcx, &item, param_env, trait_def_id, substs) } else { let ty = tcx.type_of(def_id); - let item_type = tcx.trans_apply_param_substs_env(substs, param_env, &ty); + let item_type = tcx.subst_and_normalize_erasing_regions( + substs, + param_env, + &ty, + ); let def = match item_type.sty { ty::TyFnDef(..) if { diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index abe45267a1f9e..c236ce2abc5f0 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -249,7 +249,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M trace!("resolve: {:?}, {:#?}", def_id, substs); trace!("substs: {:#?}", self.substs()); trace!("param_env: {:#?}", self.param_env); - let substs = self.tcx.trans_apply_param_substs_env(self.substs(), self.param_env, &substs); + let substs = self.tcx.subst_and_normalize_erasing_regions( + self.substs(), + self.param_env, + &substs, + ); ty::Instance::resolve( *self.tcx, self.param_env, @@ -722,7 +726,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M ClosureFnPointer => { match self.eval_operand(operand)?.ty.sty { ty::TyClosure(def_id, substs) => { - let substs = self.tcx.trans_apply_param_substs(self.substs(), &substs); + let substs = self.tcx.subst_and_normalize_erasing_regions( + self.substs(), + ty::ParamEnv::reveal_all(), + &substs, + ); let instance = ty::Instance::resolve_closure( *self.tcx, def_id, diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index a729e3a5dda82..be34b8705eb18 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -122,7 +122,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { // FIXME(CTFE): forbid drop in const eval let place = self.eval_place(location)?; let ty = self.place_ty(location); - let ty = self.tcx.trans_apply_param_substs(self.substs(), &ty); + let ty = self.tcx.subst_and_normalize_erasing_regions( + self.substs(), + ty::ParamEnv::reveal_all(), + &ty, + ); trace!("TerminatorKind::drop: {:?}, type {}", location, ty); let instance = ::monomorphize::resolve_drop_in_place(*self.tcx, ty); diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 561500a3f3c5f..7fd2ea265de80 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -523,11 +523,17 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { // have to instantiate all methods of the trait being cast to, so we // can build the appropriate vtable. mir::Rvalue::Cast(mir::CastKind::Unsize, ref operand, target_ty) => { - let target_ty = self.tcx.trans_apply_param_substs(self.param_substs, - &target_ty); + let target_ty = self.tcx.subst_and_normalize_erasing_regions( + self.param_substs, + ty::ParamEnv::reveal_all(), + &target_ty, + ); let source_ty = operand.ty(self.mir, self.tcx); - let source_ty = self.tcx.trans_apply_param_substs(self.param_substs, - &source_ty); + let source_ty = self.tcx.subst_and_normalize_erasing_regions( + self.param_substs, + ty::ParamEnv::reveal_all(), + &source_ty, + ); let (source_ty, target_ty) = find_vtable_types_for_unsizing(self.tcx, source_ty, target_ty); @@ -543,14 +549,20 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { } mir::Rvalue::Cast(mir::CastKind::ReifyFnPointer, ref operand, _) => { let fn_ty = operand.ty(self.mir, self.tcx); - let fn_ty = self.tcx.trans_apply_param_substs(self.param_substs, - &fn_ty); + let fn_ty = self.tcx.subst_and_normalize_erasing_regions( + self.param_substs, + ty::ParamEnv::reveal_all(), + &fn_ty, + ); visit_fn_use(self.tcx, fn_ty, false, &mut self.output); } mir::Rvalue::Cast(mir::CastKind::ClosureFnPointer, ref operand, _) => { let source_ty = operand.ty(self.mir, self.tcx); - let source_ty = self.tcx.trans_apply_param_substs(self.param_substs, - &source_ty); + let source_ty = self.tcx.subst_and_normalize_erasing_regions( + self.param_substs, + ty::ParamEnv::reveal_all(), + &source_ty, + ); match source_ty.sty { ty::TyClosure(def_id, substs) => { let instance = monomorphize::resolve_closure( @@ -595,14 +607,22 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { match *kind { mir::TerminatorKind::Call { ref func, .. } => { let callee_ty = func.ty(self.mir, tcx); - let callee_ty = tcx.trans_apply_param_substs(self.param_substs, &callee_ty); + let callee_ty = tcx.subst_and_normalize_erasing_regions( + self.param_substs, + ty::ParamEnv::reveal_all(), + &callee_ty, + ); visit_fn_use(self.tcx, callee_ty, true, &mut self.output); } mir::TerminatorKind::Drop { ref location, .. } | mir::TerminatorKind::DropAndReplace { ref location, .. } => { let ty = location.ty(self.mir, self.tcx) .to_ty(self.tcx); - let ty = tcx.trans_apply_param_substs(self.param_substs, &ty); + let ty = tcx.subst_and_normalize_erasing_regions( + self.param_substs, + ty::ParamEnv::reveal_all(), + &ty, + ); visit_drop_use(self.tcx, ty, true, self.output); } mir::TerminatorKind::Goto { .. } | @@ -1155,8 +1175,11 @@ fn collect_const<'a, 'tcx>( let val = match constant.val { ConstVal::Unevaluated(def_id, substs) => { let param_env = ty::ParamEnv::reveal_all(); - let substs = tcx.trans_apply_param_substs(param_substs, - &substs); + let substs = tcx.subst_and_normalize_erasing_regions( + param_substs, + param_env, + &substs, + ); let instance = ty::Instance::resolve(tcx, param_env, def_id, diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index d65c1e03298a1..3789342b3891d 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -645,7 +645,11 @@ fn characteristic_def_id_of_trans_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if let Some(impl_def_id) = tcx.impl_of_method(def_id) { // This is a method within an inherent impl, find out what the // self-type is: - let impl_self_ty = tcx.trans_impl_self_ty(impl_def_id, instance.substs); + let impl_self_ty = tcx.subst_and_normalize_erasing_regions( + instance.substs, + ty::ParamEnv::reveal_all(), + &tcx.type_of(impl_def_id), + ); if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) { return Some(def_id); } diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index b8a0e0f89073d..6380d2a5c15fe 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -19,7 +19,7 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::mir::*; use rustc::mir::visit::*; -use rustc::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{self, Instance, Ty, TyCtxt}; use rustc::ty::subst::{Subst,Substs}; use std::collections::VecDeque; @@ -129,8 +129,12 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { let callee_mir = match ty::queries::optimized_mir::try_get(self.tcx, callsite.location.span, callsite.callee) { - Ok(ref callee_mir) if self.should_inline(callsite, callee_mir) => { - subst_and_normalize(callee_mir, self.tcx, &callsite.substs, param_env) + Ok(callee_mir) if self.should_inline(callsite, callee_mir) => { + self.tcx.subst_and_normalize_erasing_regions( + &callsite.substs, + param_env, + callee_mir, + ) } Ok(_) => continue, @@ -664,30 +668,6 @@ fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx.layout_of(param_env.and(ty)).ok().map(|layout| layout.size.bytes()) } -fn subst_and_normalize<'a, 'tcx: 'a>( - mir: &Mir<'tcx>, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - substs: &'tcx ty::subst::Substs<'tcx>, - param_env: ty::ParamEnv<'tcx>, -) -> Mir<'tcx> { - struct Folder<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - substs: &'tcx ty::subst::Substs<'tcx>, - } - impl<'a, 'tcx: 'a> ty::fold::TypeFolder<'tcx, 'tcx> for Folder<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { - self.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - self.tcx.trans_apply_param_substs_env(&self.substs, self.param_env, &t) - } - } - let mut f = Folder { tcx, param_env, substs }; - mir.fold_with(&mut f) -} - /** * Integrator. * diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index 683e1835968f8..c13b91eb3b6bd 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -429,7 +429,11 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| { // If the method does *not* belong to a trait, proceed if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { - let impl_self_ty = cx.tcx.trans_impl_self_ty(impl_def_id, instance.substs); + let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions( + instance.substs, + ty::ParamEnv::reveal_all(), + &cx.tcx.type_of(impl_def_id), + ); // Only "class" methods are generally understood by LLVM, // so avoid methods on other types (e.g. `<*mut T>::null`). diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index a92a59edfde1f..0533b04a0c1d8 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -109,7 +109,11 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { pub fn monomorphize(&self, value: &T) -> T where T: TypeFoldable<'tcx> { - self.cx.tcx.trans_apply_param_substs(self.param_substs, value) + self.cx.tcx.subst_and_normalize_erasing_regions( + self.param_substs, + ty::ParamEnv::reveal_all(), + value, + ) } pub fn set_debug_loc(&mut self, bx: &Builder, source_info: mir::SourceInfo) { From fc04c41a4075cef1cc9c84911fbdc17b5b9265b7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 9 Mar 2018 16:44:20 -0500 Subject: [PATCH 24/27] add a debug assertion that only outlives-oblig. result from norm. --- .../normalize_erasing_regions.rs | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/librustc_traits/normalize_erasing_regions.rs b/src/librustc_traits/normalize_erasing_regions.rs index 805bf1030b3f6..14f8694dbf72a 100644 --- a/src/librustc_traits/normalize_erasing_regions.rs +++ b/src/librustc_traits/normalize_erasing_regions.rs @@ -10,7 +10,7 @@ use rustc::traits::{Normalized, ObligationCause}; use rustc::traits::query::NoSolution; -use rustc::ty::{ParamEnvAnd, Ty, TyCtxt}; +use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt}; use rustc::util::common::CellUsizeExt; crate fn normalize_ty_after_erasing_regions<'tcx>( @@ -18,15 +18,27 @@ crate fn normalize_ty_after_erasing_regions<'tcx>( goal: ParamEnvAnd<'tcx, Ty<'tcx>>, ) -> Ty<'tcx> { let ParamEnvAnd { param_env, value } = goal; - tcx.sess.perf_stats.normalize_ty_after_erasing_regions.increment(); + tcx.sess + .perf_stats + .normalize_ty_after_erasing_regions + .increment(); tcx.infer_ctxt().enter(|infcx| { let cause = ObligationCause::dummy(); match infcx.at(&cause, param_env).normalize(&value) { - Ok(Normalized { value: normalized_value, obligations: _ }) => { - // ^^^^^^^^^^^ - // We don't care about the `obligations`, - // they are always only region relations, - // and we are about to erase those anyway. + Ok(Normalized { + value: normalized_value, + obligations: normalized_obligations, + }) => { + // We don't care about the `obligations`; they are + // always only region relations, and we are about to + // erase those anyway: + debug_assert_eq!( + normalized_obligations + .iter() + .find(|p| not_outlives_predicate(&p.predicate)), + None, + ); + let normalized_value = infcx.resolve_type_vars_if_possible(&normalized_value); let normalized_value = infcx.tcx.erase_regions(&normalized_value); tcx.lift_to_global(&normalized_value).unwrap() @@ -35,3 +47,16 @@ crate fn normalize_ty_after_erasing_regions<'tcx>( } }) } + +fn not_outlives_predicate(p: &ty::Predicate<'_>) -> bool { + match p { + ty::Predicate::RegionOutlives(..) | ty::Predicate::TypeOutlives(..) => false, + ty::Predicate::Trait(..) + | ty::Predicate::Projection(..) + | ty::Predicate::WellFormed(..) + | ty::Predicate::ObjectSafe(..) + | ty::Predicate::ClosureKind(..) + | ty::Predicate::Subtype(..) + | ty::Predicate::ConstEvaluatable(..) => true, + } +} From d32673858698b8e733c9b42d82ad01f16c487eed Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 11 Mar 2018 07:08:52 -0400 Subject: [PATCH 25/27] replace inline docs with references to rustc-guide --- src/librustc/infer/canonical.rs | 75 ++++++++------------------------- 1 file changed, 18 insertions(+), 57 deletions(-) diff --git a/src/librustc/infer/canonical.rs b/src/librustc/infer/canonical.rs index 83194b5e504e5..73fb2a92de6e9 100644 --- a/src/librustc/infer/canonical.rs +++ b/src/librustc/infer/canonical.rs @@ -25,6 +25,11 @@ //! M, into constraints in our source context. This process of //! translating the results back is done by the //! `instantiate_query_result` method. +//! +//! For a more detailed look at what is happening here, check +//! out the [chapter in the rustc guide][c]. +//! +//! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits-canonicalization.html use infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TypeVariableOrigin}; use rustc_data_structures::indexed_vec::Idx; @@ -270,64 +275,10 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// have been ambiguous; you should check the certainty level of /// the query before applying this function.) /// - /// It's easiest to explain what is happening here by - /// example. Imagine we start out with the query `?A: Foo<'static, - /// ?B>`. We would canonicalize that by introducing two variables: - /// - /// ?0: Foo<'?1, ?2> - /// - /// (Note that all regions get replaced with variables always, - /// even "known" regions like `'static`.) After canonicalization, - /// we also get back an array with the "original values" for each - /// canonicalized variable: - /// - /// [?A, 'static, ?B] - /// - /// Now we do the query and get back some result R. As part of that - /// result, we'll have an array of values for the canonical inputs. - /// For example, the canonical result might be: - /// - /// ``` - /// for<2> { - /// values = [ Vec, '1, ?0 ] - /// ^^ ^^ ^^ these are variables in the result! - /// ... - /// } - /// ``` - /// - /// Note that this result is itself canonical and may include some - /// variables (in this case, `?0`). + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc guide][c]. /// - /// What we want to do conceptually is to (a) instantiate each of the - /// canonical variables in the result with a fresh inference variable - /// and then (b) unify the values in the result with the original values. - /// Doing step (a) would yield a result of - /// - /// ``` - /// { - /// values = [ Vec, '?X, ?C ] - /// ^^ ^^^ fresh inference variables in `self` - /// .. - /// } - /// ``` - /// - /// Step (b) would then unify: - /// - /// ``` - /// ?A with Vec - /// 'static with '?X - /// ?B with ?C - /// ``` - /// - /// But what we actually do is a mildly optimized variant of - /// that. Rather than eagerly instantiating all of the canonical - /// values in the result with variables, we instead walk the - /// vector of values, looking for cases where the value is just a - /// canonical variable. In our example, `values[2]` is `?C`, so - /// that we means we can deduce that `?C := ?B and `'?X := - /// 'static`. This gives us a partial set of values. Anything for - /// which we do not find a value, we create an inference variable - /// for. **Then** we unify. + /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits-canonicalization.html#processing-the-canonicalized-query-result pub fn instantiate_query_result( &self, cause: &ObligationCause<'tcx>, @@ -509,6 +460,11 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// T: Trait<'?0> /// /// with a mapping M that maps `'?0` to `'static`. + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc guide][c]. + /// + /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits-canonicalization.html#canonicalizing-the-query pub fn canonicalize_query(&self, value: &V) -> (V::Canonicalized, CanonicalVarValues<'tcx>) where V: Canonicalize<'gcx, 'tcx>, @@ -541,6 +497,11 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// things) includes a mapping to `'?0 := 'static`. When /// canonicalizing this query result R, we would leave this /// reference to `'static` alone. + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc guide][c]. + /// + /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits-canonicalization.html#canonicalizing-the-query-result pub fn canonicalize_response( &self, value: &V, From 29dc902beb037c4cdcad3569d3fc7b72424e8779 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 11 Mar 2018 07:09:06 -0400 Subject: [PATCH 26/27] remove dead code --- src/librustc/infer/canonical.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/librustc/infer/canonical.rs b/src/librustc/infer/canonical.rs index 73fb2a92de6e9..21d60bb8b36fc 100644 --- a/src/librustc/infer/canonical.rs +++ b/src/librustc/infer/canonical.rs @@ -518,17 +518,6 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { } } -impl<'cx, 'gcx> TyCtxt<'cx, 'gcx, 'gcx> { - /// Canonicalize a value that doesn't have any inference variables - /// or other things (and we know it). - pub fn canonicalize_global(self, value: &V) -> (V::Canonicalized, CanonicalVarValues<'gcx>) - where - V: Canonicalize<'gcx, 'gcx>, - { - Canonicalizer::canonicalize(value, None, self, CanonicalizeAllFreeRegions(false)) - } -} - /// If this flag is true, then all free regions will be replaced with /// a canonical var. This is used to make queries as generic as /// possible. For example, the query `F: Foo<'static>` would be From 17c4103f3f0cc8bd4dea9de5e7ef155daf363cfe Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 11 Mar 2018 07:13:14 -0400 Subject: [PATCH 27/27] add "text" sections for things that seem likely to be a problem --- src/librustc/infer/canonical.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/librustc/infer/canonical.rs b/src/librustc/infer/canonical.rs index 21d60bb8b36fc..9519baa3ff7bc 100644 --- a/src/librustc/infer/canonical.rs +++ b/src/librustc/infer/canonical.rs @@ -457,7 +457,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// *also* replace all free regions whatsoever. So for example a /// query like `T: Trait<'static>` would be canonicalized to /// - /// T: Trait<'?0> + /// ```text + /// T: Trait<'?0> + /// ``` /// /// with a mapping M that maps `'?0` to `'static`. /// @@ -486,7 +488,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// there was an input query `T: Trait<'static>`, it would have /// been canonicalized to /// - /// T: Trait<'?0> + /// ```text + /// T: Trait<'?0> + /// ``` /// /// with a mapping M that maps `'?0` to `'static`. But if we found that there /// exists only one possible impl of `Trait`, and it looks like