mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Auto merge of #139881 - matthiaskrgr:rollup-7x6zcrc, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #138455 (`librustdoc`: more `impl fmt::Display`) - #139818 (Normalize ADT field in `find_tails_for_unsizing`) - #139819 (Use `rust-cache` to speed-up `citool` compilation) - #139824 (Remove safe remove) - #139848 ( Reduce kw::Empty usage, part 5) - #139859 (CI: rename MacOS runner) - #139877 (Add warning comment to `Take::get_ref` and `Chain::get_ref`) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
38c560ae68
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@ -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 }}
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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<Svh>) {
|
||||
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,
|
||||
}
|
||||
|
@ -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<T>` could go through the ADT code below, b/c it'll unpeel to `Unique<T>`,
|
||||
// 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 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()
|
||||
);
|
||||
|
||||
find_vtable_types_for_unsizing(
|
||||
tcx,
|
||||
source_fields[coerce_index].ty(*tcx, source_args),
|
||||
target_fields[coerce_index].ty(*tcx, target_args),
|
||||
)
|
||||
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)
|
||||
}
|
||||
|
||||
// `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),
|
||||
|
||||
_ => 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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)?;
|
||||
}
|
||||
|
@ -2658,6 +2658,10 @@ impl<T, U> Chain<T, U> {
|
||||
|
||||
/// 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<T> Take<T> {
|
||||
|
||||
/// 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
|
||||
|
@ -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:
|
||||
|
@ -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 {
|
||||
|
@ -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<UrlFragment>, 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}::"));
|
||||
pub(crate) fn link_tooltip(
|
||||
did: DefId,
|
||||
fragment: &Option<UrlFragment>,
|
||||
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)?;
|
||||
}
|
||||
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}"));
|
||||
}
|
||||
}
|
||||
buf
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Used to render a [`clean::Path`].
|
||||
|
@ -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
|
||||
|
@ -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<ItemEntry>, kind: ItemSection) {
|
||||
if !e.is_empty() {
|
||||
let mut e: Vec<&ItemEntry> = e.iter().collect();
|
||||
e.sort();
|
||||
write_str(
|
||||
f,
|
||||
format_args!(
|
||||
"<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
|
||||
id = kind.id(),
|
||||
title = kind.name(),
|
||||
),
|
||||
);
|
||||
|
||||
for s in e.iter() {
|
||||
write_str(f, format_args!("<li>{}</li>", s.print()));
|
||||
fn print(&self) -> impl fmt::Display {
|
||||
fn print_entries(e: &FxIndexSet<ItemEntry>, kind: ItemSection) -> impl fmt::Display {
|
||||
fmt::from_fn(move |f| {
|
||||
if e.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
f.push_str("</ul>");
|
||||
}
|
||||
let mut e: Vec<&ItemEntry> = e.iter().collect();
|
||||
e.sort();
|
||||
write!(
|
||||
f,
|
||||
"<h3 id=\"{id}\">{title}</h3><ul class=\"all-items\">",
|
||||
id = kind.id(),
|
||||
title = kind.name(),
|
||||
)?;
|
||||
|
||||
for s in e.iter() {
|
||||
write!(f, "<li>{}</li>", s.print())?;
|
||||
}
|
||||
|
||||
f.write_str("</ul>")
|
||||
})
|
||||
}
|
||||
|
||||
f.push_str("<h1>List of all items</h1>");
|
||||
// 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("<h1>List of all items</h1>")?;
|
||||
// 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 = <Vec<&str>>::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("</details>");
|
||||
write_str(
|
||||
&mut tmp_buf,
|
||||
format_args!(
|
||||
"<details class=\"toggle big-toggle\" open><summary>{}</summary>",
|
||||
write_impl_section_heading(
|
||||
&format!(
|
||||
"<span>Methods from {trait_}<Target = {type_}></span>",
|
||||
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,
|
||||
"<details class=\"toggle big-toggle\" open><summary>{}</summary>",
|
||||
write_impl_section_heading(
|
||||
fmt::from_fn(|f| write!(
|
||||
f,
|
||||
"<span>Methods from {trait_}<Target = {type_}></span>",
|
||||
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}<div id=\"{id}\"{class_html}>{impls_buf}</div>").unwrap();
|
||||
for tag in close_tags.into_iter().rev() {
|
||||
w.write_str(tag).unwrap();
|
||||
}
|
||||
write!(
|
||||
w,
|
||||
"{section_heading}<div id=\"{id}\"{class_html}>{impls_buf}</div>{}",
|
||||
matches!(what, AssocItemRender::DerefFor { .. })
|
||||
.then_some("</details>")
|
||||
.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!("<details class=\"toggle{method_toggle_class}\" open><summary>"),
|
||||
);
|
||||
write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>")?;
|
||||
}
|
||||
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!(
|
||||
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
|
||||
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
|
||||
{}",
|
||||
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!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
|
||||
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
|
||||
}
|
||||
write_str(
|
||||
write!(
|
||||
w,
|
||||
format_args!(
|
||||
"<h4 class=\"code-header\">{}</h4></section>",
|
||||
render_assoc_item(
|
||||
item,
|
||||
link.anchor(source_id.as_ref().unwrap_or(&id)),
|
||||
ItemType::Impl,
|
||||
cx,
|
||||
render_mode,
|
||||
),
|
||||
"<h4 class=\"code-header\">{}</h4></section>",
|
||||
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!(
|
||||
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
|
||||
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
|
||||
{}",
|
||||
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!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
|
||||
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
|
||||
}
|
||||
write_str(
|
||||
write!(
|
||||
w,
|
||||
format_args!(
|
||||
"<h4 class=\"code-header\">{}</h4></section>",
|
||||
assoc_const(
|
||||
item,
|
||||
generics,
|
||||
ty,
|
||||
AssocConstValue::None,
|
||||
link.anchor(if trait_.is_some() { &source_id } else { &id }),
|
||||
0,
|
||||
cx,
|
||||
)
|
||||
"<h4 class=\"code-header\">{}</h4></section>",
|
||||
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!(
|
||||
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
|
||||
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
|
||||
{}",
|
||||
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!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
|
||||
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
|
||||
}
|
||||
write_str(
|
||||
write!(
|
||||
w,
|
||||
format_args!(
|
||||
"<h4 class=\"code-header\">{}</h4></section>",
|
||||
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,
|
||||
)
|
||||
"<h4 class=\"code-header\">{}</h4></section>",
|
||||
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!(
|
||||
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
|
||||
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
|
||||
{}",
|
||||
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!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
|
||||
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
|
||||
}
|
||||
write_str(
|
||||
write!(
|
||||
w,
|
||||
format_args!(
|
||||
"<h4 class=\"code-header\">{}</h4></section>",
|
||||
assoc_type(
|
||||
item,
|
||||
generics,
|
||||
bounds,
|
||||
None,
|
||||
link.anchor(if trait_.is_some() { &source_id } else { &id }),
|
||||
0,
|
||||
cx,
|
||||
)
|
||||
"<h4 class=\"code-header\">{}</h4></section>",
|
||||
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!(
|
||||
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
|
||||
"<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">\
|
||||
{}",
|
||||
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!("<a href=\"#{id}\" class=\"anchor\">§</a>"));
|
||||
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>")?;
|
||||
}
|
||||
write_str(
|
||||
write!(
|
||||
w,
|
||||
format_args!(
|
||||
"<h4 class=\"code-header\">{}</h4></section>",
|
||||
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,
|
||||
)
|
||||
"<h4 class=\"code-header\">{}</h4></section>",
|
||||
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("</summary>");
|
||||
w.push_str(&doc_buffer);
|
||||
w.push_str("</details>");
|
||||
write!(w, "</summary>{doc_buffer}</details>")?;
|
||||
}
|
||||
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 {
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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: Smartass+?Sized>(A::Data);
|
||||
|
||||
pub trait Smartass {
|
||||
type Data;
|
||||
type Data2: CoerceUnsized<*const [u8]>;
|
||||
}
|
||||
|
||||
pub trait MaybeObjectSafe {}
|
||||
|
||||
impl MaybeObjectSafe for () {}
|
||||
|
||||
impl<T> Smartass for T {
|
||||
type Data = <Self as Smartass>::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<U: Smartass+?Sized, T: Smartass+?Sized> CoerceUnsized<SmartassPtr<T>> for SmartassPtr<U>
|
||||
where <U as Smartass>::Data: std::ops::CoerceUnsized<<T as Smartass>::Data>
|
||||
{}
|
||||
|
||||
pub fn conv(s: SmartassPtr<()>) -> SmartassPtr<dyn MaybeObjectSafe> {
|
||||
s // This shouldn't coerce
|
||||
}
|
32
tests/ui/coercion/codegen-smart-pointer-with-alias.rs
Normal file
32
tests/ui/coercion/codegen-smart-pointer-with-alias.rs
Normal file
@ -0,0 +1,32 @@
|
||||
//@ build-pass
|
||||
|
||||
// Regression test for <https://github.com/rust-lang/rust/issues/139812>.
|
||||
|
||||
// Make sure that the unsize coercion we collect in mono for `Signal<i32> -> Signal<dyn Any>`
|
||||
// doesn't choke on the fact that the inner unsized field of `Signal<T>` 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<T: ?Sized> Mirror for T {
|
||||
type Assoc = T;
|
||||
}
|
||||
|
||||
trait Any {}
|
||||
impl<T> 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<T> -> Signal<U>`.
|
||||
impl<'a> CoerceUnsized<Signal<'a, dyn Any>> for Signal<'a, i32> {}
|
||||
|
||||
fn main() {
|
||||
Signal(&1i32) as Signal<dyn Any>;
|
||||
}
|
Loading…
Reference in New Issue
Block a user