use rustc_data_structures::fx::FxHashMap; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_lint_defs::Applicability; use rustc_middle::ty::{self as ty, Ty, TypeVisitableExt}; use rustc_span::symbol::Ident; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits; use smallvec::SmallVec; use crate::astconv::{ AstConv, ConvertedBinding, ConvertedBindingKind, OnlySelfBounds, PredicateFilter, }; use crate::bounds::Bounds; use crate::errors; impl<'tcx> dyn AstConv<'tcx> + '_ { /// Sets `implicitly_sized` to true on `Bounds` if necessary pub(crate) fn add_implicitly_sized( &self, bounds: &mut Bounds<'tcx>, self_ty: Ty<'tcx>, ast_bounds: &'tcx [hir::GenericBound<'tcx>], self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, span: Span, ) { let tcx = self.tcx(); // Try to find an unbound in bounds. let mut unbounds: SmallVec<[_; 1]> = SmallVec::new(); let mut search_bounds = |ast_bounds: &'tcx [hir::GenericBound<'tcx>]| { for ab in ast_bounds { if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab { unbounds.push(ptr) } } }; search_bounds(ast_bounds); if let Some((self_ty, where_clause)) = self_ty_where_predicates { for clause in where_clause { if let hir::WherePredicate::BoundPredicate(pred) = clause { if pred.is_param_bound(self_ty.to_def_id()) { search_bounds(pred.bounds); } } } } if unbounds.len() > 1 { tcx.sess.emit_err(errors::MultipleRelaxedDefaultBounds { spans: unbounds.iter().map(|ptr| ptr.span).collect(), }); } let sized_def_id = tcx.lang_items().sized_trait(); let mut seen_sized_unbound = false; for unbound in unbounds { if let Some(sized_def_id) = sized_def_id { if unbound.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) { seen_sized_unbound = true; continue; } } // There was a `?Trait` bound, but it was not `?Sized`; warn. tcx.sess.span_warn( unbound.span, "relaxing a default bound only does something for `?Sized`; \ all other traits are not bound by default", ); } // If the above loop finished there was no `?Sized` bound; add implicitly sized if `Sized` is available. if sized_def_id.is_none() { // No lang item for `Sized`, so we can't add it as a bound. return; } if seen_sized_unbound { // There was in fact a `?Sized` bound, return without doing anything } else { // There was no `?Sized` bound; add implicitly sized if `Sized` is available. bounds.push_sized(tcx, self_ty, span); } } /// This helper takes a *converted* parameter type (`param_ty`) /// and an *unconverted* list of bounds: /// /// ```text /// fn foo /// ^ ^^^^^ `ast_bounds` parameter, in HIR form /// | /// `param_ty`, in ty form /// ``` /// /// It adds these `ast_bounds` into the `bounds` structure. /// /// **A note on binders:** there is an implied binder around /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref` /// for more details. #[instrument(level = "debug", skip(self, ast_bounds, bounds))] pub(crate) fn add_bounds<'hir, I: Iterator>>( &self, param_ty: Ty<'tcx>, ast_bounds: I, bounds: &mut Bounds<'tcx>, bound_vars: &'tcx ty::List, only_self_bounds: OnlySelfBounds, ) { for ast_bound in ast_bounds { match ast_bound { hir::GenericBound::Trait(poly_trait_ref, modifier) => { let (constness, polarity) = match modifier { hir::TraitBoundModifier::MaybeConst => { (ty::BoundConstness::ConstIfConst, ty::ImplPolarity::Positive) } hir::TraitBoundModifier::None => { (ty::BoundConstness::NotConst, ty::ImplPolarity::Positive) } hir::TraitBoundModifier::Negative => { (ty::BoundConstness::NotConst, ty::ImplPolarity::Negative) } hir::TraitBoundModifier::Maybe => continue, }; let _ = self.instantiate_poly_trait_ref( &poly_trait_ref.trait_ref, poly_trait_ref.span, constness, polarity, param_ty, bounds, false, only_self_bounds, ); } &hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => { self.instantiate_lang_item_trait_ref( lang_item, span, hir_id, args, param_ty, bounds, only_self_bounds, ); } hir::GenericBound::Outlives(lifetime) => { let region = self.ast_region_to_region(lifetime, None); bounds.push_region_bound( self.tcx(), ty::Binder::bind_with_vars( ty::OutlivesPredicate(param_ty, region), bound_vars, ), lifetime.ident.span, ); } } } } /// Translates a list of bounds from the HIR into the `Bounds` data structure. /// The self-type for the bounds is given by `param_ty`. /// /// Example: /// /// ```ignore (illustrative) /// fn foo() { } /// // ^ ^^^^^^^^^ ast_bounds /// // param_ty /// ``` /// /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the /// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`. /// /// `span` should be the declaration size of the parameter. pub(crate) fn compute_bounds( &self, param_ty: Ty<'tcx>, ast_bounds: &[hir::GenericBound<'_>], filter: PredicateFilter, ) -> Bounds<'tcx> { let mut bounds = Bounds::default(); let only_self_bounds = match filter { PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { OnlySelfBounds(false) } PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => OnlySelfBounds(true), }; self.add_bounds( param_ty, ast_bounds.iter().filter(|bound| { match filter { PredicateFilter::All | PredicateFilter::SelfOnly | PredicateFilter::SelfAndAssociatedTypeBounds => true, PredicateFilter::SelfThatDefines(assoc_name) => { if let Some(trait_ref) = bound.trait_ref() && let Some(trait_did) = trait_ref.trait_def_id() && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name) { true } else { false } } } }), &mut bounds, ty::List::empty(), only_self_bounds, ); debug!(?bounds); bounds } /// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates /// onto `bounds`. /// /// **A note on binders:** given something like `T: for<'a> Iterator`, the /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside* /// the binder (e.g., `&'a u32`) and hence may reference bound regions. #[instrument(level = "debug", skip(self, bounds, speculative, dup_bindings, path_span))] pub(super) fn add_predicates_for_ast_type_binding( &self, hir_ref_id: hir::HirId, trait_ref: ty::PolyTraitRef<'tcx>, binding: &ConvertedBinding<'_, 'tcx>, bounds: &mut Bounds<'tcx>, speculative: bool, dup_bindings: &mut FxHashMap, path_span: Span, constness: ty::BoundConstness, only_self_bounds: OnlySelfBounds, polarity: ty::ImplPolarity, ) -> Result<(), ErrorGuaranteed> { // Given something like `U: SomeTrait`, we want to produce a // predicate like `::T = X`. This is somewhat // subtle in the event that `T` is defined in a supertrait of // `SomeTrait`, because in that case we need to upcast. // // That is, consider this case: // // ``` // trait SubTrait: SuperTrait { } // trait SuperTrait { type T; } // // ... B: SubTrait ... // ``` // // We want to produce `>::T == foo`. let tcx = self.tcx(); let return_type_notation = binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation; let candidate = if return_type_notation { if self.trait_defines_associated_item_named( trait_ref.def_id(), ty::AssocKind::Fn, binding.item_name, ) { trait_ref } else { self.one_bound_for_assoc_method( traits::supertraits(tcx, trait_ref), trait_ref.print_only_trait_path(), binding.item_name, path_span, )? } } else if self.trait_defines_associated_item_named( trait_ref.def_id(), ty::AssocKind::Type, binding.item_name, ) { // Simple case: X is defined in the current trait. trait_ref } else { // Otherwise, we have to walk through the supertraits to find // those that do. self.one_bound_for_assoc_type( || traits::supertraits(tcx, trait_ref), trait_ref.skip_binder().print_only_trait_name(), None, binding.item_name, path_span, match binding.kind { ConvertedBindingKind::Equality(term) => Some(term), _ => None, }, )? }; let (assoc_ident, def_scope) = tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id); // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead // of calling `filter_by_name_and_kind`. let find_item_of_kind = |kind| { tcx.associated_items(candidate.def_id()) .filter_by_name_unhygienic(assoc_ident.name) .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident) }; let assoc_item = if return_type_notation { find_item_of_kind(ty::AssocKind::Fn) } else { find_item_of_kind(ty::AssocKind::Type) .or_else(|| find_item_of_kind(ty::AssocKind::Const)) } .expect("missing associated type"); if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) { tcx.sess .struct_span_err( binding.span, format!("{} `{}` is private", assoc_item.kind, binding.item_name), ) .span_label(binding.span, format!("private {}", assoc_item.kind)) .emit(); } tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None); if !speculative { dup_bindings .entry(assoc_item.def_id) .and_modify(|prev_span| { tcx.sess.emit_err(errors::ValueOfAssociatedStructAlreadySpecified { span: binding.span, prev_span: *prev_span, item_name: binding.item_name, def_path: tcx.def_path_str(assoc_item.container_id(tcx)), }); }) .or_insert(binding.span); } let projection_ty = if return_type_notation { let mut emitted_bad_param_err = false; // If we have an method return type bound, then we need to substitute // the method's early bound params with suitable late-bound params. let mut num_bound_vars = candidate.bound_vars().len(); let args = candidate.skip_binder().args.extend_to(tcx, assoc_item.def_id, |param, _| { let subst = match param.kind { ty::GenericParamDefKind::Lifetime => ty::Region::new_late_bound( tcx, ty::INNERMOST, ty::BoundRegion { var: ty::BoundVar::from_usize(num_bound_vars), kind: ty::BoundRegionKind::BrNamed(param.def_id, param.name), }, ) .into(), ty::GenericParamDefKind::Type { .. } => { if !emitted_bad_param_err { tcx.sess.emit_err( crate::errors::ReturnTypeNotationIllegalParam::Type { span: path_span, param_span: tcx.def_span(param.def_id), }, ); emitted_bad_param_err = true; } Ty::new_bound( tcx, ty::INNERMOST, ty::BoundTy { var: ty::BoundVar::from_usize(num_bound_vars), kind: ty::BoundTyKind::Param(param.def_id, param.name), }, ) .into() } ty::GenericParamDefKind::Const { .. } => { if !emitted_bad_param_err { tcx.sess.emit_err( crate::errors::ReturnTypeNotationIllegalParam::Const { span: path_span, param_span: tcx.def_span(param.def_id), }, ); emitted_bad_param_err = true; } let ty = tcx .type_of(param.def_id) .no_bound_vars() .expect("ct params cannot have early bound vars"); ty::Const::new_bound( tcx, ty::INNERMOST, ty::BoundVar::from_usize(num_bound_vars), ty, ) .into() } }; num_bound_vars += 1; subst }); // Next, we need to check that the return-type notation is being used on // an RPITIT (return-position impl trait in trait) or AFIT (async fn in trait). let output = tcx.fn_sig(assoc_item.def_id).skip_binder().output(); let output = if let ty::Alias(ty::Projection, alias_ty) = *output.skip_binder().kind() && tcx.is_impl_trait_in_trait(alias_ty.def_id) { alias_ty } else { return Err(self.tcx().sess.emit_err( crate::errors::ReturnTypeNotationOnNonRpitit { span: binding.span, ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output), fn_span: tcx.hir().span_if_local(assoc_item.def_id), note: (), }, )); }; // Finally, move the fn return type's bound vars over to account for the early bound // params (and trait ref's late bound params). This logic is very similar to // `Predicate::subst_supertrait`, and it's no coincidence why. let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output); let subst_output = ty::EarlyBinder::bind(shifted_output).instantiate(tcx, args); let bound_vars = tcx.late_bound_vars(binding.hir_id); ty::Binder::bind_with_vars(subst_output, bound_vars) } else { // Append the generic arguments of the associated type to the `trait_ref`. candidate.map_bound(|trait_ref| { let ident = Ident::new(assoc_item.name, binding.item_name.span); let item_segment = hir::PathSegment { ident, hir_id: binding.hir_id, res: Res::Err, args: Some(binding.gen_args), infer_args: false, }; let args_trait_ref_and_assoc_item = self.create_args_for_associated_item( path_span, assoc_item.def_id, &item_segment, trait_ref.args, ); debug!(?args_trait_ref_and_assoc_item); ty::AliasTy::new(tcx, assoc_item.def_id, args_trait_ref_and_assoc_item) }) }; if !speculative { // Find any late-bound regions declared in `ty` that are not // declared in the trait-ref or assoc_item. These are not well-formed. // // Example: // // for<'a> ::Item = &'a str // <-- 'a is bad // for<'a> >::Output = &'a str // <-- 'a is ok if let ConvertedBindingKind::Equality(ty) = binding.kind { let late_bound_in_trait_ref = tcx.collect_constrained_late_bound_regions(&projection_ty); let late_bound_in_ty = tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty)); debug!(?late_bound_in_trait_ref); debug!(?late_bound_in_ty); // FIXME: point at the type params that don't have appropriate lifetimes: // struct S1 Fn(&i32, &i32) -> &'a i32>(F); // ---- ---- ^^^^^^^ self.validate_late_bound_regions( late_bound_in_trait_ref, late_bound_in_ty, |br_name| { struct_span_err!( tcx.sess, binding.span, E0582, "binding for associated type `{}` references {}, \ which does not appear in the trait input types", binding.item_name, br_name ) }, ); } } let assoc_item_def_id = projection_ty.skip_binder().def_id; let def_kind = tcx.def_kind(assoc_item_def_id); match binding.kind { ConvertedBindingKind::Equality(..) if return_type_notation => { return Err(self.tcx().sess.emit_err( crate::errors::ReturnTypeNotationEqualityBound { span: binding.span }, )); } ConvertedBindingKind::Equality(mut term) => { // "Desugar" a constraint like `T: Iterator` this to // the "projection predicate" for: // // `::Item = u32` match (def_kind, term.unpack()) { (DefKind::AssocTy, ty::TermKind::Ty(_)) | (DefKind::AssocConst, ty::TermKind::Const(_)) => (), (_, _) => { let got = if let Some(_) = term.ty() { "type" } else { "constant" }; let expected = tcx.def_descr(assoc_item_def_id); let mut err = tcx.sess.struct_span_err( binding.span, format!("expected {expected} bound, found {got}"), ); err.span_note( tcx.def_span(assoc_item_def_id), format!("{expected} defined here"), ); if let DefKind::AssocConst = def_kind && let Some(t) = term.ty() && (t.is_enum() || t.references_error()) && tcx.features().associated_const_equality { err.span_suggestion( binding.span, "if equating a const, try wrapping with braces", format!("{} = {{ const }}", binding.item_name), Applicability::HasPlaceholders, ); } let reported = err.emit(); term = match def_kind { DefKind::AssocTy => Ty::new_error(tcx, reported).into(), DefKind::AssocConst => ty::Const::new_error( tcx, reported, tcx.type_of(assoc_item_def_id) .instantiate(tcx, projection_ty.skip_binder().args), ) .into(), _ => unreachable!(), }; } } bounds.push_projection_bound( tcx, projection_ty .map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }), binding.span, ); } ConvertedBindingKind::Constraint(ast_bounds) => { match def_kind { DefKind::AssocTy => {} _ => { return Err(tcx.sess.emit_err(errors::AssocBoundOnConst { span: assoc_ident.span, descr: tcx.def_descr(assoc_item_def_id), })); } } // "Desugar" a constraint like `T: Iterator` to // // `::Item: Debug` // // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty` // parameter to have a skipped binder. // // NOTE: If `only_self_bounds` is true, do NOT expand this associated // type bound into a trait predicate, since we only want to add predicates // for the `Self` type. if !only_self_bounds.0 { let param_ty = Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder()); self.add_bounds( param_ty, ast_bounds.iter(), bounds, projection_ty.bound_vars(), only_self_bounds, ); } } } Ok(()) } }