From c5befdc6308f8624b4b5fa8808ba0af70d2fbcf1 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Tue, 24 Jan 2017 17:17:06 +0200 Subject: [PATCH] rustc: always keep an explicit lifetime in trait objects. --- src/librustc/hir/intravisit.rs | 13 +- src/librustc/hir/lowering.rs | 24 ++- src/librustc/hir/mod.rs | 2 +- src/librustc/hir/print.rs | 17 +- src/librustc/middle/resolve_lifetime.rs | 12 +- .../calculate_svh/svh_visitor.rs | 2 +- src/librustc_passes/ast_validation.rs | 11 ++ src/librustc_passes/diagnostics.rs | 1 + src/librustc_typeck/astconv.rs | 167 ++++-------------- src/librustc_typeck/collect.rs | 19 +- src/librustc_typeck/diagnostics.rs | 1 - src/librustdoc/clean/mod.rs | 28 +-- ...n-bounds-on-objects-and-type-parameters.rs | 6 +- 13 files changed, 137 insertions(+), 166 deletions(-) diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index d71263bea0046..4b3e0d29101e4 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -301,7 +301,7 @@ pub trait Visitor<'v> : Sized { fn visit_ty_param_bound(&mut self, bounds: &'v TyParamBound) { walk_ty_param_bound(self, bounds) } - fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef, m: &'v TraitBoundModifier) { + fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef, m: TraitBoundModifier) { walk_poly_trait_ref(self, t, m) } fn visit_variant_data(&mut self, @@ -421,7 +421,7 @@ pub fn walk_lifetime_def<'v, V: Visitor<'v>>(visitor: &mut V, lifetime_def: &'v pub fn walk_poly_trait_ref<'v, V>(visitor: &mut V, trait_ref: &'v PolyTraitRef, - _modifier: &'v TraitBoundModifier) + _modifier: TraitBoundModifier) where V: Visitor<'v> { walk_list!(visitor, visit_lifetime_def, &trait_ref.bound_lifetimes); @@ -566,8 +566,11 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) { visitor.visit_ty(ty); visitor.visit_nested_body(length) } - TyTraitObject(ref bounds) => { - walk_list!(visitor, visit_ty_param_bound, bounds); + TyTraitObject(ref bounds, ref lifetime) => { + for bound in bounds { + visitor.visit_poly_trait_ref(bound, TraitBoundModifier::None); + } + visitor.visit_lifetime(lifetime); } TyImplTrait(ref bounds) => { walk_list!(visitor, visit_ty_param_bound, bounds); @@ -695,7 +698,7 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v pub fn walk_ty_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v TyParamBound) { match *bound { - TraitTyParamBound(ref typ, ref modifier) => { + TraitTyParamBound(ref typ, modifier) => { visitor.visit_poly_trait_ref(typ, modifier); } RegionTyParamBound(ref lifetime) => { diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index f7d40350dc932..8a4acb3d03880 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -360,7 +360,23 @@ impl<'a> LoweringContext<'a> { hir::TyTypeof(self.record_body(expr, None)) } TyKind::TraitObject(ref bounds) => { - hir::TyTraitObject(self.lower_bounds(bounds)) + let mut lifetime_bound = None; + let bounds = bounds.iter().filter_map(|bound| { + match *bound { + TraitTyParamBound(ref ty, TraitBoundModifier::None) => { + Some(self.lower_poly_trait_ref(ty)) + } + TraitTyParamBound(_, TraitBoundModifier::Maybe) => None, + RegionTyParamBound(ref lifetime) => { + lifetime_bound = Some(self.lower_lifetime(lifetime)); + None + } + } + }).collect(); + let lifetime_bound = lifetime_bound.unwrap_or_else(|| { + self.elided_lifetime(t.span) + }); + hir::TyTraitObject(bounds, lifetime_bound) } TyKind::ImplTrait(ref bounds) => { hir::TyImplTrait(self.lower_bounds(bounds)) @@ -2361,20 +2377,20 @@ impl<'a> LoweringContext<'a> { hir::QPath::Resolved(None, path) => { // Turn trait object paths into `TyTraitObject` instead. if let Def::Trait(_) = path.def { - let principal = hir::TraitTyParamBound(hir::PolyTraitRef { + let principal = hir::PolyTraitRef { bound_lifetimes: hir_vec![], trait_ref: hir::TraitRef { path: path.and_then(|path| path), ref_id: id, }, span, - }, hir::TraitBoundModifier::None); + }; // The original ID is taken by the `PolyTraitRef`, // so the `Ty` itself needs a different one. id = self.next_id(); - hir::TyTraitObject(hir_vec![principal]) + hir::TyTraitObject(hir_vec![principal], self.elided_lifetime(span)) } else { hir::TyPath(hir::QPath::Resolved(None, path)) } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 94cb33b138c58..4ebe416e1bfe6 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1205,7 +1205,7 @@ pub enum Ty_ { TyPath(QPath), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TyTraitObject(TyParamBounds), + TyTraitObject(HirVec, Lifetime), /// An `impl Bound1 + Bound2 + Bound3` type /// where `Bound` is a trait or a lifetime. TyImplTrait(TyParamBounds), diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 8e866f5717498..e058c48c59149 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -416,8 +416,21 @@ impl<'a> State<'a> { hir::TyPath(ref qpath) => { self.print_qpath(qpath, false)? } - hir::TyTraitObject(ref bounds) => { - self.print_bounds("", &bounds[..])?; + hir::TyTraitObject(ref bounds, ref lifetime) => { + let mut first = true; + for bound in bounds { + self.nbsp()?; + if first { + first = false; + } else { + self.word_space("+")?; + } + self.print_poly_trait_ref(bound)?; + } + if !lifetime.is_elided() { + self.word_space("+")?; + self.print_lifetime(lifetime)?; + } } hir::TyImplTrait(ref bounds) => { self.print_bounds("impl ", &bounds[..])?; diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index aa118891d9825..88da47c2f0c44 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -322,6 +322,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { intravisit::walk_ty(this, ty); }); } + hir::TyTraitObject(ref bounds, ref lifetime) => { + for bound in bounds { + self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); + } + if !lifetime.is_elided() { + self.visit_lifetime(lifetime); + } + } _ => { intravisit::walk_ty(self, ty) } @@ -441,7 +449,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_poly_trait_ref(&mut self, trait_ref: &'tcx hir::PolyTraitRef, - _modifier: &'tcx hir::TraitBoundModifier) { + _modifier: hir::TraitBoundModifier) { debug!("visit_poly_trait_ref trait_ref={:?}", trait_ref); if !self.trait_ref_hack || !trait_ref.bound_lifetimes.is_empty() { @@ -962,7 +970,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { fn visit_poly_trait_ref(&mut self, trait_ref: &hir::PolyTraitRef, - modifier: &hir::TraitBoundModifier) { + modifier: hir::TraitBoundModifier) { self.binder_depth += 1; intravisit::walk_poly_trait_ref(self, trait_ref, modifier); self.binder_depth -= 1; diff --git a/src/librustc_incremental/calculate_svh/svh_visitor.rs b/src/librustc_incremental/calculate_svh/svh_visitor.rs index 69237f406760d..c7512f2971b33 100644 --- a/src/librustc_incremental/calculate_svh/svh_visitor.rs +++ b/src/librustc_incremental/calculate_svh/svh_visitor.rs @@ -828,7 +828,7 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has visit::walk_ty_param_bound(self, bounds) } - fn visit_poly_trait_ref(&mut self, t: &'tcx PolyTraitRef, m: &'tcx TraitBoundModifier) { + fn visit_poly_trait_ref(&mut self, t: &'tcx PolyTraitRef, m: TraitBoundModifier) { debug!("visit_poly_trait_ref: st={:?}", self.st); SawPolyTraitRef.hash(self.st); m.hash(self.st); diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 9720bb8426475..0933fdfd357cd 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -144,6 +144,17 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); } TyKind::TraitObject(ref bounds) => { + let mut any_lifetime_bounds = false; + for bound in bounds { + if let RegionTyParamBound(ref lifetime) = *bound { + if any_lifetime_bounds { + span_err!(self.session, lifetime.span, E0226, + "only a single explicit lifetime bound is permitted"); + break; + } + any_lifetime_bounds = true; + } + } self.no_questions_in_bounds(bounds, "trait object types", false); } TyKind::ImplTrait(ref bounds) => { diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index c414d71836818..ef871959176af 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -244,6 +244,7 @@ match 5u32 { } register_diagnostics! { + E0226, // only a single explicit lifetime bound is permitted E0472, // asm! is unsupported on this target E0561, // patterns aren't allowed in function pointer types E0571, // `break` with a value in a non-`loop`-loop diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 324d569cb8dce..5c71947c2077e 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -453,24 +453,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { (self.tcx().mk_ty(ty::TyTuple(inputs)), output_binding) } - pub fn instantiate_poly_trait_ref(&self, - rscope: &RegionScope, - ast_trait_ref: &hir::PolyTraitRef, - self_ty: Ty<'tcx>, - poly_projections: &mut Vec>) - -> ty::PolyTraitRef<'tcx> - { - let trait_ref = &ast_trait_ref.trait_ref; - let trait_def_id = self.trait_def_id(trait_ref); - self.ast_path_to_poly_trait_ref(rscope, - trait_ref.path.span, - trait_def_id, - self_ty, - trait_ref.ref_id, - trait_ref.path.segments.last().unwrap(), - poly_projections) - } - /// Instantiates the path for the given trait reference, assuming that it's /// bound to a valid trait type. Returns the def_id for the defining trait. /// Fails if the type is a type other than a trait type. @@ -505,17 +487,17 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } } - fn ast_path_to_poly_trait_ref(&self, + pub fn instantiate_poly_trait_ref(&self, rscope: &RegionScope, - span: Span, - trait_def_id: DefId, + ast_trait_ref: &hir::PolyTraitRef, self_ty: Ty<'tcx>, - path_id: ast::NodeId, - trait_segment: &hir::PathSegment, poly_projections: &mut Vec>) -> ty::PolyTraitRef<'tcx> { - debug!("ast_path_to_poly_trait_ref(trait_segment={:?})", trait_segment); + let trait_ref = &ast_trait_ref.trait_ref; + let trait_def_id = self.trait_def_id(trait_ref); + + debug!("ast_path_to_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id); // The trait reference introduces a binding level here, so // we need to shift the `rscope`. It'd be nice if we could // do away with this rscope stuff and work this knowledge @@ -525,23 +507,23 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let (substs, assoc_bindings) = self.create_substs_for_ast_trait_ref(shifted_rscope, - span, + trait_ref.path.span, trait_def_id, self_ty, - trait_segment); + trait_ref.path.segments.last().unwrap()); let poly_trait_ref = ty::Binder(ty::TraitRef::new(trait_def_id, substs)); poly_projections.extend(assoc_bindings.iter().filter_map(|binding| { // specify type to assert that error was already reported in Err case: let predicate: Result<_, ErrorReported> = - self.ast_type_binding_to_poly_projection_predicate(path_id, + self.ast_type_binding_to_poly_projection_predicate(trait_ref.ref_id, poly_trait_ref, binding); predicate.ok() // ok to ignore Err() because ErrorReported (see above) })); - debug!("ast_path_to_poly_trait_ref(trait_segment={:?}, projections={:?}) -> {:?}", - trait_segment, poly_projections, poly_trait_ref); + debug!("ast_path_to_poly_trait_ref({:?}, projections={:?}) -> {:?}", + trait_ref, poly_projections, poly_trait_ref); poly_trait_ref } @@ -754,32 +736,29 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { ty::ExistentialTraitRef::erase_self_ty(self.tcx(), trait_ref) } - fn trait_path_to_object_type(&self, - rscope: &RegionScope, - path_span: Span, - trait_def_id: DefId, - trait_path_ref_id: ast::NodeId, - trait_segment: &hir::PathSegment, - span: Span, - partitioned_bounds: PartitionedBounds) - -> Ty<'tcx> { + fn conv_object_ty_poly_trait_ref(&self, + rscope: &RegionScope, + span: Span, + trait_bounds: &[hir::PolyTraitRef], + lifetime: &hir::Lifetime) + -> Ty<'tcx> + { let tcx = self.tcx(); + if trait_bounds.is_empty() { + span_err!(tcx.sess, span, E0224, + "at least one non-builtin trait is required for an object type"); + return tcx.types.err; + } + let mut projection_bounds = vec![]; let dummy_self = tcx.mk_ty(TRAIT_OBJECT_DUMMY_SELF); - let principal = self.ast_path_to_poly_trait_ref(rscope, - path_span, - trait_def_id, + let principal = self.instantiate_poly_trait_ref(rscope, + &trait_bounds[0], dummy_self, - trait_path_ref_id, - trait_segment, &mut projection_bounds); - let PartitionedBounds { trait_bounds, - region_bounds } = - partitioned_bounds; - - let (auto_traits, trait_bounds) = split_auto_traits(tcx, trait_bounds); + let (auto_traits, trait_bounds) = split_auto_traits(tcx, &trait_bounds[1..]); if !trait_bounds.is_empty() { let b = &trait_bounds[0]; @@ -854,13 +833,12 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { v.sort_by(|a, b| a.cmp(tcx, b)); let existential_predicates = ty::Binder(tcx.mk_existential_predicates(v.into_iter())); - let region_bound = self.compute_object_lifetime_bound(span, - ®ion_bounds, - existential_predicates); - let region_bound = match region_bound { - Some(r) => r, - None => { + // Explicitly specified region bound. Use that. + let region_bound = if !lifetime.is_elided() { + self.ast_region_to_region(lifetime, None) + } else { + self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| { tcx.mk_region(match rscope.object_lifetime_default(span) { Some(r) => r, None => { @@ -870,7 +848,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { ty::ReStatic } }) - } + }) }; debug!("region_bound: {:?}", region_bound); @@ -1330,8 +1308,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } tcx.mk_fn_ptr(bare_fn_ty) } - hir::TyTraitObject(ref bounds) => { - self.conv_object_ty_poly_trait_ref(rscope, ast_ty.span, bounds) + hir::TyTraitObject(ref bounds, ref lifetime) => { + self.conv_object_ty_poly_trait_ref(rscope, ast_ty.span, bounds, lifetime) } hir::TyImplTrait(ref bounds) => { use collect::{compute_bounds, SizedByDefault}; @@ -1537,33 +1515,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } } - fn conv_object_ty_poly_trait_ref(&self, - rscope: &RegionScope, - span: Span, - ast_bounds: &[hir::TyParamBound]) - -> Ty<'tcx> - { - let mut partitioned_bounds = partition_bounds(ast_bounds); - - let trait_bound = if !partitioned_bounds.trait_bounds.is_empty() { - partitioned_bounds.trait_bounds.remove(0) - } else { - span_err!(self.tcx().sess, span, E0224, - "at least one non-builtin trait is required for an object type"); - return self.tcx().types.err; - }; - - let trait_ref = &trait_bound.trait_ref; - let trait_def_id = self.trait_def_id(trait_ref); - self.trait_path_to_object_type(rscope, - trait_ref.path.span, - trait_def_id, - trait_ref.ref_id, - trait_ref.path.segments.last().unwrap(), - span, - partitioned_bounds) - } - /// Given the bounds on an object, determines what single region bound (if any) we can /// use to summarize this type. The basic idea is that we will use the bound the user /// provided, if they provided one, and otherwise search the supertypes of trait bounds @@ -1571,27 +1522,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { /// we return `None`. fn compute_object_lifetime_bound(&self, span: Span, - explicit_region_bounds: &[&hir::Lifetime], existential_predicates: ty::Binder<&'tcx ty::Slice>>) -> Option<&'tcx ty::Region> // if None, use the default { let tcx = self.tcx(); - debug!("compute_opt_region_bound(explicit_region_bounds={:?}, \ - existential_predicates={:?})", - explicit_region_bounds, + debug!("compute_opt_region_bound(existential_predicates={:?})", existential_predicates); - if explicit_region_bounds.len() > 1 { - span_err!(tcx.sess, explicit_region_bounds[1].span, E0226, - "only a single explicit lifetime bound is permitted"); - } - - if let Some(&r) = explicit_region_bounds.get(0) { - // Explicitly specified region bound. Use that. - return Some(self.ast_region_to_region(r, None)); - } - if let Some(principal) = existential_predicates.principal() { if let Err(ErrorReported) = self.ensure_super_predicates(span, principal.def_id()) { return Some(tcx.mk_region(ty::ReStatic)); @@ -1627,18 +1565,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } } -pub struct PartitionedBounds<'a> { - pub trait_bounds: Vec<&'a hir::PolyTraitRef>, - pub region_bounds: Vec<&'a hir::Lifetime>, -} - /// Divides a list of general trait bounds into two groups: builtin bounds (Sync/Send) and the /// remaining general trait bounds. fn split_auto_traits<'a, 'b, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - trait_bounds: Vec<&'b hir::PolyTraitRef>) + trait_bounds: &'b [hir::PolyTraitRef]) -> (Vec, Vec<&'b hir::PolyTraitRef>) { - let (auto_traits, trait_bounds): (Vec<_>, _) = trait_bounds.into_iter().partition(|bound| { + let (auto_traits, trait_bounds): (Vec<_>, _) = trait_bounds.iter().partition(|bound| { match bound.trait_ref.path.def { Def::Trait(trait_did) => { // Checks whether `trait_did` refers to one of the builtin @@ -1675,30 +1608,6 @@ fn split_auto_traits<'a, 'b, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, (auto_traits, trait_bounds) } -/// Divides a list of bounds from the AST into two groups: general trait bounds and region bounds -pub fn partition_bounds<'a, 'b, 'gcx, 'tcx>(ast_bounds: &'b [hir::TyParamBound]) - -> PartitionedBounds<'b> -{ - let mut region_bounds = Vec::new(); - let mut trait_bounds = Vec::new(); - for ast_bound in ast_bounds { - match *ast_bound { - hir::TraitTyParamBound(ref b, hir::TraitBoundModifier::None) => { - trait_bounds.push(b); - } - hir::TraitTyParamBound(_, hir::TraitBoundModifier::Maybe) => {} - hir::RegionTyParamBound(ref l) => { - region_bounds.push(l); - } - } - } - - PartitionedBounds { - trait_bounds: trait_bounds, - region_bounds: region_bounds, - } -} - fn check_type_argument_count(tcx: TyCtxt, span: Span, supplied: usize, ty_param_defs: &[ty::TypeParameterDef]) { let accepted = ty_param_defs.len(); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 1bfa4fc7b682c..90e2f821b10b5 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -57,7 +57,7 @@ There are some shortcomings in this design: */ -use astconv::{AstConv, Bounds, PartitionedBounds, partition_bounds}; +use astconv::{AstConv, Bounds}; use lint; use constrained_type_params as ctp; use middle::lang_items::SizedTraitLangItem; @@ -1961,10 +1961,19 @@ pub fn compute_bounds<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, span: Span) -> Bounds<'tcx> { - let PartitionedBounds { - trait_bounds, - region_bounds - } = partition_bounds(&ast_bounds); + let mut region_bounds = vec![]; + let mut trait_bounds = vec![]; + for ast_bound in ast_bounds { + match *ast_bound { + hir::TraitTyParamBound(ref b, hir::TraitBoundModifier::None) => { + trait_bounds.push(b); + } + hir::TraitTyParamBound(_, hir::TraitBoundModifier::Maybe) => {} + hir::RegionTyParamBound(ref l) => { + region_bounds.push(l); + } + } + } let mut projection_bounds = vec![]; diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 7f8c508bf2224..c41d40b41e42a 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4096,7 +4096,6 @@ register_diagnostics! { // E0222, // Error code E0045 (variadic function must have C calling // convention) duplicate E0224, // at least one non-builtin train is required for an object type - E0226, // only a single explicit lifetime bound is permitted E0227, // ambiguous lifetime bound, explicit lifetime bound required E0228, // explicit lifetime bound required E0231, // only named substitution parameters are allowed diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 4182dca715311..f1ced233dde7b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1776,20 +1776,20 @@ impl Clean for hir::Ty { trait_: box resolve_type(cx, trait_path.clean(cx), self.id) } } - TyTraitObject(ref bounds) => { - let lhs_ty = bounds[0].clean(cx); - match lhs_ty { - TraitBound(poly_trait, ..) => { - match poly_trait.trait_ { - ResolvedPath { path, typarams: None, did, is_generic } => { - ResolvedPath { - path: path, - typarams: Some(bounds[1..].clean(cx)), - did: did, - is_generic: is_generic, - } - } - _ => Infer // shouldn't happen + TyTraitObject(ref bounds, ref lifetime) => { + match bounds[0].clean(cx).trait_ { + ResolvedPath { path, typarams: None, did, is_generic } => { + let mut bounds: Vec<_> = bounds[1..].iter().map(|bound| { + TraitBound(bound.clean(cx), hir::TraitBoundModifier::None) + }).collect(); + if !lifetime.is_elided() { + bounds.push(RegionBound(lifetime.clean(cx))); + } + ResolvedPath { + path: path, + typarams: Some(bounds), + did: did, + is_generic: is_generic, } } _ => Infer // shouldn't happen diff --git a/src/test/compile-fail/region-bounds-on-objects-and-type-parameters.rs b/src/test/compile-fail/region-bounds-on-objects-and-type-parameters.rs index b8cbbdbe9ec3e..503b577b1f1b4 100644 --- a/src/test/compile-fail/region-bounds-on-objects-and-type-parameters.rs +++ b/src/test/compile-fail/region-bounds-on-objects-and-type-parameters.rs @@ -18,7 +18,7 @@ trait SomeTrait { } // Bounds on object types: -struct Foo<'a,'b,'c> { +struct Foo<'a,'b,'c> { //~ ERROR parameter `'b` is never used // All of these are ok, because we can derive exactly one bound: a: Box, b: Box>, @@ -28,7 +28,9 @@ struct Foo<'a,'b,'c> { f: Box, // OK, defaults to 'static due to RFC 599. g: Box, - z: Box+'b+'c>, //~ ERROR only a single explicit lifetime bound is permitted + z: Box+'b+'c>, + //~^ ERROR only a single explicit lifetime bound is permitted + //~| ERROR lifetime bound not satisfied } fn test<