diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index e8cf25b190621..7658e7ad35f3a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -371,10 +371,9 @@ fn clean_where_predicate<'tcx>( bounds: wrp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }, - hir::WherePredicateKind::EqPredicate(wrp) => WherePredicate::EqPredicate { - lhs: clean_ty(wrp.lhs_ty, cx), - rhs: clean_ty(wrp.rhs_ty, cx).into(), - }, + // We should never actually reach this case because these predicates should've already been + // rejected in an earlier compiler pass. This feature isn't fully implemented (#20041). + hir::WherePredicateKind::EqPredicate(_) => bug!("EqPredicate"), }) } @@ -470,49 +469,37 @@ fn clean_projection_predicate<'tcx>( cx: &mut DocContext<'tcx>, ) -> WherePredicate { WherePredicate::EqPredicate { - lhs: clean_projection( - pred.map_bound(|p| { - // FIXME: This needs to be made resilient for `AliasTerm`s that - // are associated consts. - p.projection_term.expect_ty(cx.tcx) - }), - cx, - None, - ), + lhs: clean_projection(pred.map_bound(|p| p.projection_term), cx, None), rhs: clean_middle_term(pred.map_bound(|p| p.term), cx), } } fn clean_projection<'tcx>( - ty: ty::Binder<'tcx, ty::AliasTy<'tcx>>, + proj: ty::Binder<'tcx, ty::AliasTerm<'tcx>>, cx: &mut DocContext<'tcx>, - def_id: Option, -) -> Type { - if cx.tcx.is_impl_trait_in_trait(ty.skip_binder().def_id) { - return clean_middle_opaque_bounds(cx, ty.skip_binder().def_id, ty.skip_binder().args); - } - + parent_def_id: Option, +) -> QPathData { let trait_ = clean_trait_ref_with_constraints( cx, - ty.map_bound(|ty| ty.trait_ref(cx.tcx)), + proj.map_bound(|proj| proj.trait_ref(cx.tcx)), ThinVec::new(), ); - let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None, None); - let self_def_id = if let Some(def_id) = def_id { - cx.tcx.opt_parent(def_id).or(Some(def_id)) - } else { - self_type.def_id(&cx.cache) + let self_type = clean_middle_ty(proj.map_bound(|proj| proj.self_ty()), cx, None, None); + let self_def_id = match parent_def_id { + Some(parent_def_id) => cx.tcx.opt_parent(parent_def_id).or(Some(parent_def_id)), + None => self_type.def_id(&cx.cache), }; - let should_show_cast = compute_should_show_cast(self_def_id, &trait_, &self_type); - Type::QPath(Box::new(QPathData { - assoc: projection_to_path_segment(ty, cx), - should_show_cast, + let should_fully_qualify = should_fully_qualify_path(self_def_id, &trait_, &self_type); + + QPathData { + assoc: projection_to_path_segment(proj, cx), self_type, + should_fully_qualify, trait_: Some(trait_), - })) + } } -fn compute_should_show_cast(self_def_id: Option, trait_: &Path, self_type: &Type) -> bool { +fn should_fully_qualify_path(self_def_id: Option, trait_: &Path, self_type: &Type) -> bool { !trait_.segments.is_empty() && self_def_id .zip(Some(trait_.def_id())) @@ -520,18 +507,17 @@ fn compute_should_show_cast(self_def_id: Option, trait_: &Path, self_type } fn projection_to_path_segment<'tcx>( - ty: ty::Binder<'tcx, ty::AliasTy<'tcx>>, + proj: ty::Binder<'tcx, ty::AliasTerm<'tcx>>, cx: &mut DocContext<'tcx>, ) -> PathSegment { - let def_id = ty.skip_binder().def_id; - let item = cx.tcx.associated_item(def_id); + let def_id = proj.skip_binder().def_id; let generics = cx.tcx.generics_of(def_id); PathSegment { - name: item.name(), + name: cx.tcx.item_name(def_id), args: GenericArgs::AngleBracketed { args: clean_middle_generic_args( cx, - ty.map_bound(|ty| &ty.args[generics.parent_count..]), + proj.map_bound(|ty| &ty.args[generics.parent_count..]), false, def_id, ), @@ -845,7 +831,7 @@ fn clean_ty_generics<'tcx>( .predicates .iter() .flat_map(|(pred, _)| { - let mut projection = None; + let mut proj_pred = None; let param_idx = { let bound_p = pred.kind(); match bound_p.skip_binder() { @@ -860,7 +846,7 @@ fn clean_ty_generics<'tcx>( ty::ClauseKind::Projection(p) if let ty::Param(param) = p.projection_term.self_ty().kind() => { - projection = Some(bound_p.rebind(p)); + proj_pred = Some(bound_p.rebind(p)); Some(param.index) } _ => None, @@ -874,22 +860,12 @@ fn clean_ty_generics<'tcx>( bounds.extend(pred.get_bounds().into_iter().flatten().cloned()); - if let Some(proj) = projection - && let lhs = clean_projection( - proj.map_bound(|p| { - // FIXME: This needs to be made resilient for `AliasTerm`s that - // are associated consts. - p.projection_term.expect_ty(cx.tcx) - }), - cx, - None, - ) - && let Some((_, trait_did, name)) = lhs.projection() - { + if let Some(pred) = proj_pred { + let lhs = clean_projection(pred.map_bound(|p| p.projection_term), cx, None); impl_trait_proj.entry(param_idx).or_default().push(( - trait_did, - name, - proj.map_bound(|p| p.term), + lhs.trait_.unwrap().def_id(), + lhs.assoc, + pred.map_bound(|p| p.term), )); } @@ -1695,10 +1671,11 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type register_res(cx, trait_.res); let self_def_id = DefId::local(qself.hir_id.owner.def_id.local_def_index); let self_type = clean_ty(qself, cx); - let should_show_cast = compute_should_show_cast(Some(self_def_id), &trait_, &self_type); + let should_fully_qualify = + should_fully_qualify_path(Some(self_def_id), &trait_, &self_type); Type::QPath(Box::new(QPathData { assoc: clean_path_segment(p.segments.last().expect("segments were empty"), cx), - should_show_cast, + should_fully_qualify, self_type, trait_: Some(trait_), })) @@ -1707,16 +1684,16 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type let ty = lower_ty(cx.tcx, hir_ty); let self_type = clean_ty(qself, cx); - let (trait_, should_show_cast) = match ty.kind() { + let (trait_, should_fully_qualify) = match ty.kind() { ty::Alias(ty::Projection, proj) => { let res = Res::Def(DefKind::Trait, proj.trait_ref(cx.tcx).def_id); let trait_ = clean_path(&hir::Path { span, res, segments: &[] }, cx); register_res(cx, trait_.res); let self_def_id = res.opt_def_id(); - let should_show_cast = - compute_should_show_cast(self_def_id, &trait_, &self_type); + let should_fully_qualify = + should_fully_qualify_path(self_def_id, &trait_, &self_type); - (Some(trait_), should_show_cast) + (Some(trait_), should_fully_qualify) } ty::Alias(ty::Inherent, _) => (None, false), // Rustdoc handles `ty::Error`s by turning them into `Type::Infer`s. @@ -1726,7 +1703,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type Type::QPath(Box::new(QPathData { assoc: clean_path_segment(segment, cx), - should_show_cast, + should_fully_qualify, self_type, trait_, })) @@ -2145,14 +2122,8 @@ pub(crate) fn clean_middle_ty<'tcx>( .map(|pb| AssocItemConstraint { assoc: projection_to_path_segment( pb.map_bound(|pb| { - pb - // HACK(compiler-errors): Doesn't actually matter what self - // type we put here, because we're only using the GAT's args. - .with_self_ty(cx.tcx, cx.tcx.types.self_param) + pb.with_self_ty(cx.tcx, cx.tcx.types.trait_object_dummy_self) .projection_term - // FIXME: This needs to be made resilient for `AliasTerm`s - // that are associated consts. - .expect_ty(cx.tcx) }), cx, ), @@ -2185,18 +2156,25 @@ pub(crate) fn clean_middle_ty<'tcx>( Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None, None)).collect()) } - ty::Alias(ty::Projection, data) => { - clean_projection(bound_ty.rebind(data), cx, parent_def_id) + ty::Alias(ty::Projection, alias_ty @ ty::AliasTy { def_id, args, .. }) => { + if cx.tcx.is_impl_trait_in_trait(def_id) { + clean_middle_opaque_bounds(cx, def_id, args) + } else { + Type::QPath(Box::new(clean_projection( + bound_ty.rebind(alias_ty.into()), + cx, + parent_def_id, + ))) + } } - ty::Alias(ty::Inherent, alias_ty) => { - let def_id = alias_ty.def_id; + ty::Alias(ty::Inherent, alias_ty @ ty::AliasTy { def_id, .. }) => { let alias_ty = bound_ty.rebind(alias_ty); let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None, None); Type::QPath(Box::new(QPathData { assoc: PathSegment { - name: cx.tcx.associated_item(def_id).name(), + name: cx.tcx.item_name(def_id), args: GenericArgs::AngleBracketed { args: clean_middle_generic_args( cx, @@ -2207,26 +2185,21 @@ pub(crate) fn clean_middle_ty<'tcx>( constraints: Default::default(), }, }, - should_show_cast: false, + should_fully_qualify: false, self_type, trait_: None, })) } - ty::Alias(ty::Free, data) => { + ty::Alias(ty::Free, ty::AliasTy { def_id, args, .. }) => { if cx.tcx.features().lazy_type_alias() { // Free type alias `data` represents the `type X` in `type X = Y`. If we need `Y`, // we need to use `type_of`. - let path = clean_middle_path( - cx, - data.def_id, - false, - ThinVec::new(), - bound_ty.rebind(data.args), - ); + let path = + clean_middle_path(cx, def_id, false, ThinVec::new(), bound_ty.rebind(args)); Type::Path { path } } else { - let ty = cx.tcx.type_of(data.def_id).instantiate(cx.tcx, data.args); + let ty = cx.tcx.type_of(def_id).instantiate(cx.tcx, args); clean_middle_ty(bound_ty.rebind(ty), cx, None, None) } } @@ -2313,18 +2286,17 @@ fn clean_middle_opaque_bounds<'tcx>( let bindings: ThinVec<_> = bounds .iter() .filter_map(|(bound, _)| { - if let ty::ClauseKind::Projection(proj) = bound.kind().skip_binder() - && proj.projection_term.trait_ref(cx.tcx) == trait_ref.skip_binder() + let bound = bound.kind(); + if let ty::ClauseKind::Projection(proj_pred) = bound.skip_binder() + && proj_pred.projection_term.trait_ref(cx.tcx) == trait_ref.skip_binder() { return Some(AssocItemConstraint { assoc: projection_to_path_segment( - // FIXME: This needs to be made resilient for `AliasTerm`s that - // are associated consts. - bound.kind().rebind(proj.projection_term.expect_ty(cx.tcx)), + bound.rebind(proj_pred.projection_term), cx, ), kind: AssocItemConstraintKind::Equality { - term: clean_middle_term(bound.kind().rebind(proj.term), cx), + term: clean_middle_term(bound.rebind(proj_pred.term), cx), }, }); } diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 41943b94d1e21..40efa99786801 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -46,11 +46,8 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: ThinVec) -> ThinVe // Look for equality predicates on associated types that can be merged into // general bound predicates. equalities.retain(|(lhs, rhs)| { - let Some((ty, trait_did, name)) = lhs.projection() else { - return true; - }; - let Some((bounds, _)) = tybounds.get_mut(ty) else { return true }; - merge_bounds(cx, bounds, trait_did, name, rhs) + let Some((bounds, _)) = tybounds.get_mut(&lhs.self_type) else { return true }; + merge_bounds(cx, bounds, lhs.trait_.as_ref().unwrap().def_id(), lhs.assoc.clone(), rhs) }); // And finally, let's reassemble everything diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 9e46d0b47e91b..7ba75d4a8c953 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1341,7 +1341,7 @@ impl PreciseCapturingArg { pub(crate) enum WherePredicate { BoundPredicate { ty: Type, bounds: Vec, bound_params: Vec }, RegionPredicate { lifetime: Lifetime, bounds: Vec }, - EqPredicate { lhs: Type, rhs: Term }, + EqPredicate { lhs: QPathData, rhs: Term }, } impl WherePredicate { @@ -1704,14 +1704,6 @@ impl Type { matches!(self, Type::Tuple(v) if v.is_empty()) } - pub(crate) fn projection(&self) -> Option<(&Type, DefId, PathSegment)> { - if let QPath(box QPathData { self_type, trait_, assoc, .. }) = self { - Some((self_type, trait_.as_ref()?.def_id(), assoc.clone())) - } else { - None - } - } - /// Use this method to get the [DefId] of a [clean] AST node, including [PrimitiveType]s. /// /// [clean]: crate::clean @@ -1746,7 +1738,7 @@ pub(crate) struct QPathData { pub assoc: PathSegment, pub self_type: Type, /// FIXME: compute this field on demand. - pub should_show_cast: bool, + pub should_fully_qualify: bool, pub trait_: Option, } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index e9a7f4367a387..6ab1520386d8e 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1019,18 +1019,33 @@ fn fmt_type( f.write_str("impl ")?; print_generic_bounds(bounds, cx).fmt(f) } - &clean::QPath(box clean::QPathData { - ref assoc, - ref self_type, - ref trait_, - should_show_cast, - }) => { + clean::QPath(qpath) => qpath.print(cx).fmt(f), + } +} + +impl clean::Type { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { + fmt::from_fn(move |f| fmt_type(self, f, false, cx)) + } +} + +impl clean::Path { + pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { + fmt::from_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx)) + } +} + +impl clean::QPathData { + fn print(&self, cx: &Context<'_>) -> impl Display { + let Self { ref assoc, ref self_type, should_fully_qualify, ref trait_ } = *self; + + fmt::from_fn(move |f| { // FIXME(inherent_associated_types): Once we support non-ADT self-types (#106719), // we need to surround them with angle brackets in some cases (e.g. `::P`). if f.alternate() { if let Some(trait_) = trait_ - && should_show_cast + && should_fully_qualify { write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))? } else { @@ -1038,7 +1053,7 @@ fn fmt_type( } } else { if let Some(trait_) = trait_ - && should_show_cast + && should_fully_qualify { write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))? } else { @@ -1090,19 +1105,7 @@ fn fmt_type( }?; assoc.args.print(cx).fmt(f) - } - } -} - -impl clean::Type { - pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| fmt_type(self, f, false, cx)) - } -} - -impl clean::Path { - pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx)) + }) } } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index bfcb794b89a27..3ade40940bc97 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -468,6 +468,9 @@ impl FromClean for WherePredicate { .collect(), }, EqPredicate { lhs, rhs } => WherePredicate::EqPredicate { + // The LHS currently has type `Type` but it should be a `QualifiedPath` since it may + // refer to an associated const. However, `EqPredicate` shouldn't exist in the first + // place: . lhs: lhs.into_json(renderer), rhs: rhs.into_json(renderer), }, @@ -557,12 +560,7 @@ impl FromClean for Type { is_mutable: mutability == ast::Mutability::Mut, type_: Box::new((*type_).into_json(renderer)), }, - QPath(box clean::QPathData { assoc, self_type, trait_, .. }) => Type::QualifiedPath { - name: assoc.name.to_string(), - args: Box::new(assoc.args.into_json(renderer)), - self_type: Box::new(self_type.into_json(renderer)), - trait_: trait_.map(|trait_| trait_.into_json(renderer)), - }, + QPath(qpath) => (*qpath).into_json(renderer), // FIXME(unsafe_binder): Implement rustdoc-json. UnsafeBinder(_) => todo!(), } @@ -579,6 +577,19 @@ impl FromClean for Path { } } +impl FromClean for Type { + fn from_clean(qpath: clean::QPathData, renderer: &JsonRenderer<'_>) -> Self { + let clean::QPathData { assoc, self_type, should_fully_qualify: _, trait_ } = qpath; + + Self::QualifiedPath { + name: assoc.name.to_string(), + args: Box::new(assoc.args.into_json(renderer)), + self_type: Box::new(self_type.into_json(renderer)), + trait_: trait_.map(|trait_| trait_.into_json(renderer)), + } + } +} + impl FromClean for Term { fn from_clean(term: clean::Term, renderer: &JsonRenderer<'_>) -> Term { match term { diff --git a/tests/rustdoc/inline_cross/assoc-const-equality.rs b/tests/rustdoc/inline_cross/assoc-const-equality.rs index ec5c2f748ef5e..36ab027ef71b5 100644 --- a/tests/rustdoc/inline_cross/assoc-const-equality.rs +++ b/tests/rustdoc/inline_cross/assoc-const-equality.rs @@ -1,6 +1,5 @@ //@ aux-crate:assoc_const_equality=assoc-const-equality.rs //@ edition:2021 -//@ ignore-test (FIXME: #125092) #![crate_name = "user"]