diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index a792c439f5b8..d950746f6e6d 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -60,9 +60,12 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapError; pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::engine::{TraitEngine, TraitEngineExt}; pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs}; -pub use self::util::{supertraits, supertrait_def_ids, transitive_bounds, - Supertraits, SupertraitDefIds}; -pub use self::util::{expand_trait_refs, TraitRefExpander}; +pub use self::util::{ + supertraits, supertrait_def_ids, transitive_bounds, Supertraits, SupertraitDefIds, +}; +pub use self::util::{ + expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfoDignosticBuilder, +}; pub use self::chalk_fulfill::{ CanonicalGoal as ChalkCanonicalGoal, diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs index 4cb3f551123d..d765827e3d56 100644 --- a/src/librustc/traits/util.rs +++ b/src/librustc/traits/util.rs @@ -1,3 +1,5 @@ +use errors::DiagnosticBuilder; +use smallvec::SmallVec; use syntax_pos::Span; use crate::hir; @@ -43,7 +45,7 @@ fn anonymize_predicate<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, } } -struct PredicateSet<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +struct PredicateSet<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { tcx: TyCtxt<'a, 'gcx, 'tcx>, set: FxHashSet>, } @@ -53,6 +55,10 @@ impl<'a, 'gcx, 'tcx> PredicateSet<'a, 'gcx, 'tcx> { PredicateSet { tcx: tcx, set: Default::default() } } + fn contains(&mut self, pred: &ty::Predicate<'tcx>) -> bool { + self.set.contains(&anonymize_predicate(self.tcx, pred)) + } + fn insert(&mut self, pred: &ty::Predicate<'tcx>) -> bool { // We have to be careful here because we want // @@ -66,6 +72,18 @@ impl<'a, 'gcx, 'tcx> PredicateSet<'a, 'gcx, 'tcx> { // regions before we throw things into the underlying set. self.set.insert(anonymize_predicate(self.tcx, pred)) } + + fn remove(&mut self, pred: &ty::Predicate<'tcx>) -> bool { + self.set.remove(&anonymize_predicate(self.tcx, pred)) + } +} + +impl<'a, 'gcx, 'tcx, T: AsRef>> Extend for PredicateSet<'a, 'gcx, 'tcx> { + fn extend>(&mut self, iter: I) { + for pred in iter.into_iter() { + self.insert(pred.as_ref()); + } + } } /////////////////////////////////////////////////////////////////////////// @@ -230,10 +248,16 @@ impl<'cx, 'gcx, 'tcx> Iterator for Elaborator<'cx, 'gcx, 'tcx> { } fn next(&mut self) -> Option> { - self.stack.pop().map(|item| { - self.push(&item); - item - }) + // Extract next item from top-most stack frame, if any. + let next_predicate = match self.stack.pop() { + Some(predicate) => predicate, + None => { + // No more stack frames. Done. + return None; + } + }; + self.push(&next_predicate); + return Some(next_predicate); } } @@ -256,96 +280,140 @@ pub fn transitive_bounds<'cx, 'gcx, 'tcx>(tcx: TyCtxt<'cx, 'gcx, 'tcx>, } /////////////////////////////////////////////////////////////////////////// -// `TraitRefExpander` iterator +// `TraitAliasExpander` iterator /////////////////////////////////////////////////////////////////////////// -/// "Trait reference expansion" is the process of expanding a sequence of trait +/// "Trait alias expansion" is the process of expanding a sequence of trait /// references into another sequence by transitively following all trait /// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias /// `trait Foo = Bar + Sync;`, and another trait alias /// `trait Bar = Read + Write`, then the bounds would expand to /// `Read + Write + Sync + Send`. -pub struct TraitRefExpander<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - stack: Vec>, +pub struct TraitAliasExpander<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { + stack: Vec>, + /// The set of predicates visited from the root directly to the current point in the + /// expansion tree. visited: PredicateSet<'a, 'gcx, 'tcx>, } #[derive(Debug, Clone)] -pub struct TraitRefExpansionInfo<'tcx> { - pub top_level_trait_ref: ty::PolyTraitRef<'tcx>, - pub top_level_span: Span, - pub trait_ref: ty::PolyTraitRef<'tcx>, - pub span: Span, +pub struct TraitAliasExpansionInfo<'tcx> { + pub items: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>, } -pub fn expand_trait_refs<'cx, 'gcx, 'tcx>( +impl<'tcx> TraitAliasExpansionInfo<'tcx> { + fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> TraitAliasExpansionInfo<'tcx> { + TraitAliasExpansionInfo { + items: smallvec![(trait_ref, span)] + } + } + + fn push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> TraitAliasExpansionInfo<'tcx> { + let mut items = self.items.clone(); + items.push((trait_ref, span)); + + TraitAliasExpansionInfo { + items + } + } + + pub fn trait_ref(&self) -> &ty::PolyTraitRef<'tcx> { + &self.top().0 + } + + pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { + self.items.last().unwrap() + } + + pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { + self.items.first().unwrap() + } +} + +pub trait TraitAliasExpansionInfoDignosticBuilder { + fn label_with_exp_info<'tcx>(&mut self, + info: &TraitAliasExpansionInfo<'tcx>, + top_label: &str + ) -> &mut Self; +} + +impl<'a> TraitAliasExpansionInfoDignosticBuilder for DiagnosticBuilder<'a> { + fn label_with_exp_info<'tcx>(&mut self, + info: &TraitAliasExpansionInfo<'tcx>, + top_label: &str + ) -> &mut Self { + self.span_label(info.top().1, top_label); + if info.items.len() > 1 { + for (_, sp) in info.items[1..(info.items.len() - 1)].iter().rev() { + self.span_label(*sp, "referenced here"); + } + } + self + } +} + +pub fn expand_trait_aliases<'cx, 'gcx, 'tcx>( tcx: TyCtxt<'cx, 'gcx, 'tcx>, trait_refs: impl IntoIterator, Span)> -) -> TraitRefExpander<'cx, 'gcx, 'tcx> { - let mut visited = PredicateSet::new(tcx); - let mut items: Vec<_> = - trait_refs - .into_iter() - .map(|(trait_ref, span)| TraitRefExpansionInfo { - top_level_trait_ref: trait_ref.clone(), - top_level_span: span, - trait_ref, - span, - }) - .collect(); - items.retain(|item| visited.insert(&item.trait_ref.to_predicate())); - TraitRefExpander { stack: items, visited } +) -> TraitAliasExpander<'cx, 'gcx, 'tcx> { + let items: Vec<_> = trait_refs + .into_iter() + .map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)) + .collect(); + TraitAliasExpander { stack: items, visited: PredicateSet::new(tcx) } } -impl<'cx, 'gcx, 'tcx> TraitRefExpander<'cx, 'gcx, 'tcx> { - /// If `item` refers to a trait alias, adds the components of the trait alias to the stack, - /// and returns `false`. - /// If `item` refers to an ordinary trait, simply returns `true`. - fn push(&mut self, item: &TraitRefExpansionInfo<'tcx>) -> bool { +impl<'cx, 'gcx, 'tcx> TraitAliasExpander<'cx, 'gcx, 'tcx> { + /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item` + /// to the definition and pushes the resulting expansion onto `self.stack`, and returns `false`. + /// Otherwise, immediately returns `true` if `item` is a regular trait and `false` if it is a + /// trait alias. + /// The return value indicates whether `item` should not be yielded to the user. + fn push(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool { let tcx = self.visited.tcx; + let trait_ref = item.trait_ref(); + let pred = trait_ref.to_predicate(); - if !tcx.is_trait_alias(item.trait_ref.def_id()) { - return true; + debug!("expand_trait_aliases: trait_ref={:?}", trait_ref); + + self.visited.remove(&pred); + + let is_alias = tcx.is_trait_alias(trait_ref.def_id()); + if !is_alias || self.visited.contains(&pred) { + return !is_alias; } - // Get components of the trait alias. - let predicates = tcx.super_predicates_of(item.trait_ref.def_id()); + // Get components of trait alias. + let predicates = tcx.super_predicates_of(trait_ref.def_id()); - let mut items: Vec<_> = predicates.predicates + let items: Vec<_> = predicates.predicates .iter() .rev() .filter_map(|(pred, span)| { - pred.subst_supertrait(tcx, &item.trait_ref) + pred.subst_supertrait(tcx, &trait_ref) .to_opt_poly_trait_ref() - .map(|trait_ref| - TraitRefExpansionInfo { - trait_ref, - span: *span, - ..*item - } - ) + .map(|trait_ref| item.push(trait_ref, *span)) }) .collect(); - - debug!("trait_ref_expander: trait_ref={:?} items={:?}", - item.trait_ref, items); - - // Only keep those items that we haven't already seen. - items.retain(|i| self.visited.insert(&i.trait_ref.to_predicate())); + debug!("expand_trait_aliases: items={:?}", items); self.stack.extend(items); + + // Record predicate into set of already-visited. + self.visited.insert(&pred); + false } } -impl<'cx, 'gcx, 'tcx> Iterator for TraitRefExpander<'cx, 'gcx, 'tcx> { - type Item = TraitRefExpansionInfo<'tcx>; +impl<'cx, 'gcx, 'tcx> Iterator for TraitAliasExpander<'cx, 'gcx, 'tcx> { + type Item = TraitAliasExpansionInfo<'tcx>; fn size_hint(&self) -> (usize, Option) { (self.stack.len(), None) } - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option> { while let Some(item) = self.stack.pop() { if self.push(&item) { return Some(item); @@ -386,8 +454,8 @@ impl<'cx, 'gcx, 'tcx> Iterator for SupertraitDefIds<'cx, 'gcx, 'tcx> { self.stack.extend( predicates.predicates .iter() - .filter_map(|(p, _)| p.to_opt_poly_trait_ref()) - .map(|t| t.def_id()) + .filter_map(|(pred, _)| pred.to_opt_poly_trait_ref()) + .map(|trait_ref| trait_ref.def_id()) .filter(|&super_def_id| visited.insert(super_def_id))); Some(def_id) } @@ -413,17 +481,12 @@ impl<'tcx, I: Iterator>> Iterator for FilterToTraits< type Item = ty::PolyTraitRef<'tcx>; fn next(&mut self) -> Option> { - loop { - match self.base_iterator.next() { - None => { - return None; - } - Some(ty::Predicate::Trait(data)) => { - return Some(data.to_poly_trait_ref()); - } - Some(_) => {} + while let Some(pred) = self.base_iterator.next() { + if let ty::Predicate::Trait(data) = pred { + return Some(data.to_poly_trait_ref()); } } + None } fn size_hint(&self) -> (usize, Option) { diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 2bea1db841ae..d70c81854955 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -504,7 +504,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> { any_lifetime_bounds = true; } } - self.no_questions_in_bounds(bounds, "trait object types", false); } TyKind::ImplTrait(_, ref bounds) => { if self.is_impl_trait_banned { diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index e1dc0b10c9f9..428fa66101ac 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -11,7 +11,7 @@ use crate::lint; use crate::middle::resolve_lifetime as rl; use crate::namespace::Namespace; use rustc::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; -use rustc::traits; +use rustc::traits::{self, TraitAliasExpansionInfoDignosticBuilder}; use rustc::ty::{self, DefIdTree, Ty, TyCtxt, ToPredicate, TypeFoldable}; use rustc::ty::{GenericParamDef, GenericParamDefKind}; use rustc::ty::subst::{Kind, Subst, InternalSubsts, SubstsRef}; @@ -965,30 +965,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o { ty::ExistentialTraitRef::erase_self_ty(self.tcx(), trait_ref) } - fn expand_trait_refs(&self, - trait_refs: impl IntoIterator, Span)> - ) -> Vec { - let tcx = self.tcx(); - - // Expand trait aliases recursively and check that only one regular (non-auto) trait - // is used. - let expanded_traits = traits::expand_trait_refs(tcx, trait_refs); - let (auto_traits, regular_traits): (Vec<_>, Vec<_>) = - expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref.def_id())); - if regular_traits.len() > 1 { - let extra_trait = ®ular_traits[1]; - let mut err = struct_span_err!(tcx.sess, extra_trait.top_level_span, E0225, - "only auto traits can be used as additional traits in a trait object"); - err.span_label(extra_trait.span, "non-auto additional trait"); - if extra_trait.span != extra_trait.top_level_span { - err.span_label(extra_trait.top_level_span, "expanded from this trait alias"); - } - err.emit(); - } - - auto_traits.into_iter().map(|i| i.trait_ref.def_id()).collect() - } - fn conv_object_ty_poly_trait_ref(&self, span: Span, trait_bounds: &[hir::PolyTraitRef], @@ -997,52 +973,69 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o { { let tcx = self.tcx(); - if trait_bounds.is_empty() { + let mut projection_bounds = Vec::new(); + let mut potential_assoc_types = Vec::new(); + let dummy_self = self.tcx().types.trait_object_dummy_self; + let bound_trait_refs: Vec<_> = trait_bounds + .iter() + .rev() + .map(|trait_bound| { + let (trait_ref, cur_potential_assoc_types) = self.instantiate_poly_trait_ref( + trait_bound, + dummy_self, + &mut projection_bounds + ); + potential_assoc_types.extend(cur_potential_assoc_types.into_iter().flatten()); + (trait_ref, trait_bound.span) + }).collect(); + + // Expand trait aliases recursively and check that only one regular (non-auto) trait + // is used and no 'maybe' bounds are used. + let expanded_traits = traits::expand_trait_aliases(tcx, bound_trait_refs.clone()); + let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = + expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); + if regular_traits.len() > 1 { + let extra_trait = ®ular_traits[1]; + let mut err = struct_span_err!(tcx.sess, extra_trait.bottom().1, E0225, + "only auto traits can be used as additional traits in a trait object"); + err.label_with_exp_info(extra_trait, "additional non-auto trait"); + err.span_label(regular_traits[0].top().1, "first non-auto trait"); + err.emit(); + } + + if regular_traits.is_empty() && auto_traits.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::new(); - let dummy_self = self.tcx().types.trait_object_dummy_self; - let (principal, potential_assoc_types) = self.instantiate_poly_trait_ref( - &trait_bounds[0], - dummy_self, - &mut projection_bounds, - ); - debug!("principal: {:?}", principal); - - let mut bound_trait_refs = Vec::with_capacity(trait_bounds.len()); - for trait_bound in trait_bounds[1..].iter().rev() { - // Sanity check for non-principal trait bounds. - let (tr, _) = self.instantiate_poly_trait_ref( - trait_bound, - dummy_self, - &mut Vec::new() - ); - bound_trait_refs.push((tr, trait_bound.span)); - } - bound_trait_refs.push((principal, trait_bounds[0].span)); - - let mut auto_traits = self.expand_trait_refs(bound_trait_refs); - // Check that there are no gross object safety violations; // most importantly, that the supertraits don't contain `Self`, // to avoid ICEs. - let object_safety_violations = - tcx.global_tcx().astconv_object_safety_violations(principal.def_id()); - if !object_safety_violations.is_empty() { - tcx.report_object_safety_error(span, principal.def_id(), object_safety_violations) - .map(|mut err| err.emit()); - return tcx.types.err; + for item in ®ular_traits { + let object_safety_violations = + tcx.global_tcx().astconv_object_safety_violations(item.trait_ref().def_id()); + if !object_safety_violations.is_empty() { + tcx.report_object_safety_error( + span, + item.trait_ref().def_id(), + object_safety_violations + ) + .map(|mut err| err.emit()); + return tcx.types.err; + } } // Use a `BTreeSet` to keep output in a more consistent order. let mut associated_types = BTreeSet::default(); - for tr in traits::elaborate_trait_ref(tcx, principal) { - debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", tr); - match tr { + let regular_traits_refs = bound_trait_refs + .into_iter() + .filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())) + .map(|(trait_ref, _)| trait_ref); + for trait_ref in traits::elaborate_trait_refs(tcx, regular_traits_refs) { + debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", trait_ref); + match trait_ref { ty::Predicate::Trait(pred) => { associated_types .extend(tcx.associated_items(pred.def_id()) @@ -1102,20 +1095,18 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o { if associated_types.len() == 1 { "" } else { "s" }, names, ); - let mut suggest = false; - let mut potential_assoc_types_spans = vec![]; - if let Some(potential_assoc_types) = potential_assoc_types { + let (suggest, potential_assoc_types_spans) = if potential_assoc_types.len() == associated_types.len() { - // Only suggest when the amount of missing associated types is equals to the + // Only suggest when the amount of missing associated types equals the number of // extra type arguments present, as that gives us a relatively high confidence // that the user forgot to give the associtated type's name. The canonical // example would be trying to use `Iterator` instead of - // `Iterator`. - suggest = true; - potential_assoc_types_spans = potential_assoc_types; - } - } - let mut suggestions = vec![]; + // `Iterator`. + (true, potential_assoc_types) + } else { + (false, Vec::new()) + }; + let mut suggestions = Vec::new(); for (i, item_def_id) in associated_types.iter().enumerate() { let assoc_item = tcx.associated_item(*item_def_id); err.span_label( @@ -1151,9 +1142,16 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o { err.emit(); } + // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as + // `dyn Trait + Send`. + auto_traits.sort_by_key(|i| i.trait_ref().def_id()); + auto_traits.dedup_by_key(|i| i.trait_ref().def_id()); + debug!("regular_traits: {:?}", regular_traits); + debug!("auto_traits: {:?}", auto_traits); + // Erase the `dummy_self` (`trait_object_dummy_self`) used above. - let existential_principal = principal.map_bound(|trait_ref| { - self.trait_ref_to_existential(trait_ref) + let existential_trait_refs = regular_traits.iter().map(|i| { + i.trait_ref().map_bound(|trait_ref| self.trait_ref_to_existential(trait_ref)) }); let existential_projections = projection_bounds.iter().map(|(bound, _)| { bound.map_bound(|b| { @@ -1166,21 +1164,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o { }) }); - // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as - // `dyn Trait + Send`. - auto_traits.sort(); - auto_traits.dedup(); - debug!("auto_traits: {:?}", auto_traits); - // Calling `skip_binder` is okay because the predicates are re-bound. - let principal = if tcx.trait_is_auto(existential_principal.def_id()) { - ty::ExistentialPredicate::AutoTrait(existential_principal.def_id()) - } else { - ty::ExistentialPredicate::Trait(*existential_principal.skip_binder()) - }; + let regular_trait_predicates = existential_trait_refs.map( + |trait_ref| ty::ExistentialPredicate::Trait(*trait_ref.skip_binder())); + let auto_trait_predicates = auto_traits.into_iter().map( + |trait_ref| ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id())); let mut v = - iter::once(principal) - .chain(auto_traits.into_iter().map(ty::ExistentialPredicate::AutoTrait)) + regular_trait_predicates + .chain(auto_trait_predicates) .chain(existential_projections .map(|x| ty::ExistentialPredicate::Projection(*x.skip_binder()))) .collect::>();