diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index d57fad6ba4c2d..4561f9d9b49df 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2708,6 +2708,13 @@ impl PreciseCapturingArg<'_> { PreciseCapturingArg::Param(param) => param.hir_id, } } + + pub fn name(self) -> Symbol { + match self { + PreciseCapturingArg::Lifetime(lt) => lt.ident.name, + PreciseCapturingArg::Param(param) => param.ident.name, + } + } } /// We need to have a [`Node`] for the [`HirId`] that we attach the type/const param diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 0acc119115a70..05ef0374e5d34 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -86,6 +86,7 @@ pub fn provide(providers: &mut Providers) { coroutine_for_closure, is_type_alias_impl_trait, find_field, + rendered_precise_capturing_args, ..*providers }; } @@ -1899,3 +1900,23 @@ fn is_type_alias_impl_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool _ => bug!("tried getting opaque_ty_origin for non-opaque: {:?}", def_id), } } + +fn rendered_precise_capturing_args<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> Option<&'tcx [Symbol]> { + if let Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) = + tcx.opt_rpitit_info(def_id.to_def_id()) + { + return tcx.rendered_precise_capturing_args(opaque_def_id); + } + + tcx.hir_node_by_def_id(def_id).expect_item().expect_opaque_ty().bounds.iter().find_map( + |bound| match bound { + hir::GenericBound::Use(args, ..) => { + Some(&*tcx.arena.alloc_from_iter(args.iter().map(|arg| arg.name()))) + } + _ => None, + }, + ) +} diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 6b240f0f0b3de..bbd9ab5704fd8 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -72,6 +72,15 @@ impl<'a, 'tcx, T: Copy + Decodable>> ProcessQueryValue<' } } +impl<'a, 'tcx, T: Copy + Decodable>> + ProcessQueryValue<'tcx, Option<&'tcx [T]>> for Option> +{ + #[inline(always)] + fn process_decoded(self, tcx: TyCtxt<'tcx>, _err: impl Fn() -> !) -> Option<&'tcx [T]> { + if let Some(iter) = self { Some(&*tcx.arena.alloc_from_iter(iter)) } else { None } + } +} + impl ProcessQueryValue<'_, Option> for Option { #[inline(always)] fn process_decoded(self, _tcx: TyCtxt<'_>, _err: impl Fn() -> !) -> Option { @@ -249,6 +258,7 @@ provide! { tcx, def_id, other, cdata, .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) } mir_const_qualif => { table } rendered_const => { table } + rendered_precise_capturing_args => { table } asyncness => { table_direct } fn_arg_names => { table } coroutine_kind => { table_direct } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 209316ca20fdb..9a7c8e9e12d76 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1496,6 +1496,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.tables .is_type_alias_impl_trait .set(def_id.index, self.tcx.is_type_alias_impl_trait(def_id)); + self.encode_precise_capturing_args(def_id); } if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) @@ -1636,12 +1637,21 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { <- self.tcx.assumed_wf_types_for_rpitit(def_id) ); } + self.encode_precise_capturing_args(def_id); } if item.is_effects_desugaring { self.tables.is_effects_desugaring.set(def_id.index, true); } } + fn encode_precise_capturing_args(&mut self, def_id: DefId) { + let Some(precise_capturing_args) = self.tcx.rendered_precise_capturing_args(def_id) else { + return; + }; + + record_array!(self.tables.rendered_precise_capturing_args[def_id] <- precise_capturing_args); + } + fn encode_mir(&mut self) { if self.is_proc_macro { return; diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 2a44b3423ae2f..e565c8c1ea1c9 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -442,6 +442,7 @@ define_tables! { coerce_unsized_info: Table>, mir_const_qualif: Table>, rendered_const: Table>, + rendered_precise_capturing_args: Table>, asyncness: Table, fn_arg_names: Table>, coroutine_kind: Table, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 33c27d41d8642..d18eb7ab1a665 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1261,6 +1261,7 @@ rustc_queries! { desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } separate_provide_extern } + /// Gets the rendered value of the specified constant or associated constant. /// Used by rustdoc. query rendered_const(def_id: DefId) -> &'tcx String { @@ -1268,6 +1269,12 @@ rustc_queries! { desc { |tcx| "rendering constant initializer of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } + /// Gets the rendered precise capturing args for an opaque for use in rustdoc. + query rendered_precise_capturing_args(def_id: DefId) -> Option<&'tcx [Symbol]> { + desc { |tcx| "computing precise capturing args for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + query impl_parent(def_id: DefId) -> Option { desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } separate_provide_extern diff --git a/rustfmt.toml b/rustfmt.toml index b15ffdca38a06..e060fd8fe8bfa 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -22,6 +22,8 @@ ignore = [ "/tests/rustdoc-ui/", # Some have syntax errors, some are whitespace-sensitive. "/tests/ui/", # Some have syntax errors, some are whitespace-sensitive. "/tests/ui-fulldeps/", # Some are whitespace-sensitive (e.g. `// ~ERROR` comments). + # #[cfg(bootstrap)] so that t-release sees this when they search for it + "/tests/rustdoc-json/impl-trait-precise-capturing.rs", # Do not format submodules. # FIXME: sync submodule list with tidy/bootstrap/etc diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index aa596897fc42f..5dd7f03638956 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -228,8 +228,9 @@ fn clean_generic_bound<'tcx>( GenericBound::TraitBound(clean_poly_trait_ref(t, cx), modifier) } - // FIXME(precise_capturing): Implement rustdoc support - hir::GenericBound::Use(..) => return None, + hir::GenericBound::Use(args, ..) => { + GenericBound::Use(args.iter().map(|arg| arg.name()).collect()) + } }) } @@ -460,13 +461,7 @@ fn clean_projection<'tcx>( def_id: Option, ) -> Type { if cx.tcx.is_impl_trait_in_trait(ty.skip_binder().def_id) { - let bounds = cx - .tcx - .explicit_item_bounds(ty.skip_binder().def_id) - .iter_instantiated_copied(cx.tcx, ty.skip_binder().args) - .map(|(pred, _)| pred) - .collect::>(); - return clean_middle_opaque_bounds(cx, bounds); + return clean_middle_opaque_bounds(cx, ty.skip_binder().def_id, ty.skip_binder().args); } let trait_ = clean_trait_ref_with_constraints( @@ -2242,13 +2237,7 @@ pub(crate) fn clean_middle_ty<'tcx>( *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, // by looking up the bounds associated with the def_id. - let bounds = cx - .tcx - .explicit_item_bounds(def_id) - .iter_instantiated_copied(cx.tcx, args) - .map(|(bound, _)| bound) - .collect::>(); - let ty = clean_middle_opaque_bounds(cx, bounds); + let ty = clean_middle_opaque_bounds(cx, def_id, args); if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { *count -= 1; if *count == 0 { @@ -2271,12 +2260,20 @@ pub(crate) fn clean_middle_ty<'tcx>( fn clean_middle_opaque_bounds<'tcx>( cx: &mut DocContext<'tcx>, - bounds: Vec>, + impl_trait_def_id: DefId, + args: ty::GenericArgsRef<'tcx>, ) -> Type { let mut has_sized = false; + + let bounds: Vec<_> = cx + .tcx + .explicit_item_bounds(impl_trait_def_id) + .iter_instantiated_copied(cx.tcx, args) + .collect(); + let mut bounds = bounds .iter() - .filter_map(|bound| { + .filter_map(|(bound, _)| { let bound_predicate = bound.kind(); let trait_ref = match bound_predicate.skip_binder() { ty::ClauseKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), @@ -2295,7 +2292,7 @@ fn clean_middle_opaque_bounds<'tcx>( let bindings: ThinVec<_> = bounds .iter() - .filter_map(|bound| { + .filter_map(|(bound, _)| { if let ty::ClauseKind::Projection(proj) = bound.kind().skip_binder() { if proj.projection_term.trait_ref(cx.tcx) == trait_ref.skip_binder() { Some(AssocItemConstraint { @@ -2335,6 +2332,10 @@ fn clean_middle_opaque_bounds<'tcx>( bounds.insert(0, GenericBound::sized(cx)); } + if let Some(args) = cx.tcx.rendered_precise_capturing_args(impl_trait_def_id) { + bounds.push(GenericBound::Use(args.to_vec())); + } + ImplTrait(bounds) } diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 58eef36677b23..1b7d84add1f85 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -79,7 +79,7 @@ pub(crate) fn merge_bounds( !bounds.iter_mut().any(|b| { let trait_ref = match *b { clean::GenericBound::TraitBound(ref mut tr, _) => tr, - clean::GenericBound::Outlives(..) => return false, + clean::GenericBound::Outlives(..) | clean::GenericBound::Use(_) => return false, }; // If this QPath's trait `trait_did` is the same as, or a supertrait // of, the bound's trait `did` then we can keep going, otherwise diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index fe01d17b08a8c..a31adc9949a3f 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1244,6 +1244,8 @@ impl Eq for Attributes {} pub(crate) enum GenericBound { TraitBound(PolyTrait, hir::TraitBoundModifier), Outlives(Lifetime), + /// `use<'a, T>` precise-capturing bound syntax + Use(Vec), } impl GenericBound { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 4268fadd6c59c..9b0b2571ec115 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -412,6 +412,20 @@ impl clean::GenericBound { })?; ty.print(cx).fmt(f) } + clean::GenericBound::Use(args) => { + if f.alternate() { + f.write_str("use<")?; + } else { + f.write_str("use<")?; + } + for (i, arg) in args.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + arg.fmt(f)?; + } + if f.alternate() { f.write_str(">") } else { f.write_str(">") } + } }) } } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 5111e363c522e..4ab0df3670859 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -542,6 +542,7 @@ impl FromWithTcx for GenericBound { } } Outlives(lifetime) => GenericBound::Outlives(convert_lifetime(lifetime)), + Use(args) => GenericBound::Use(args.into_iter().map(|arg| arg.to_string()).collect()), } } } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 89115d4d7d667..6fd23b60c8ad6 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; /// rustdoc format-version. -pub const FORMAT_VERSION: u32 = 31; +pub const FORMAT_VERSION: u32 = 32; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow @@ -538,6 +538,8 @@ pub enum GenericBound { modifier: TraitBoundModifier, }, Outlives(String), + /// `use<'a, T>` precise-capturing bound syntax + Use(Vec), } #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index cd011dce7844e..ea1e573384b82 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -298,6 +298,7 @@ impl<'a> Validator<'a> { generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } GenericBound::Outlives(_) => {} + GenericBound::Use(_) => {} } } diff --git a/tests/rustdoc-json/impl-trait-precise-capturing.rs b/tests/rustdoc-json/impl-trait-precise-capturing.rs new file mode 100644 index 0000000000000..bf98868d1453d --- /dev/null +++ b/tests/rustdoc-json/impl-trait-precise-capturing.rs @@ -0,0 +1,6 @@ +#![feature(precise_capturing)] + +// @is "$.index[*][?(@.name=='hello')].inner.function.decl.output.impl_trait[1].use[0]" \"\'a\" +// @is "$.index[*][?(@.name=='hello')].inner.function.decl.output.impl_trait[1].use[1]" \"T\" +// @is "$.index[*][?(@.name=='hello')].inner.function.decl.output.impl_trait[1].use[2]" \"N\" +pub fn hello<'a, T, const N: usize>() -> impl Sized + use<'a, T, N> {} diff --git a/tests/rustdoc/auxiliary/precise-capturing.rs b/tests/rustdoc/auxiliary/precise-capturing.rs new file mode 100644 index 0000000000000..531d4dfdccc6e --- /dev/null +++ b/tests/rustdoc/auxiliary/precise-capturing.rs @@ -0,0 +1,7 @@ +#![feature(precise_capturing)] + +pub fn cross_crate_empty() -> impl Sized + use<> {} + +pub fn cross_crate_missing() -> impl Sized {} + +pub fn cross_crate_args<'a, T, const N: usize>() -> impl Sized + use<'a, T, N> {} diff --git a/tests/rustdoc/impl-trait-precise-capturing.rs b/tests/rustdoc/impl-trait-precise-capturing.rs new file mode 100644 index 0000000000000..baf563dccb3d9 --- /dev/null +++ b/tests/rustdoc/impl-trait-precise-capturing.rs @@ -0,0 +1,27 @@ +//@ aux-build:precise-capturing.rs + +#![crate_name = "foo"] +#![feature(precise_capturing)] + +extern crate precise_capturing; + +//@ has foo/fn.two.html '//section[@id="main-content"]//pre' "-> impl Sized + use<'b, 'a>" +pub fn two<'a, 'b, 'c>() -> impl Sized + use<'b, 'a /* no 'c */> {} + +//@ has foo/fn.params.html '//section[@id="main-content"]//pre' "-> impl Sized + use<'a, T, N>" +pub fn params<'a, T, const N: usize>() -> impl Sized + use<'a, T, N> {} + +//@ has foo/fn.none.html '//section[@id="main-content"]//pre' "-> impl Sized + use<>" +pub fn none() -> impl Sized + use<> {} + +//@ has foo/fn.first.html '//section[@id="main-content"]//pre' "-> impl use<> + Sized" +pub fn first() -> impl use<> + Sized {} + +//@ has foo/fn.cross_crate_empty.html '//section[@id="main-content"]//pre' "-> impl Sized + use<>" +pub use precise_capturing::cross_crate_empty; + +//@ has foo/fn.cross_crate_missing.html '//section[@id="main-content"]//pre' "-> impl Sized" +pub use precise_capturing::cross_crate_missing; + +//@ has foo/fn.cross_crate_args.html '//section[@id="main-content"]//pre' "-> impl Sized + use<'a, T, N>" +pub use precise_capturing::cross_crate_args;