diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51dd0f81ed147..2e83bbf643fee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,13 @@ jobs: steps: - name: Checkout the source code uses: actions/checkout@v4 + # Cache citool to make its build faster, as it's in the critical path. + # The rust-cache doesn't bleed into the main `job`, so it should not affect any other + # Rust compilation. + - name: Cache citool + uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 + with: + workspaces: src/ci/citool - name: Calculate the CI job matrix env: COMMIT_MESSAGE: ${{ github.event.head_commit.message }} diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b1d1c35e64a1d..5be740609c7cf 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1765,7 +1765,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ident: Ident, is_anon_in_path: IsAnonInPath, ) -> &'hir hir::Lifetime { - debug_assert_ne!(ident.name, kw::Empty); let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error); let res = match res { LifetimeRes::Param { param, .. } => hir::LifetimeName::Param(param), diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 532c5092a4e94..d6a7bfc446a41 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1600,11 +1600,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(method) } Err(error) => { - if segment.ident.name == kw::Empty { - span_bug!(rcvr.span, "empty method name") - } else { - Err(self.report_method_error(expr.hir_id, rcvr_t, error, expected, false)) - } + Err(self.report_method_error(expr.hir_id, rcvr_t, error, expected, false)) } }; @@ -2941,9 +2937,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ty::new_error(self.tcx(), guar); } - let guar = if field.name == kw::Empty { - self.dcx().span_bug(field.span, "field name with no name") - } else if self.method_exists_for_diagnostic( + let guar = if self.method_exists_for_diagnostic( field, base_ty, expr.hir_id, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 91190a32ff548..87d92b3fbde8d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -27,9 +27,9 @@ use rustc_middle::ty::{ }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; -use rustc_span::{Span, kw}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::traits::{ self, NormalizeExt, ObligationCauseCode, StructurallyNormalizeExt, @@ -833,7 +833,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_missing_method = matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait(); - assert_ne!(item_name.name, kw::Empty); self.report_method_error( hir_id, ty.normalized, diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 76a1ff3cf3828..f0d24d27e85a3 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -290,7 +290,7 @@ pub(crate) fn prepare_session_directory(sess: &Session, crate_name: Symbol) { // Try to remove the session directory we just allocated. We don't // know if there's any garbage in it from the failed copy action. - if let Err(err) = safe_remove_dir_all(&session_dir) { + if let Err(err) = std_fs::remove_dir_all(&session_dir) { sess.dcx().emit_warn(errors::DeletePartial { path: &session_dir, err }); } @@ -324,7 +324,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option) { incr_comp_session_dir.display() ); - if let Err(err) = safe_remove_dir_all(&*incr_comp_session_dir) { + if let Err(err) = std_fs::remove_dir_all(&*incr_comp_session_dir) { sess.dcx().emit_warn(errors::DeleteFull { path: &incr_comp_session_dir, err }); } @@ -715,7 +715,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< for directory_name in session_directories { if !lock_file_to_session_dir.items().any(|(_, dir)| *dir == directory_name) { let path = crate_directory.join(directory_name); - if let Err(err) = safe_remove_dir_all(&path) { + if let Err(err) = std_fs::remove_dir_all(&path) { sess.dcx().emit_warn(errors::InvalidGcFailed { path: &path, err }); } } @@ -821,7 +821,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< all_except_most_recent(deletion_candidates).into_items().all(|(path, lock)| { debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); - if let Err(err) = safe_remove_dir_all(&path) { + if let Err(err) = std_fs::remove_dir_all(&path) { sess.dcx().emit_warn(errors::FinalizedGcFailed { path: &path, err }); } else { delete_session_dir_lock_file(sess, &lock_file_path(&path)); @@ -839,7 +839,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< fn delete_old(sess: &Session, path: &Path) { debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); - if let Err(err) = safe_remove_dir_all(path) { + if let Err(err) = std_fs::remove_dir_all(path) { sess.dcx().emit_warn(errors::SessionGcFailed { path, err }); } else { delete_session_dir_lock_file(sess, &lock_file_path(path)); @@ -862,30 +862,8 @@ fn all_except_most_recent( } } -/// Since paths of artifacts within session directories can get quite long, we -/// need to support deleting files with very long paths. The regular -/// WinApi functions only support paths up to 260 characters, however. In order -/// to circumvent this limitation, we canonicalize the path of the directory -/// before passing it to std::fs::remove_dir_all(). This will convert the path -/// into the '\\?\' format, which supports much longer paths. -fn safe_remove_dir_all(p: &Path) -> io::Result<()> { - let canonicalized = match try_canonicalize(p) { - Ok(canonicalized) => canonicalized, - Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()), - Err(err) => return Err(err), - }; - - std_fs::remove_dir_all(canonicalized) -} - fn safe_remove_file(p: &Path) -> io::Result<()> { - let canonicalized = match try_canonicalize(p) { - Ok(canonicalized) => canonicalized, - Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()), - Err(err) => return Err(err), - }; - - match std_fs::remove_file(canonicalized) { + match std_fs::remove_file(p) { Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(()), result => result, } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 1dbb35f92c2cf..d1d0f7cacaee2 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -688,7 +688,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { let target_ty = self.monomorphize(target_ty); let source_ty = self.monomorphize(source_ty); let (source_ty, target_ty) = - find_vtable_types_for_unsizing(self.tcx.at(span), source_ty, target_ty); + find_tails_for_unsizing(self.tcx.at(span), source_ty, target_ty); // This could also be a different Unsize instruction, like // from a fixed sized array to a slice. But we are only // interested in things that produce a vtable. @@ -1037,36 +1037,35 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> /// /// Finally, there is also the case of custom unsizing coercions, e.g., for /// smart pointers such as `Rc` and `Arc`. -fn find_vtable_types_for_unsizing<'tcx>( +fn find_tails_for_unsizing<'tcx>( tcx: TyCtxtAt<'tcx>, source_ty: Ty<'tcx>, target_ty: Ty<'tcx>, ) -> (Ty<'tcx>, Ty<'tcx>) { - let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| { - let typing_env = ty::TypingEnv::fully_monomorphized(); - if tcx.type_has_metadata(inner_source, typing_env) { - (inner_source, inner_target) - } else { - tcx.struct_lockstep_tails_for_codegen(inner_source, inner_target, typing_env) - } - }; + let typing_env = ty::TypingEnv::fully_monomorphized(); + debug_assert!(!source_ty.has_param(), "{source_ty} should be fully monomorphic"); + debug_assert!(!target_ty.has_param(), "{target_ty} should be fully monomorphic"); match (source_ty.kind(), target_ty.kind()) { - (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _)) - | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => ptr_vtable(a, b), + ( + &ty::Ref(_, source_pointee, _), + &ty::Ref(_, target_pointee, _) | &ty::RawPtr(target_pointee, _), + ) + | (&ty::RawPtr(source_pointee, _), &ty::RawPtr(target_pointee, _)) => { + tcx.struct_lockstep_tails_for_codegen(source_pointee, target_pointee, typing_env) + } + + // `Box` could go through the ADT code below, b/c it'll unpeel to `Unique`, + // and eventually bottom out in a raw ref, but we can micro-optimize it here. (_, _) if let Some(source_boxed) = source_ty.boxed_ty() && let Some(target_boxed) = target_ty.boxed_ty() => { - ptr_vtable(source_boxed, target_boxed) + tcx.struct_lockstep_tails_for_codegen(source_boxed, target_boxed, typing_env) } - // T as dyn* Trait - (_, &ty::Dynamic(_, _, ty::DynStar)) => ptr_vtable(source_ty, target_ty), - (&ty::Adt(source_adt_def, source_args), &ty::Adt(target_adt_def, target_args)) => { assert_eq!(source_adt_def, target_adt_def); - let CustomCoerceUnsized::Struct(coerce_index) = match crate::custom_coerce_unsize_info(tcx, source_ty, target_ty) { Ok(ccu) => ccu, @@ -1075,21 +1074,23 @@ fn find_vtable_types_for_unsizing<'tcx>( return (e, e); } }; + let coerce_field = &source_adt_def.non_enum_variant().fields[coerce_index]; + // We're getting a possibly unnormalized type, so normalize it. + let source_field = + tcx.normalize_erasing_regions(typing_env, coerce_field.ty(*tcx, source_args)); + let target_field = + tcx.normalize_erasing_regions(typing_env, coerce_field.ty(*tcx, target_args)); + find_tails_for_unsizing(tcx, source_field, target_field) + } - let source_fields = &source_adt_def.non_enum_variant().fields; - let target_fields = &target_adt_def.non_enum_variant().fields; - - assert!( - coerce_index.index() < source_fields.len() - && source_fields.len() == target_fields.len() - ); + // `T` as `dyn* Trait` unsizes *directly*. + // + // FIXME(dyn_star): This case is a bit awkward, b/c we're not really computing + // a tail here. We probably should handle this separately in the *caller* of + // this function, rather than returning something that is semantically different + // than what we return above. + (_, &ty::Dynamic(_, _, ty::DynStar)) => (source_ty, target_ty), - find_vtable_types_for_unsizing( - tcx, - source_fields[coerce_index].ty(*tcx, source_args), - target_fields[coerce_index].ty(*tcx, target_args), - ) - } _ => bug!( "find_vtable_types_for_unsizing: invalid coercion {:?} -> {:?}", source_ty, @@ -1308,7 +1309,7 @@ fn visit_mentioned_item<'tcx>( } MentionedItem::UnsizeCast { source_ty, target_ty } => { let (source_ty, target_ty) = - find_vtable_types_for_unsizing(tcx.at(span), source_ty, target_ty); + find_tails_for_unsizing(tcx.at(span), source_ty, target_ty); // This could also be a different Unsize instruction, like // from a fixed sized array to a slice. But we are only // interested in things that produce a vtable. diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 3f3b455f4db24..762e08b2be5f0 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1012,7 +1012,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // HACK(eddyb) `lint_if_path_starts_with_module` needs at least // 2 segments, so the `resolve_path` above won't trigger it. let mut full_path = import.module_path.clone(); - full_path.push(Segment::from_ident(Ident::empty())); + full_path.push(Segment::from_ident(Ident::dummy())); self.lint_if_path_starts_with_module(Some(finalize), &full_path, None); } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index a4e1266e76439..f310aa6550025 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -776,7 +776,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { self.push_disambiguator( disambiguated_field.disambiguator as u64, ); - self.push_ident(field_name.unwrap_or(kw::Empty).as_str()); + self.push_ident(field_name.unwrap().as_str()); field.print(self)?; } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index b6545eada86ae..5242261cf93f6 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2658,6 +2658,10 @@ impl Chain { /// Gets references to the underlying readers in this `Chain`. /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying readers as doing so may corrupt the internal state of this + /// `Chain`. + /// /// # Examples /// /// ```no_run @@ -2915,6 +2919,10 @@ impl Take { /// Gets a reference to the underlying reader. /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying reader as doing so may corrupt the internal limit of this + /// `Take`. + /// /// # Examples /// /// ```no_run diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index fd4650ba28532..cb2bec5a9dfa6 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -23,8 +23,8 @@ runners: os: ubuntu-24.04-16core-64gb <<: *base-job - - &job-macos-xl - os: macos-13 # We use the standard runner for now + - &job-macos + os: macos-13 <<: *base-job - &job-macos-m1 @@ -380,7 +380,7 @@ auto: NO_OVERFLOW_CHECKS: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift - <<: *job-macos-xl + <<: *job-macos - name: dist-apple-various env: @@ -397,18 +397,18 @@ auto: NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 - <<: *job-macos-xl + <<: *job-macos - name: x86_64-apple-1 env: <<: *env-x86_64-apple-tests - <<: *job-macos-xl + <<: *job-macos - name: x86_64-apple-2 env: SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc <<: *env-x86_64-apple-tests - <<: *job-macos-xl + <<: *job-macos - name: dist-aarch64-apple env: diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 7786b216112bf..31aa84535cc4e 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -517,7 +517,7 @@ impl Item { Some(RenderedLink { original_text: s.clone(), new_text: link_text.clone(), - tooltip: link_tooltip(*id, fragment, cx), + tooltip: link_tooltip(*id, fragment, cx).to_string(), href, }) } else { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 41e9a5a665169..4998c671b61ed 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -11,6 +11,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::fmt::{self, Display, Write}; use std::iter::{self, once}; +use std::slice; use itertools::Either; use rustc_abi::ExternAbi; @@ -650,33 +651,35 @@ pub(crate) fn href_relative_parts<'fqp>( } } -pub(crate) fn link_tooltip(did: DefId, fragment: &Option, cx: &Context<'_>) -> String { - let cache = cx.cache(); - let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) - else { - return String::new(); - }; - let mut buf = String::new(); - let fqp = if *shortty == ItemType::Primitive { - // primitives are documented in a crate, but not actually part of it - &fqp[fqp.len() - 1..] - } else { - fqp - }; - if let &Some(UrlFragment::Item(id)) = fragment { - write_str(&mut buf, format_args!("{} ", cx.tcx().def_descr(id))); - for component in fqp { - write_str(&mut buf, format_args!("{component}::")); - } - write_str(&mut buf, format_args!("{}", cx.tcx().item_name(id))); - } else if !fqp.is_empty() { - let mut fqp_it = fqp.iter(); - write_str(&mut buf, format_args!("{shortty} {}", fqp_it.next().unwrap())); - for component in fqp_it { - write_str(&mut buf, format_args!("::{component}")); +pub(crate) fn link_tooltip( + did: DefId, + fragment: &Option, + cx: &Context<'_>, +) -> impl fmt::Display { + fmt::from_fn(move |f| { + let cache = cx.cache(); + let Some((fqp, shortty)) = cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) + else { + return Ok(()); + }; + let fqp = if *shortty == ItemType::Primitive { + // primitives are documented in a crate, but not actually part of it + slice::from_ref(fqp.last().unwrap()) + } else { + fqp + }; + if let &Some(UrlFragment::Item(id)) = fragment { + write!(f, "{} ", cx.tcx().def_descr(id))?; + for component in fqp { + write!(f, "{component}::")?; + } + write!(f, "{}", cx.tcx().item_name(id))?; + } else if !fqp.is_empty() { + write!(f, "{shortty} ")?; + fqp.iter().joined("::", f)?; } - } - buf + Ok(()) + }) } /// Used to render a [`clean::Path`]. diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index e2d1f58a37ecb..596ac665fc313 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -650,15 +650,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { bar.render_into(&mut sidebar).unwrap(); - let v = layout::render( - &shared.layout, - &page, - sidebar, - BufDisplay(|buf: &mut String| { - all.print(buf); - }), - &shared.style_files, - ); + let v = layout::render(&shared.layout, &page, sidebar, all.print(), &shared.style_files); shared.fs.write(final_file, v)?; // if to avoid writing help, settings files to doc root unless we're on the final invocation diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 21c823f49d15f..94171ad6de801 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -40,6 +40,7 @@ mod span_map; mod type_layout; mod write_shared; +use std::borrow::Cow; use std::collections::VecDeque; use std::fmt::{self, Display as _, Write}; use std::iter::Peekable; @@ -47,6 +48,7 @@ use std::path::PathBuf; use std::{fs, str}; use askama::Template; +use itertools::Either; use rustc_attr_parsing::{ ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince, }; @@ -98,6 +100,19 @@ enum AssocItemRender<'a> { DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool }, } +impl AssocItemRender<'_> { + fn render_mode(&self) -> RenderMode { + match self { + Self::All => RenderMode::Normal, + &Self::DerefFor { deref_mut_, .. } => RenderMode::ForDeref { mut_: deref_mut_ }, + } + } + + fn class(&self) -> Option<&'static str> { + if let Self::DerefFor { .. } = self { Some("impl-items") } else { None } + } +} + /// For different handling of associated items from the Deref target of a type rather than the type /// itself. #[derive(Copy, Clone, PartialEq)] @@ -439,44 +454,49 @@ impl AllTypes { sections } - fn print(&self, f: &mut String) { - fn print_entries(f: &mut String, e: &FxIndexSet, kind: ItemSection) { - if !e.is_empty() { + fn print(&self) -> impl fmt::Display { + fn print_entries(e: &FxIndexSet, kind: ItemSection) -> impl fmt::Display { + fmt::from_fn(move |f| { + if e.is_empty() { + return Ok(()); + } + let mut e: Vec<&ItemEntry> = e.iter().collect(); e.sort(); - write_str( + write!( f, - format_args!( - "

{title}

    ", - id = kind.id(), - title = kind.name(), - ), - ); + "

    {title}

      ", + id = kind.id(), + title = kind.name(), + )?; for s in e.iter() { - write_str(f, format_args!("
    • {}
    • ", s.print())); + write!(f, "
    • {}
    • ", s.print())?; } - f.push_str("
    "); - } + f.write_str("
") + }) } - f.push_str("

List of all items

"); - // Note: print_entries does not escape the title, because we know the current set of titles - // doesn't require escaping. - print_entries(f, &self.structs, ItemSection::Structs); - print_entries(f, &self.enums, ItemSection::Enums); - print_entries(f, &self.unions, ItemSection::Unions); - print_entries(f, &self.primitives, ItemSection::PrimitiveTypes); - print_entries(f, &self.traits, ItemSection::Traits); - print_entries(f, &self.macros, ItemSection::Macros); - print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros); - print_entries(f, &self.derive_macros, ItemSection::DeriveMacros); - print_entries(f, &self.functions, ItemSection::Functions); - print_entries(f, &self.type_aliases, ItemSection::TypeAliases); - print_entries(f, &self.trait_aliases, ItemSection::TraitAliases); - print_entries(f, &self.statics, ItemSection::Statics); - print_entries(f, &self.constants, ItemSection::Constants); + fmt::from_fn(|f| { + f.write_str("

List of all items

")?; + // Note: print_entries does not escape the title, because we know the current set of titles + // doesn't require escaping. + print_entries(&self.structs, ItemSection::Structs).fmt(f)?; + print_entries(&self.enums, ItemSection::Enums).fmt(f)?; + print_entries(&self.unions, ItemSection::Unions).fmt(f)?; + print_entries(&self.primitives, ItemSection::PrimitiveTypes).fmt(f)?; + print_entries(&self.traits, ItemSection::Traits).fmt(f)?; + print_entries(&self.macros, ItemSection::Macros).fmt(f)?; + print_entries(&self.attribute_macros, ItemSection::AttributeMacros).fmt(f)?; + print_entries(&self.derive_macros, ItemSection::DeriveMacros).fmt(f)?; + print_entries(&self.functions, ItemSection::Functions).fmt(f)?; + print_entries(&self.type_aliases, ItemSection::TypeAliases).fmt(f)?; + print_entries(&self.trait_aliases, ItemSection::TraitAliases).fmt(f)?; + print_entries(&self.statics, ItemSection::Statics).fmt(f)?; + print_entries(&self.constants, ItemSection::Constants).fmt(f)?; + Ok(()) + }) } } @@ -1205,7 +1225,7 @@ impl<'a> AssocItemLink<'a> { } fn write_section_heading( - title: &str, + title: impl fmt::Display, id: &str, extra_class: Option<&str>, extra: impl fmt::Display, @@ -1225,7 +1245,7 @@ fn write_section_heading( }) } -fn write_impl_section_heading(title: &str, id: &str) -> impl fmt::Display { +fn write_impl_section_heading(title: impl fmt::Display, id: &str) -> impl fmt::Display { write_section_heading(title, id, None, "") } @@ -1302,20 +1322,17 @@ fn render_assoc_items_inner( let (mut non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); if !non_trait.is_empty() { - let mut close_tags = >::with_capacity(1); - let mut tmp_buf = String::new(); - let (render_mode, id, class_html) = match what { - AssocItemRender::All => { - write_str( - &mut tmp_buf, - format_args!( - "{}", - write_impl_section_heading("Implementations", "implementations") - ), - ); - (RenderMode::Normal, "implementations-list".to_owned(), "") - } - AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { + let render_mode = what.render_mode(); + let class_html = what + .class() + .map(|class| fmt::from_fn(move |f| write!(f, r#" class="{class}""#))) + .maybe_display(); + let (section_heading, id) = match what { + AssocItemRender::All => ( + Either::Left(write_impl_section_heading("Implementations", "implementations")), + Cow::Borrowed("implementations-list"), + ), + AssocItemRender::DerefFor { trait_, type_, .. } => { let id = cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); // the `impls.get` above only looks at the outermost type, @@ -1329,25 +1346,27 @@ fn render_assoc_items_inner( type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache) }); let derived_id = cx.derive_id(&id); - close_tags.push(""); - write_str( - &mut tmp_buf, - format_args!( - "
{}", - write_impl_section_heading( - &format!( - "Methods from {trait_}<Target = {type_}>", - trait_ = trait_.print(cx), - type_ = type_.print(cx), - ), - &id, - ) - ), - ); if let Some(def_id) = type_.def_id(cx.cache()) { - cx.deref_id_map.borrow_mut().insert(def_id, id); + cx.deref_id_map.borrow_mut().insert(def_id, id.clone()); } - (RenderMode::ForDeref { mut_: deref_mut_ }, derived_id, r#" class="impl-items""#) + ( + Either::Right(fmt::from_fn(move |f| { + write!( + f, + "
{}", + write_impl_section_heading( + fmt::from_fn(|f| write!( + f, + "Methods from {trait_}<Target = {type_}>", + trait_ = trait_.print(cx), + type_ = type_.print(cx), + )), + &id, + ) + ) + })), + Cow::Owned(derived_id), + ) } }; let mut impls_buf = String::new(); @@ -1375,10 +1394,14 @@ fn render_assoc_items_inner( ); } if !impls_buf.is_empty() { - write!(w, "{tmp_buf}
{impls_buf}
").unwrap(); - for tag in close_tags.into_iter().rev() { - w.write_str(tag).unwrap(); - } + write!( + w, + "{section_heading}
{impls_buf}
{}", + matches!(what, AssocItemRender::DerefFor { .. }) + .then_some("
") + .maybe_display(), + ) + .unwrap(); } } @@ -1639,8 +1662,8 @@ fn render_impl( // `containing_item` is used for rendering stability info. If the parent is a trait impl, // `containing_item` will the grandparent, since trait impls can't have stability attached. fn doc_impl_item( - boring: &mut String, - interesting: &mut String, + boring: impl fmt::Write, + interesting: impl fmt::Write, cx: &Context<'_>, item: &clean::Item, parent: &clean::Item, @@ -1649,7 +1672,7 @@ fn render_impl( is_default_item: bool, trait_: Option<&clean::Trait>, rendering_params: ImplRenderingParameters, - ) { + ) -> fmt::Result { let item_type = item.type_(); let name = item.name.as_ref().unwrap(); @@ -1724,15 +1747,16 @@ fn render_impl( ); } } - let w = if short_documented && trait_.is_some() { interesting } else { boring }; + let mut w = if short_documented && trait_.is_some() { + Either::Left(interesting) + } else { + Either::Right(boring) + }; let toggled = !doc_buffer.is_empty(); if toggled { let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" }; - write_str( - w, - format_args!("
"), - ); + write!(w, "
")?; } match &item.kind { clean::MethodItem(..) | clean::RequiredMethodItem(_) => { @@ -1747,172 +1771,151 @@ fn render_impl( .find(|item| item.name.map(|n| n == *name).unwrap_or(false)) }) .map(|item| format!("{}.{name}", item.type_())); - write_str( + write!( w, - format_args!( - "
\ + "
\ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode) + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("§")); + write!(w, "§")?; } - write_str( + write!( w, - format_args!( - "

{}

", - render_assoc_item( - item, - link.anchor(source_id.as_ref().unwrap_or(&id)), - ItemType::Impl, - cx, - render_mode, - ), + "

{}

", + render_assoc_item( + item, + link.anchor(source_id.as_ref().unwrap_or(&id)), + ItemType::Impl, + cx, + render_mode, ), - ); + )?; } } clean::RequiredAssocConstItem(generics, ty) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "
\ + "
\ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode) + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("§")); + write!(w, "§")?; } - write_str( + write!( w, - format_args!( - "

{}

", - assoc_const( - item, - generics, - ty, - AssocConstValue::None, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "

{}

", + assoc_const( + item, + generics, + ty, + AssocConstValue::None, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "
\ + "
\ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode), + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("§")); + write!(w, "§")?; } - write_str( + write!( w, - format_args!( - "

{}

", - assoc_const( - item, - &ci.generics, - &ci.type_, - match item.kind { - clean::ProvidedAssocConstItem(_) => - AssocConstValue::TraitDefault(&ci.kind), - clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind), - _ => unreachable!(), - }, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "

{}

", + assoc_const( + item, + &ci.generics, + &ci.type_, + match item.kind { + clean::ProvidedAssocConstItem(_) => + AssocConstValue::TraitDefault(&ci.kind), + clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind), + _ => unreachable!(), + }, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } clean::RequiredAssocTypeItem(generics, bounds) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "
\ + "
\ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode), + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("§")); + write!(w, "§")?; } - write_str( + write!( w, - format_args!( - "

{}

", - assoc_type( - item, - generics, - bounds, - None, - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "

{}

", + assoc_type( + item, + generics, + bounds, + None, + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } clean::AssocTypeItem(tydef, _bounds) => { let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); - write_str( + write!( w, - format_args!( - "
\ + "
\ {}", - render_rightside(cx, item, render_mode) - ), - ); + render_rightside(cx, item, render_mode), + )?; if trait_.is_some() { // Anchors are only used on trait impls. - write_str(w, format_args!("§")); + write!(w, "§")?; } - write_str( + write!( w, - format_args!( - "

{}

", - assoc_type( - item, - &tydef.generics, - &[], // intentionally leaving out bounds - Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), - link.anchor(if trait_.is_some() { &source_id } else { &id }), - 0, - cx, - ) + "

{}

", + assoc_type( + item, + &tydef.generics, + &[], // intentionally leaving out bounds + Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)), + link.anchor(if trait_.is_some() { &source_id } else { &id }), + 0, + cx, ), - ); + )?; } - clean::StrippedItem(..) => return, + clean::StrippedItem(..) => return Ok(()), _ => panic!("can't make docs for trait item with name {:?}", item.name), } - w.push_str(&info_buffer); + w.write_str(&info_buffer)?; if toggled { - w.push_str("
"); - w.push_str(&doc_buffer); - w.push_str("
"); + write!(w, "
{doc_buffer}
")?; } + Ok(()) } let mut impl_items = String::new(); @@ -1955,7 +1958,7 @@ fn render_impl( false, trait_, rendering_params, - ); + )?; } _ => {} } @@ -1973,7 +1976,7 @@ fn render_impl( false, trait_, rendering_params, - ); + )?; } for method in methods { doc_impl_item( @@ -1987,20 +1990,20 @@ fn render_impl( false, trait_, rendering_params, - ); + )?; } } fn render_default_items( - boring: &mut String, - interesting: &mut String, + mut boring: impl fmt::Write, + mut interesting: impl fmt::Write, cx: &Context<'_>, t: &clean::Trait, i: &clean::Impl, parent: &clean::Item, render_mode: RenderMode, rendering_params: ImplRenderingParameters, - ) { + ) -> fmt::Result { for trait_item in &t.items { // Skip over any default trait items that are impossible to reference // (e.g. if it has a `Self: Sized` bound on an unsized type). @@ -2020,8 +2023,8 @@ fn render_impl( let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods); doc_impl_item( - boring, - interesting, + &mut boring, + &mut interesting, cx, trait_item, parent, @@ -2030,8 +2033,9 @@ fn render_impl( true, Some(t), rendering_params, - ); + )?; } + Ok(()) } // If we've implemented a trait, then also emit documentation for all @@ -2051,7 +2055,7 @@ fn render_impl( &i.impl_item, render_mode, rendering_params, - ); + )?; } } if render_mode == RenderMode::Normal { diff --git a/src/librustdoc/html/render/tests.rs b/src/librustdoc/html/render/tests.rs index 657cd3c82aae3..327a30887b1db 100644 --- a/src/librustdoc/html/render/tests.rs +++ b/src/librustdoc/html/render/tests.rs @@ -47,8 +47,7 @@ fn test_all_types_prints_header_once() { // Regression test for #82477 let all_types = AllTypes::new(); - let mut buffer = String::new(); - all_types.print(&mut buffer); + let buffer = all_types.print().to_string(); assert_eq!(1, buffer.matches("List of all items").count()); } diff --git a/tests/crashes/74451.rs b/tests/crashes/74451.rs deleted file mode 100644 index 8f93699467896..0000000000000 --- a/tests/crashes/74451.rs +++ /dev/null @@ -1,42 +0,0 @@ -//@ known-bug: #74451 -//@ compile-flags: -Copt-level=0 - -#![feature(specialization)] -#![feature(unsize, coerce_unsized)] -#![allow(incomplete_features)] -#![crate_type = "lib"] - -use std::ops::CoerceUnsized; - -pub struct SmartassPtr(A::Data); - -pub trait Smartass { - type Data; - type Data2: CoerceUnsized<*const [u8]>; -} - -pub trait MaybeObjectSafe {} - -impl MaybeObjectSafe for () {} - -impl Smartass for T { - type Data = ::Data2; - default type Data2 = *const [u8; 0]; -} - -impl Smartass for () { - type Data2 = *const [u8; 1]; -} - -impl Smartass for dyn MaybeObjectSafe { - type Data = *const [u8]; - type Data2 = *const [u8; 0]; -} - -impl CoerceUnsized> for SmartassPtr - where ::Data: std::ops::CoerceUnsized<::Data> -{} - -pub fn conv(s: SmartassPtr<()>) -> SmartassPtr { - s // This shouldn't coerce -} diff --git a/tests/ui/coercion/codegen-smart-pointer-with-alias.rs b/tests/ui/coercion/codegen-smart-pointer-with-alias.rs new file mode 100644 index 0000000000000..a68952bb70aa2 --- /dev/null +++ b/tests/ui/coercion/codegen-smart-pointer-with-alias.rs @@ -0,0 +1,32 @@ +//@ build-pass + +// Regression test for . + +// Make sure that the unsize coercion we collect in mono for `Signal -> Signal` +// doesn't choke on the fact that the inner unsized field of `Signal` is a (trivial) alias. +// This exercises a normalize call that is necessary since we're getting a type from the type +// system, which isn't guaranteed to be normalized after substitution. + +#![feature(coerce_unsized)] + +use std::ops::CoerceUnsized; + +trait Mirror { + type Assoc: ?Sized; +} +impl Mirror for T { + type Assoc = T; +} + +trait Any {} +impl Any for T {} + +struct Signal<'a, T: ?Sized>(<&'a T as Mirror>::Assoc); + +// This `CoerceUnsized` impl isn't special; it's a bit more restricted than we'd see in the wild, +// but this ICE also reproduces if we were to make it general over `Signal -> Signal`. +impl<'a> CoerceUnsized> for Signal<'a, i32> {} + +fn main() { + Signal(&1i32) as Signal; +}