From 622a78cd54a090f3f53f3a0f6872073245c80bc5 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 1 Oct 2017 17:15:15 +0200 Subject: [PATCH 1/3] handle nested generics in Generics::type_param/region_param Fixes #44952. --- src/librustc/infer/error_reporting/mod.rs | 2 +- src/librustc/ty/mod.rs | 28 +++++++++++---- src/librustc/ty/util.rs | 4 +-- .../lifetime-doesnt-live-long-enough.rs | 10 ++++++ .../lifetime-doesnt-live-long-enough.stderr | 35 ++++++++++++++++++- 5 files changed, 68 insertions(+), 11 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 3f22950fc77..72f24b62e68 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -795,7 +795,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // Account for the case where `did` corresponds to `Self`, which doesn't have // the expected type argument. if generics.types.len() > 0 { - let type_param = generics.type_param(param); + let type_param = generics.type_param(param, self.tcx); let hir = &self.tcx.hir; hir.as_local_node_id(type_param.def_id).map(|id| { // Get the `hir::TyParam` to verify wether it already has any bounds. diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index da635ec80fc..c57ecb9b69c 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -729,7 +729,7 @@ pub struct Generics { pub has_late_bound_regions: Option, } -impl Generics { +impl<'a, 'gcx, 'tcx> Generics { pub fn parent_count(&self) -> usize { self.parent_regions as usize + self.parent_types as usize } @@ -742,14 +742,28 @@ impl Generics { self.parent_count() + self.own_count() } - pub fn region_param(&self, param: &EarlyBoundRegion) -> &RegionParameterDef { - assert_eq!(self.parent_count(), 0); - &self.regions[param.index as usize - self.has_self as usize] + pub fn region_param(&'tcx self, + param: &EarlyBoundRegion, + tcx: TyCtxt<'a, 'gcx, 'tcx>) + -> &'tcx RegionParameterDef + { + if let Some(index) = param.index.checked_sub(self.parent_count() as u32) { + &self.regions[index as usize - self.has_self as usize] + } else { + tcx.generics_of(self.parent.expect("parent_count>0 but no parent?")) + .region_param(param, tcx) + } } - pub fn type_param(&self, param: &ParamTy) -> &TypeParameterDef { - assert_eq!(self.parent_count(), 0); - &self.types[param.idx as usize - self.has_self as usize - self.regions.len()] + pub fn type_param(&'tcx self, + param: &ParamTy, + tcx: TyCtxt<'a, 'gcx, 'tcx>) -> &TypeParameterDef { + if let Some(idx) = param.idx.checked_sub(self.parent_count() as u32) { + &self.types[idx as usize - self.has_self as usize - self.regions.len()] + } else { + tcx.generics_of(self.parent.expect("parent_count>0 but no parent?")) + .type_param(param, tcx) + } } } diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 27819f551b9..c8037ce081a 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -515,11 +515,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { let result = item_substs.iter().zip(impl_substs.iter()) .filter(|&(_, &k)| { if let Some(&ty::RegionKind::ReEarlyBound(ref ebr)) = k.as_region() { - !impl_generics.region_param(ebr).pure_wrt_drop + !impl_generics.region_param(ebr, self).pure_wrt_drop } else if let Some(&ty::TyS { sty: ty::TypeVariants::TyParam(ref pt), .. }) = k.as_type() { - !impl_generics.type_param(pt).pure_wrt_drop + !impl_generics.type_param(pt, self).pure_wrt_drop } else { // not a type or region param - this should be reported // as an error. diff --git a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs index 465b4271035..a41d8b0e342 100644 --- a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs +++ b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs @@ -28,4 +28,14 @@ struct Foo { foo: &'static T } +trait X {} + +struct Nested(K); +impl Nested { + fn generic_in_parent<'a, L: X<&'a Nested>>() { + } + fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { + } +} + fn main() {} diff --git a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr index e17a660c591..c74302fe9b3 100644 --- a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr +++ b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr @@ -26,5 +26,38 @@ note: ...so that the reference type `&'static T` does not outlive the data it po 28 | foo: &'static T | ^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error[E0309]: the parameter type `K` may not live long enough + --> $DIR/lifetime-doesnt-live-long-enough.rs:35:5 + | +34 | impl Nested { + | - help: consider adding an explicit lifetime bound `K: 'a`... +35 | / fn generic_in_parent<'a, L: X<&'a Nested>>() { +36 | | } + | |_____^ + | +note: ...so that the reference type `&'a Nested` does not outlive the data it points at + --> $DIR/lifetime-doesnt-live-long-enough.rs:35:5 + | +35 | / fn generic_in_parent<'a, L: X<&'a Nested>>() { +36 | | } + | |_____^ + +error[E0309]: the parameter type `M` may not live long enough + --> $DIR/lifetime-doesnt-live-long-enough.rs:37:5 + | +37 | fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { + | ^ -- help: consider adding an explicit lifetime bound `M: 'a`... + | _____| + | | +38 | | } + | |_____^ + | +note: ...so that the reference type `&'a Nested` does not outlive the data it points at + --> $DIR/lifetime-doesnt-live-long-enough.rs:37:5 + | +37 | / fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { +38 | | } + | |_____^ + +error: aborting due to 4 previous errors From ad9986605f5f743ee8cc3da8189a68815313ab07 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 1 Oct 2017 23:03:48 +0200 Subject: [PATCH 2/3] fix handling of Self --- src/librustc/infer/error_reporting/mod.rs | 3 +-- src/librustc/ty/mod.rs | 12 ++++++++++-- src/librustc/ty/util.rs | 4 +++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 72f24b62e68..3c3c5395011 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -794,8 +794,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let generics = self.tcx.generics_of(did); // Account for the case where `did` corresponds to `Self`, which doesn't have // the expected type argument. - if generics.types.len() > 0 { - let type_param = generics.type_param(param, self.tcx); + if let Some(type_param) = generics.type_param(param, self.tcx) { let hir = &self.tcx.hir; hir.as_local_node_id(type_param.def_id).map(|id| { // Get the `hir::TyParam` to verify wether it already has any bounds. diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index c57ecb9b69c..46d2f65f34e 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -755,11 +755,19 @@ impl<'a, 'gcx, 'tcx> Generics { } } + /// Returns the `TypeParameterDef` associated with this `ParamTy`, or `None` + /// if `param` is `self`. pub fn type_param(&'tcx self, param: &ParamTy, - tcx: TyCtxt<'a, 'gcx, 'tcx>) -> &TypeParameterDef { + tcx: TyCtxt<'a, 'gcx, 'tcx>) + -> Option<&TypeParameterDef> { if let Some(idx) = param.idx.checked_sub(self.parent_count() as u32) { - &self.types[idx as usize - self.has_self as usize - self.regions.len()] + let type_param_start = (self.has_self as usize) + self.regions.len(); + if let Some(idx) = (idx as usize).checked_sub(type_param_start) { + Some(&self.types[idx]) + } else { + None + } } else { tcx.generics_of(self.parent.expect("parent_count>0 but no parent?")) .type_param(param, tcx) diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index c8037ce081a..e4abe5ee615 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -519,7 +519,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } else if let Some(&ty::TyS { sty: ty::TypeVariants::TyParam(ref pt), .. }) = k.as_type() { - !impl_generics.type_param(pt, self).pure_wrt_drop + !impl_generics.type_param(pt, self) + .expect("drop impl param doesn't have a ParameterDef?") + .pure_wrt_drop } else { // not a type or region param - this should be reported // as an error. From 706e52e2cc1e1ffab4face61f5906164236a0da8 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 2 Oct 2017 10:43:36 +0200 Subject: [PATCH 3/3] fix handling of `Self` --- src/librustc/infer/error_reporting/mod.rs | 3 +- src/librustc/ty/mod.rs | 37 ++++++++++--- src/librustc/ty/util.rs | 4 +- .../lifetime-doesnt-live-long-enough.rs | 6 +- .../lifetime-doesnt-live-long-enough.stderr | 55 ++++++++++++++----- 5 files changed, 79 insertions(+), 26 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 3c3c5395011..895894a0bb2 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -794,7 +794,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let generics = self.tcx.generics_of(did); // Account for the case where `did` corresponds to `Self`, which doesn't have // the expected type argument. - if let Some(type_param) = generics.type_param(param, self.tcx) { + if !param.is_self() { + let type_param = generics.type_param(param, self.tcx); let hir = &self.tcx.hir; hir.as_local_node_id(type_param.def_id).map(|id| { // Get the `hir::TyParam` to verify wether it already has any bounds. diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 46d2f65f34e..bc5a056dd33 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -713,6 +713,13 @@ impl ty::EarlyBoundRegion { /// Information about the formal type/lifetime parameters associated /// with an item or method. Analogous to hir::Generics. +/// +/// Note that in the presence of a `Self` parameter, the ordering here +/// is different from the ordering in a Substs. Substs are ordered as +/// Self, *Regions, *Other Type Params, (...child generics) +/// while this struct is ordered as +/// regions = Regions +/// types = [Self, *Other Type Params] #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct Generics { pub parent: Option, @@ -755,18 +762,34 @@ impl<'a, 'gcx, 'tcx> Generics { } } - /// Returns the `TypeParameterDef` associated with this `ParamTy`, or `None` - /// if `param` is `self`. + /// Returns the `TypeParameterDef` associated with this `ParamTy`. pub fn type_param(&'tcx self, param: &ParamTy, tcx: TyCtxt<'a, 'gcx, 'tcx>) - -> Option<&TypeParameterDef> { + -> &TypeParameterDef { if let Some(idx) = param.idx.checked_sub(self.parent_count() as u32) { - let type_param_start = (self.has_self as usize) + self.regions.len(); - if let Some(idx) = (idx as usize).checked_sub(type_param_start) { - Some(&self.types[idx]) + // non-Self type parameters are always offset by exactly + // `self.regions.len()`. In the absence of a Self, this is obvious, + // but even in the absence of a `Self` we just have to "compensate" + // for the regions: + // + // For example, for `trait Foo<'a, 'b, T1, T2>`, the + // situation is: + // Substs: + // 0 1 2 3 4 + // Self 'a 'b T1 T2 + // generics.types: + // 0 1 2 + // Self T1 T2 + // And it can be seen that to move from a substs offset to a + // generics offset you just have to offset by the number of regions. + let type_param_offset = self.regions.len(); + if let Some(idx) = (idx as usize).checked_sub(type_param_offset) { + assert!(!(self.has_self && idx == 0)); + &self.types[idx] } else { - None + assert!(self.has_self && idx == 0); + &self.types[0] } } else { tcx.generics_of(self.parent.expect("parent_count>0 but no parent?")) diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index e4abe5ee615..c8037ce081a 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -519,9 +519,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } else if let Some(&ty::TyS { sty: ty::TypeVariants::TyParam(ref pt), .. }) = k.as_type() { - !impl_generics.type_param(pt, self) - .expect("drop impl param doesn't have a ParameterDef?") - .pure_wrt_drop + !impl_generics.type_param(pt, self).pure_wrt_drop } else { // not a type or region param - this should be reported // as an error. diff --git a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs index a41d8b0e342..242520cdb32 100644 --- a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs +++ b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs @@ -28,7 +28,11 @@ struct Foo { foo: &'static T } -trait X {} +trait X: Sized { + fn foo<'a, L: X<&'a Nested>>(); + // check that we give a sane error for `Self` + fn bar<'a, L: X<&'a Nested>>(); +} struct Nested(K); impl Nested { diff --git a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr index c74302fe9b3..42e4f28260e 100644 --- a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr +++ b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr @@ -27,37 +27,64 @@ note: ...so that the reference type `&'static T` does not outlive the data it po | ^^^^^^^^^^^^^^^ error[E0309]: the parameter type `K` may not live long enough - --> $DIR/lifetime-doesnt-live-long-enough.rs:35:5 + --> $DIR/lifetime-doesnt-live-long-enough.rs:32:5 | -34 | impl Nested { +31 | trait X: Sized { + | - help: consider adding an explicit lifetime bound `K: 'a`... +32 | fn foo<'a, L: X<&'a Nested>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...so that the reference type `&'a Nested` does not outlive the data it points at + --> $DIR/lifetime-doesnt-live-long-enough.rs:32:5 + | +32 | fn foo<'a, L: X<&'a Nested>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0309]: the parameter type `Self` may not live long enough + --> $DIR/lifetime-doesnt-live-long-enough.rs:34:5 + | +34 | fn bar<'a, L: X<&'a Nested>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `Self: 'a`... +note: ...so that the reference type `&'a Nested` does not outlive the data it points at + --> $DIR/lifetime-doesnt-live-long-enough.rs:34:5 + | +34 | fn bar<'a, L: X<&'a Nested>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0309]: the parameter type `K` may not live long enough + --> $DIR/lifetime-doesnt-live-long-enough.rs:39:5 + | +38 | impl Nested { | - help: consider adding an explicit lifetime bound `K: 'a`... -35 | / fn generic_in_parent<'a, L: X<&'a Nested>>() { -36 | | } +39 | / fn generic_in_parent<'a, L: X<&'a Nested>>() { +40 | | } | |_____^ | note: ...so that the reference type `&'a Nested` does not outlive the data it points at - --> $DIR/lifetime-doesnt-live-long-enough.rs:35:5 + --> $DIR/lifetime-doesnt-live-long-enough.rs:39:5 | -35 | / fn generic_in_parent<'a, L: X<&'a Nested>>() { -36 | | } +39 | / fn generic_in_parent<'a, L: X<&'a Nested>>() { +40 | | } | |_____^ error[E0309]: the parameter type `M` may not live long enough - --> $DIR/lifetime-doesnt-live-long-enough.rs:37:5 + --> $DIR/lifetime-doesnt-live-long-enough.rs:41:5 | -37 | fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { +41 | fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { | ^ -- help: consider adding an explicit lifetime bound `M: 'a`... | _____| | | -38 | | } +42 | | } | |_____^ | note: ...so that the reference type `&'a Nested` does not outlive the data it points at - --> $DIR/lifetime-doesnt-live-long-enough.rs:37:5 + --> $DIR/lifetime-doesnt-live-long-enough.rs:41:5 | -37 | / fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { -38 | | } +41 | / fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { +42 | | } | |_____^ -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors