diff --git a/Cargo.lock b/Cargo.lock
index 12be36ef86126..11ab3c9e66190 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2900,7 +2900,6 @@ dependencies = [
"anyhow",
"clap 4.2.1",
"fs-err",
- "rustc-hash",
"rustdoc-json-types",
"serde",
"serde_json",
@@ -5543,7 +5542,6 @@ dependencies = [
name = "rustdoc-json-types"
version = "0.1.0"
dependencies = [
- "rustc-hash",
"serde",
"serde_json",
]
diff --git a/RELEASES.md b/RELEASES.md
index b923f87abfd47..b89178a6f68fe 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,3 +1,105 @@
+Version 1.69.0 (2023-04-20)
+==========================
+
+
+
+Language
+--------
+
+- [Deriving built-in traits on packed structs works with `Copy` fields.](https://github.com/rust-lang/rust/pull/104429/)
+- [Stabilize the `cmpxchg16b` target feature on x86 and x86_64.](https://github.com/rust-lang/rust/pull/106774/)
+- [Improve analysis of trait bounds for associated types.](https://github.com/rust-lang/rust/pull/103695/)
+- [Allow associated types to be used as union fields.](https://github.com/rust-lang/rust/pull/106938/)
+- [Allow `Self: Autotrait` bounds on dyn-safe trait methods.](https://github.com/rust-lang/rust/pull/107082/)
+- [Treat `str` as containing `[u8]` for auto trait purposes.](https://github.com/rust-lang/rust/pull/107941/)
+
+
+
+Compiler
+--------
+
+- [Upgrade `*-pc-windows-gnu` on CI to mingw-w64 v10 and GCC 12.2.](https://github.com/rust-lang/rust/pull/100178/)
+- [Rework min_choice algorithm of member constraints.](https://github.com/rust-lang/rust/pull/105300/)
+- [Support `true` and `false` as boolean flags in compiler arguments.](https://github.com/rust-lang/rust/pull/107043/)
+- [Default `repr(C)` enums to `c_int` size.](https://github.com/rust-lang/rust/pull/107592/)
+
+
+
+Libraries
+---------
+
+- [Implement the unstable `DispatchFromDyn` for cell types, allowing downstream experimentation with custom method receivers.](https://github.com/rust-lang/rust/pull/97373/)
+- [Document that `fmt::Arguments::as_str()` may return `Some(_)` in more cases after optimization, subject to change.](https://github.com/rust-lang/rust/pull/106823/)
+- [Implement `AsFd` and `AsRawFd` for `Rc`.](https://github.com/rust-lang/rust/pull/107317/)
+
+
+
+Stabilized APIs
+---------------
+
+- [`CStr::from_bytes_until_nul`](https://doc.rust-lang.org/stable/core/ffi/struct.CStr.html#method.from_bytes_until_nul)
+- [`core::ffi::FromBytesUntilNulError`](https://doc.rust-lang.org/stable/core/ffi/struct.FromBytesUntilNulError.html)
+
+These APIs are now stable in const contexts:
+
+- [`SocketAddr::new`](https://doc.rust-lang.org/stable/std/net/enum.SocketAddr.html#method.new)
+- [`SocketAddr::ip`](https://doc.rust-lang.org/stable/std/net/enum.SocketAddr.html#method.ip)
+- [`SocketAddr::port`](https://doc.rust-lang.org/stable/std/net/enum.SocketAddr.html#method.port)
+- [`SocketAddr::is_ipv4`](https://doc.rust-lang.org/stable/std/net/enum.SocketAddr.html#method.is_ipv4)
+- [`SocketAddr::is_ipv6`](https://doc.rust-lang.org/stable/std/net/enum.SocketAddr.html#method.is_ipv6)
+- [`SocketAddrV4::new`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV4.html#method.new)
+- [`SocketAddrV4::ip`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV4.html#method.ip)
+- [`SocketAddrV4::port`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV4.html#method.port)
+- [`SocketAddrV6::new`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV6.html#method.new)
+- [`SocketAddrV6::ip`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV6.html#method.ip)
+- [`SocketAddrV6::port`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV6.html#method.port)
+- [`SocketAddrV6::flowinfo`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV6.html#method.flowinfo)
+- [`SocketAddrV6::scope_id`](https://doc.rust-lang.org/stable/std/net/struct.SocketAddrV6.html#method.scope_id)
+
+
+
+Cargo
+-----
+
+- [Cargo now suggests `cargo fix` or `cargo clippy --fix` when compilation warnings are auto-fixable.](https://github.com/rust-lang/cargo/pull/11558/)
+- [Cargo now suggests `cargo add` if you try to install a library crate.](https://github.com/rust-lang/cargo/pull/11410/)
+- [Cargo now sets the `CARGO_BIN_NAME` environment variable also for binary examples.](https://github.com/rust-lang/cargo/pull/11705/)
+
+
+
+Rustdoc
+-----
+
+- [Vertically compact trait bound formatting.](https://github.com/rust-lang/rust/pull/102842/)
+- [Only include stable lints in `rustdoc::all` group.](https://github.com/rust-lang/rust/pull/106316/)
+- [Compute maximum Levenshtein distance based on the query.](https://github.com/rust-lang/rust/pull/107141/)
+- [Remove inconsistently-present sidebar tooltips.](https://github.com/rust-lang/rust/pull/107490/)
+- [Search by macro when query ends with `!`.](https://github.com/rust-lang/rust/pull/108143/)
+
+
+
+Compatibility Notes
+-------------------
+
+- [The `rust-analysis` component from `rustup` now only contains a warning placeholder.](https://github.com/rust-lang/rust/pull/101841/) This was primarily intended for RLS, and the corresponding `-Zsave-analysis` flag has been removed from the compiler as well.
+- [Unaligned references to packed fields are now a hard error.](https://github.com/rust-lang/rust/pull/102513/) This has been a warning since 1.53, and denied by default with a future-compatibility warning since 1.62.
+- [Update the minimum external LLVM to 14.](https://github.com/rust-lang/rust/pull/107573/)
+- [Cargo now emits errors on invalid characters in a registry token.](https://github.com/rust-lang/cargo/pull/11600/)
+- [When `default-features` is set to false of a workspace dependency, and an inherited dependency of a member has `default-features = true`, Cargo will enable default features of that dependency.](https://github.com/rust-lang/cargo/pull/11409/)
+- [Cargo denies `CARGO_HOME` in the `[env]` configuration table. Cargo itself doesn't pick up this value, but recursive calls to cargo would, which was not intended.](https://github.com/rust-lang/cargo/pull/11644/)
+- [Debuginfo for build dependencies is now off if not explicitly set. This is expected to improve the overall build time.](https://github.com/rust-lang/cargo/pull/11252/)
+
+
+
+Internal Changes
+----------------
+
+These changes do not affect any public interfaces of Rust, but they represent
+significant improvements to the performance or internals of rustc and related
+tools.
+
+- [Move `format_args!()` into AST (and expand it during AST lowering)](https://github.com/rust-lang/rust/pull/106745/)
+
Version 1.68.2 (2023-03-28)
===========================
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index cff3d277a78fb..e01a57ea4fee8 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -55,7 +55,18 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
) -> Vec> {
let ty = self.resolve_vars_if_possible(ty);
let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);
- assert!(!ty.needs_infer());
+
+ // We do not expect existential variables in implied bounds.
+ // We may however encounter unconstrained lifetime variables in invalid
+ // code. See #110161 for context.
+ assert!(!ty.has_non_region_infer());
+ if ty.needs_infer() {
+ self.tcx.sess.delay_span_bug(
+ self.tcx.def_span(body_id),
+ "skipped implied_outlives_bounds due to unconstrained lifetimes",
+ );
+ return vec![];
+ }
let span = self.tcx.def_span(body_id);
let result = param_env
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index a67df7ed557a1..30ec73cabf849 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -170,7 +170,7 @@ pub fn forget_unsized(t: T) {
///
/// The following table gives the size for primitives.
///
-/// Type | size_of::\()
+/// Type | `size_of::()`
/// ---- | ---------------
/// () | 0
/// bool | 1
@@ -190,8 +190,8 @@ pub fn forget_unsized(t: T) {
///
/// Furthermore, `usize` and `isize` have the same size.
///
-/// The types `*const T`, `&T`, `Box`, `Option<&T>`, and `Option>` all have
-/// the same size. If `T` is Sized, all of those types have the same size as `usize`.
+/// The types [`*const T`], `&T`, [`Box`], [`Option<&T>`], and `Option>` all have
+/// the same size. If `T` is `Sized`, all of those types have the same size as `usize`.
///
/// The mutability of a pointer does not change its size. As such, `&T` and `&mut T`
/// have the same size. Likewise for `*const T` and `*mut T`.
@@ -203,7 +203,7 @@ pub fn forget_unsized(t: T) {
///
/// ## Size of Structs
///
-/// For `structs`, the size is determined by the following algorithm.
+/// For `struct`s, the size is determined by the following algorithm.
///
/// For each field in the struct ordered by declaration order:
///
@@ -299,6 +299,10 @@ pub fn forget_unsized(t: T) {
/// ```
///
/// [alignment]: align_of
+/// [`*const T`]: primitive@pointer
+/// [`Box`]: ../../std/boxed/struct.Box.html
+/// [`Option<&T>`]: crate::option::Option
+///
#[inline(always)]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
@@ -311,7 +315,7 @@ pub const fn size_of() -> usize {
/// Returns the size of the pointed-to value in bytes.
///
-/// This is usually the same as `size_of::()`. However, when `T` *has* no
+/// This is usually the same as [`size_of::()`]. However, when `T` *has* no
/// statically-known size, e.g., a slice [`[T]`][slice] or a [trait object],
/// then `size_of_val` can be used to get the dynamically-known size.
///
@@ -328,6 +332,8 @@ pub const fn size_of() -> usize {
/// let y: &[u8] = &x;
/// assert_eq!(13, mem::size_of_val(y));
/// ```
+///
+/// [`size_of::()`]: size_of
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
@@ -340,7 +346,7 @@ pub const fn size_of_val(val: &T) -> usize {
/// Returns the size of the pointed-to value in bytes.
///
-/// This is usually the same as `size_of::()`. However, when `T` *has* no
+/// This is usually the same as [`size_of::()`]. However, when `T` *has* no
/// statically-known size, e.g., a slice [`[T]`][slice] or a [trait object],
/// then `size_of_val_raw` can be used to get the dynamically-known size.
///
@@ -363,6 +369,7 @@ pub const fn size_of_val(val: &T) -> usize {
/// [`size_of_val`] on a reference to a type with an extern type tail.
/// - otherwise, it is conservatively not allowed to call this function.
///
+/// [`size_of::()`]: size_of
/// [trait object]: ../../book/ch17-02-trait-objects.html
/// [extern type]: ../../unstable-book/language-features/extern-types.html
///
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index eeda6d7c121f7..7b9eaceb00f6a 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -69,13 +69,9 @@ version = "0.46.0"
features = [
"Win32_Foundation",
"Win32_Security",
- "Win32_Storage_FileSystem",
"Win32_System_Diagnostics_Debug",
- "Win32_System_IO",
- "Win32_System_Ioctl",
"Win32_System_JobObjects",
"Win32_System_ProcessStatus",
- "Win32_System_SystemServices",
"Win32_System_Threading",
"Win32_System_Time",
]
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index cc3b3bc25f3d5..ca6dcaf495743 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -1009,7 +1009,9 @@ impl Config {
});
config.initial_cargo = build
.cargo
- .map(PathBuf::from)
+ .map(|cargo| {
+ t!(PathBuf::from(cargo).canonicalize(), "`initial_cargo` not found on disk")
+ })
.unwrap_or_else(|| config.out.join(config.build.triple).join("stage0/bin/cargo"));
// NOTE: it's important this comes *after* we set `initial_rustc` just above.
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 29912b95703b2..2ffb1519db612 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -13,7 +13,7 @@ itertools = "0.10.1"
minifier = "0.2.2"
once_cell = "1.10.0"
regex = "1"
-rustdoc-json-types = { path = "../rustdoc-json-types" }
+rustdoc-json-types = { path = "../rustdoc-json-types", features = ["rustc_hash"] }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
smallvec = "1.8.1"
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 6d2ce9e2833f4..7872d58155f0e 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -460,7 +460,8 @@ impl Item {
.iter()
.filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| {
debug!(?id);
- if let Ok((mut href, ..)) = href(*id, cx) {
+ if let Ok((href, ..)) = href(*id, cx) {
+ let mut href = href.render_string();
debug!(?href);
if let Some(ref fragment) = *fragment {
fragment.render(&mut href, cx.tcx())
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 1b445b8981e1a..21188a05e12ce 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -10,7 +10,7 @@
use std::borrow::Cow;
use std::cell::Cell;
use std::fmt::{self, Write};
-use std::iter::{self, once};
+use std::iter::once;
use rustc_ast as ast;
use rustc_attr::{ConstStability, StabilityLevel};
@@ -23,7 +23,7 @@ use rustc_metadata::creader::{CStore, LoadedMacro};
use rustc_middle::ty;
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::kw;
-use rustc_span::{sym, Symbol};
+use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;
use itertools::Itertools;
@@ -38,7 +38,6 @@ use crate::html::render::Context;
use crate::passes::collect_intra_doc_links::UrlFragment;
use super::url_parts_builder::estimate_item_path_byte_length;
-use super::url_parts_builder::UrlPartsBuilder;
pub(crate) trait Print {
fn print(self, buffer: &mut Buffer);
@@ -566,11 +565,11 @@ pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String {
/// This function is to get the external macro path because they are not in the cache used in
/// `href_with_root_path`.
-fn generate_macro_def_id_path(
+fn generate_macro_def_id_path<'a>(
def_id: DefId,
- cx: &Context<'_>,
- root_path: Option<&str>,
-) -> Result<(String, ItemType, Vec), HrefError> {
+ cx: &'a Context<'_>,
+ root_path: Option<&'a str>,
+) -> Result<(Href<'a>, Vec), HrefError> {
let tcx = cx.shared.tcx;
let crate_name = tcx.crate_name(def_id.krate);
let cache = cx.cache();
@@ -616,37 +615,46 @@ fn generate_macro_def_id_path(
return Err(HrefError::NotInExternalCache);
}
- if let Some(last) = path.last_mut() {
- *last = Symbol::intern(&format!("macro.{}.html", last.as_str()));
- }
+ let filename_prefix = "macro";
+ let filename_base = path.pop().unwrap().to_string().into();
- let url = match cache.extern_locations[&def_id.krate] {
- ExternalLocation::Remote(ref s) => {
+ let href = match cache.extern_locations[&def_id.krate] {
+ ExternalLocation::Remote(ref remote) => {
// `ExternalLocation::Remote` always end with a `/`.
- format!("{}{}", s, path.iter().map(|p| p.as_str()).join("/"))
+ Href {
+ root: remote.trim_end_matches('/'),
+ parent_directories: ParentDirectories(0),
+ path_components: path.clone().into(),
+ filename_prefix,
+ filename_base,
+ fragment: "",
+ }
}
ExternalLocation::Local => {
- // `root_path` always end with a `/`.
- format!(
- "{}{}/{}",
- root_path.unwrap_or(""),
- crate_name,
- path.iter().map(|p| p.as_str()).join("/")
- )
+ let mut path = path.clone();
+ path.insert(0, crate_name);
+ Href {
+ root: root_path.unwrap_or_default().trim_end_matches('/'),
+ parent_directories: ParentDirectories(0),
+ path_components: path.into(),
+ filename_prefix,
+ filename_base,
+ fragment: "",
+ }
}
ExternalLocation::Unknown => {
debug!("crate {} not in cache when linkifying macros", crate_name);
return Err(HrefError::NotInExternalCache);
}
};
- Ok((url, ItemType::Macro, fqp))
+ Ok((href, fqp))
}
-pub(crate) fn href_with_root_path(
+pub(crate) fn href_with_root_path<'a, 'tcx: 'a>(
did: DefId,
- cx: &Context<'_>,
- root_path: Option<&str>,
-) -> Result<(String, ItemType, Vec), HrefError> {
+ cx: &'a Context<'tcx>,
+ root_path: Option<&'a str>,
+) -> Result<(Href<'a>, Vec), HrefError> {
let tcx = cx.tcx();
let def_kind = tcx.def_kind(did);
let did = match def_kind {
@@ -670,33 +678,27 @@ pub(crate) fn href_with_root_path(
return Err(HrefError::Private);
}
- let mut is_remote = false;
- let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
- Some(&(ref fqp, shortty)) => (fqp, shortty, {
+ let (fqp, shortty, extern_root, parent_levels, path_components) = match cache.paths.get(&did) {
+ Some(&(ref fqp, shortty)) => {
let module_fqp = to_module_fqp(shortty, fqp.as_slice());
debug!(?fqp, ?shortty, ?module_fqp);
- href_relative_parts(module_fqp, relative_to).collect()
- }),
+ let (parent_levels, path_components) = href_relative_parts(module_fqp, relative_to);
+ (fqp, shortty, "", parent_levels, path_components)
+ }
None => {
if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) {
let module_fqp = to_module_fqp(shortty, fqp);
- (
- fqp,
- shortty,
- match cache.extern_locations[&did.krate] {
- ExternalLocation::Remote(ref s) => {
- is_remote = true;
- let s = s.trim_end_matches('/');
- let mut builder = UrlPartsBuilder::singleton(s);
- builder.extend(module_fqp.iter().copied());
- builder
- }
- ExternalLocation::Local => {
- href_relative_parts(module_fqp, relative_to).collect()
- }
- ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
- },
- )
+ match cache.extern_locations[&did.krate] {
+ ExternalLocation::Remote(ref root) => {
+ (fqp, shortty, root.as_str(), 0, module_fqp)
+ }
+ ExternalLocation::Local => {
+ let (parent_levels, path_components) =
+ href_relative_parts(module_fqp, relative_to);
+ (fqp, shortty, "", parent_levels, path_components)
+ }
+ ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
+ }
} else if matches!(def_kind, DefKind::Macro(_)) {
return generate_macro_def_id_path(did, cx, root_path);
} else {
@@ -704,28 +706,31 @@ pub(crate) fn href_with_root_path(
}
}
};
- if !is_remote && let Some(root_path) = root_path {
- let root = root_path.trim_end_matches('/');
- url_parts.push_front(root);
- }
- debug!(?url_parts);
- match shortty {
- ItemType::Module => {
- url_parts.push("index.html");
- }
+ let (filename_prefix, filename_base) = match shortty {
+ ItemType::Module => ("", "index"),
_ => {
let prefix = shortty.as_str();
- let last = fqp.last().unwrap();
- url_parts.push_fmt(format_args!("{}.{}.html", prefix, last));
+ let last = fqp.last().unwrap().as_str();
+ (prefix, last)
}
- }
- Ok((url_parts.finish(), shortty, fqp.to_vec()))
+ };
+ let root = if !extern_root.is_empty() { extern_root } else { root_path.unwrap_or_default() }
+ .trim_end_matches('/');
+ let href = Href {
+ root,
+ parent_directories: ParentDirectories(parent_levels),
+ path_components: path_components.into(),
+ filename_prefix,
+ filename_base: filename_base.into(),
+ fragment: "",
+ };
+ Ok((href, fqp.to_vec()))
}
-pub(crate) fn href(
+pub(crate) fn href<'a, 'tcx: 'a>(
did: DefId,
- cx: &Context<'_>,
-) -> Result<(String, ItemType, Vec), HrefError> {
+ cx: &'a Context<'tcx>,
+) -> Result<(Href<'a>, Vec), HrefError> {
href_with_root_path(did, cx, None)
}
@@ -735,29 +740,25 @@ pub(crate) fn href(
pub(crate) fn href_relative_parts<'fqp>(
fqp: &'fqp [Symbol],
relative_to_fqp: &[Symbol],
-) -> Box + 'fqp> {
+) -> (usize, &'fqp [Symbol]) {
for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
// e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
if f != r {
let dissimilar_part_count = relative_to_fqp.len() - i;
let fqp_module = &fqp[i..fqp.len()];
- return Box::new(
- iter::repeat(sym::dotdot)
- .take(dissimilar_part_count)
- .chain(fqp_module.iter().copied()),
- );
+ return (dissimilar_part_count, fqp_module);
}
}
// e.g. linking to std::sync::atomic from std::sync
if relative_to_fqp.len() < fqp.len() {
- Box::new(fqp[relative_to_fqp.len()..fqp.len()].iter().copied())
+ (0, &fqp[relative_to_fqp.len()..fqp.len()])
// e.g. linking to std::sync from std::sync::atomic
} else if fqp.len() < relative_to_fqp.len() {
let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
- Box::new(iter::repeat(sym::dotdot).take(dissimilar_part_count))
+ (dissimilar_part_count, &fqp[0..0])
// linking to the same module
} else {
- Box::new(iter::empty())
+ (0, &[])
}
}
@@ -808,20 +809,21 @@ fn resolved_path<'cx>(
if w.alternate() {
write!(w, "{}{:#}", &last.name, last.args.print(cx))?;
} else {
- let path = if use_absolute {
- if let Ok((_, _, fqp)) = href(did, cx) {
- format!(
+ if use_absolute {
+ if let Ok((_, fqp)) = href(did, cx) {
+ write!(
+ w,
"{}::{}",
join_with_double_colon(&fqp[..fqp.len() - 1]),
- anchor(did, *fqp.last().unwrap(), cx)
- )
+ anchor(did, *fqp.last().unwrap(), cx),
+ )?;
} else {
- last.name.to_string()
+ write!(w, "{}", last.name)?;
}
} else {
- anchor(did, last.name, cx).to_string()
+ write!(w, "{}", anchor(did, last.name, cx))?;
};
- write!(w, "{}{}", path, last.args.print(cx))?;
+ write!(w, "{}", last.args.print(cx))?;
}
Ok(())
}
@@ -835,6 +837,83 @@ fn primitive_link(
primitive_link_fragment(f, prim, name, "", cx)
}
+// Implements Display, emitting "../" the specified number of times.
+struct ParentDirectories(usize);
+
+impl fmt::Display for ParentDirectories {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.0 {
+ 0 => Ok(()),
+ 1 => f.write_str("../"),
+ 2 => f.write_str("../../"),
+ 3 => f.write_str("../../../"),
+ 4 => f.write_str("../../../../"),
+ 5 => f.write_str("../../../../../"),
+ 6 => f.write_str("../../../../../../"),
+ n => (0..n).map(|_| f.write_str("../")).collect(),
+ }
+ }
+}
+
+// Implements Display
+struct PathComponents<'a>(Cow<'a, [Symbol]>);
+
+impl<'a> PathComponents<'a> {
+ fn empty() -> Self {
+ Self(Default::default())
+ }
+}
+
+impl<'a> fmt::Display for PathComponents<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ .iter()
+ .map(|sym| {
+ f.write_str(sym.as_str())?;
+ f.write_char('/')
+ })
+ .collect()
+ }
+}
+
+impl<'a> From> for PathComponents<'a> {
+ fn from(value: Vec) -> Self {
+ Self(std::borrow::Cow::Owned(value))
+ }
+}
+
+impl<'a> From<&'a [Symbol]> for PathComponents<'a> {
+ fn from(value: &'a [Symbol]) -> Self {
+ Self(std::borrow::Cow::Borrowed(value))
+ }
+}
+
+use askama::Template;
+#[derive(Template)]
+#[template(path = "href.html")]
+pub(crate) struct Href<'a> {
+ /// An optional absolute URL, starting with http://, https://, or file://
+ root: &'a str,
+ /// Zero or more instances of `../`
+ parent_directories: ParentDirectories,
+ /// Zero or more
+ path_components: PathComponents<'a>,
+ /// Optional: an item type prefix, like `struct`. If present will have a `.` added.
+ filename_prefix: &'a str,
+ /// Required: the name of the page being documented. Does not include `.html`.
+ filename_base: Cow<'a, str>,
+ /// Optional: A fragment identifier, including the initial `#`.
+ fragment: &'a str,
+}
+
+impl<'a> Href<'a> {
+ pub fn render_string(&self) -> String {
+ let mut s = String::with_capacity(64);
+ self.render_into(&mut s).unwrap();
+ s
+ }
+}
+
fn primitive_link_fragment(
f: &mut fmt::Formatter<'_>,
prim: clean::PrimitiveType,
@@ -843,57 +922,62 @@ fn primitive_link_fragment(
cx: &Context<'_>,
) -> fmt::Result {
let m = &cx.cache();
- let mut needs_termination = false;
- if !f.alternate() {
- match m.primitive_locations.get(&prim) {
- Some(&def_id) if def_id.is_local() => {
- let len = cx.current.len();
- let len = if len == 0 { 0 } else { len - 1 };
- write!(
- f,
- "",
- "../".repeat(len),
- prim.as_sym()
- )?;
- needs_termination = true;
- }
- Some(&def_id) => {
- let loc = match m.extern_locations[&def_id.krate] {
- ExternalLocation::Remote(ref s) => {
- let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
- let builder: UrlPartsBuilder =
- [s.as_str().trim_end_matches('/'), cname_sym.as_str()]
- .into_iter()
- .collect();
- Some(builder)
- }
- ExternalLocation::Local => {
- let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
- Some(if cx.current.first() == Some(&cname_sym) {
- iter::repeat(sym::dotdot).take(cx.current.len() - 1).collect()
- } else {
- iter::repeat(sym::dotdot)
- .take(cx.current.len())
- .chain(iter::once(cname_sym))
- .collect()
- })
+ if f.alternate() {
+ return write!(f, "{}", name);
+ }
+ let filename_prefix = "primitive";
+ let prim_sym = prim.as_sym();
+ let filename_base = prim_sym.as_str().into();
+ let cname_sym_array: [Symbol; 1];
+ let cname_path_components: PathComponents<'_>;
+ let href = match m.primitive_locations.get(&prim) {
+ Some(&def_id) if def_id.is_local() => {
+ let len = cx.current.len();
+ let len = if len == 0 { 0 } else { len - 1 };
+ Some(Href {
+ root: "",
+ parent_directories: ParentDirectories(len),
+ path_components: PathComponents::empty(),
+ filename_prefix,
+ filename_base,
+ fragment,
+ })
+ }
+ Some(&def_id) => {
+ cname_sym_array = [ExternalCrate { crate_num: def_id.krate }.name(cx.tcx())];
+ cname_path_components = cname_sym_array.as_slice().into();
+ let (root, parent_levels) = match m.extern_locations[&def_id.krate] {
+ ExternalLocation::Remote(ref s) => (s.as_str().trim_end_matches('/'), 0),
+ ExternalLocation::Local => {
+ if cx.current.first() == Some(&cname_sym_array[0]) {
+ ("", cx.current.len() - 1)
+ } else {
+ ("", cx.current.len())
}
- ExternalLocation::Unknown => None,
- };
- if let Some(mut loc) = loc {
- loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym()));
- write!(f, "", loc.finish())?;
- needs_termination = true;
}
+ ExternalLocation::Unknown => ("", 0),
+ };
+ if parent_levels > 0 || !root.is_empty() {
+ Some(Href {
+ root,
+ parent_directories: ParentDirectories(parent_levels),
+ path_components: cname_path_components,
+ filename_prefix,
+ filename_base,
+ fragment,
+ })
+ } else {
+ None
}
- None => {}
}
+ None => None,
+ };
+
+ if let Some(href) = href {
+ write!(f, "{name}")
+ } else {
+ write!(f, "{}", name)
}
- write!(f, "{}", name)?;
- if needs_termination {
- write!(f, "")?;
- }
- Ok(())
}
/// Helper to render type parameters
@@ -919,14 +1003,27 @@ fn tybounds<'a, 'tcx: 'a>(
})
}
-pub(crate) fn anchor<'a, 'cx: 'a>(
+pub(crate) fn anchor<'b, 'a: 'b, 'tcx: 'a>(
did: DefId,
text: Symbol,
- cx: &'cx Context<'_>,
-) -> impl fmt::Display + 'a {
+ cx: &'b Context<'tcx>,
+) -> impl fmt::Display + 'b + Captures<'tcx> {
let parts = href(did, cx);
+ let cache = cx.cache();
+ let tcx = cx.shared.tcx;
display_fn(move |f| {
- if let Ok((url, short_ty, fqp)) = parts {
+ let short_ty = match cache.paths.get(&did) {
+ Some(&(_, short_ty)) => short_ty,
+ None => match cache.external_paths.get(&did) {
+ Some(&(_, short_ty)) => short_ty,
+ None if matches!(tcx.def_kind(did), DefKind::Macro(_)) => ItemType::Macro,
+ None => {
+ write!(f, "{}", text)?;
+ return Ok(());
+ }
+ },
+ };
+ if let Ok((url, fqp)) = parts {
write!(
f,
r#"{}"#,
@@ -1138,7 +1235,7 @@ fn fmt_type<'cx>(
// the ugliness comes from inlining across crates where
// everything comes in as a fully resolved QPath (hard to
// look at).
- if !f.alternate() && let Ok((url, _, path)) = href(trait_.def_id(), cx) {
+ if !f.alternate() && let Ok((url, path)) = href(trait_.def_id(), cx) {
write!(
f,
"(
item_did: ItemId,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
- use std::fmt::Write as _;
-
let to_print: Cow<'static, str> = match visibility {
None => "".into(),
Some(ty::Visibility::Public) => "pub ".into(),
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index b61dd57145802..82678b1df9c6f 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -977,7 +977,7 @@ fn string_without_closing_tag(
LinkFromSrc::External(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok()
- .map(|(url, _, _)| url)
+ .map(|(url, _)| url.render_string())
}
LinkFromSrc::Primitive(prim) => format::href_with_root_path(
PrimitiveType::primitive_locations(context.tcx())[prim],
@@ -985,7 +985,7 @@ fn string_without_closing_tag(
Some(href_context.root_path),
)
.ok()
- .map(|(url, _, _)| url),
+ .map(|(url, _)| url.render_string()),
}
})
{
diff --git a/src/librustdoc/html/templates/href.html b/src/librustdoc/html/templates/href.html
new file mode 100644
index 0000000000000..ec60ac04466d1
--- /dev/null
+++ b/src/librustdoc/html/templates/href.html
@@ -0,0 +1,11 @@
+{%- if !root.is_empty() -%}
+{{root|safe}}/
+{%- endif -%}
+{{parent_directories|safe}}
+{{path_components|safe}}
+{%- if !filename_prefix.is_empty() -%}
+ {{filename_prefix|safe}}.
+{%- endif -%}
+{{filename_base|safe}}
+.html
+{{fragment|safe}}
\ No newline at end of file
diff --git a/src/librustdoc/html/tests.rs b/src/librustdoc/html/tests.rs
index 437d3995e29fc..c60d54d97dafe 100644
--- a/src/librustdoc/html/tests.rs
+++ b/src/librustdoc/html/tests.rs
@@ -1,50 +1,50 @@
use crate::html::format::href_relative_parts;
use rustc_span::{sym, Symbol};
-fn assert_relative_path(expected: &[Symbol], relative_to_fqp: &[Symbol], fqp: &[Symbol]) {
+fn assert_relative_path(expected: (usize, &[Symbol]), relative_to_fqp: &[Symbol], fqp: &[Symbol]) {
// No `create_default_session_globals_then` call is needed here because all
// the symbols used are static, and no `Symbol::intern` calls occur.
- assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp).collect::>());
+ assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp));
}
#[test]
fn href_relative_parts_basic() {
let relative_to_fqp = &[sym::std, sym::vec];
let fqp = &[sym::std, sym::iter];
- assert_relative_path(&[sym::dotdot, sym::iter], relative_to_fqp, fqp);
+ assert_relative_path((1, &[sym::iter]), relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_parent_module() {
let relative_to_fqp = &[sym::std, sym::vec];
let fqp = &[sym::std];
- assert_relative_path(&[sym::dotdot], relative_to_fqp, fqp);
+ assert_relative_path((1, &[]), relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_different_crate() {
let relative_to_fqp = &[sym::std, sym::vec];
let fqp = &[sym::core, sym::iter];
- assert_relative_path(&[sym::dotdot, sym::dotdot, sym::core, sym::iter], relative_to_fqp, fqp);
+ assert_relative_path((2, &[sym::core, sym::iter]), relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_same_module() {
let relative_to_fqp = &[sym::std, sym::vec];
let fqp = &[sym::std, sym::vec];
- assert_relative_path(&[], relative_to_fqp, fqp);
+ assert_relative_path((0, &[]), relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_child_module() {
let relative_to_fqp = &[sym::std];
let fqp = &[sym::std, sym::vec];
- assert_relative_path(&[sym::vec], relative_to_fqp, fqp);
+ assert_relative_path((0, &[sym::vec]), relative_to_fqp, fqp);
}
#[test]
fn href_relative_parts_root() {
let relative_to_fqp = &[];
let fqp = &[sym::std];
- assert_relative_path(&[sym::std], relative_to_fqp, fqp);
+ assert_relative_path((0, &[sym::std]), relative_to_fqp, fqp);
}
diff --git a/src/librustdoc/html/url_parts_builder.rs b/src/librustdoc/html/url_parts_builder.rs
index 1e6af6af63cc4..de329c6fddb00 100644
--- a/src/librustdoc/html/url_parts_builder.rs
+++ b/src/librustdoc/html/url_parts_builder.rs
@@ -24,29 +24,6 @@ impl UrlPartsBuilder {
Self { buf: String::with_capacity(count) }
}
- /// Create a buffer with one URL component.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```ignore (private-type)
- /// let builder = UrlPartsBuilder::singleton("core");
- /// assert_eq!(builder.finish(), "core");
- /// ```
- ///
- /// Adding more components afterward.
- ///
- /// ```ignore (private-type)
- /// let mut builder = UrlPartsBuilder::singleton("core");
- /// builder.push("str");
- /// builder.push_front("nightly");
- /// assert_eq!(builder.finish(), "nightly/core/str");
- /// ```
- pub(crate) fn singleton(part: &str) -> Self {
- Self { buf: part.to_owned() }
- }
-
/// Push a component onto the buffer.
///
/// # Examples
@@ -87,29 +64,6 @@ impl UrlPartsBuilder {
self.buf.write_fmt(args).unwrap()
}
- /// Push a component onto the front of the buffer.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```ignore (private-type)
- /// let mut builder = UrlPartsBuilder::new();
- /// builder.push("core");
- /// builder.push("str");
- /// builder.push_front("nightly");
- /// builder.push("struct.Bytes.html");
- /// assert_eq!(builder.finish(), "nightly/core/str/struct.Bytes.html");
- /// ```
- pub(crate) fn push_front(&mut self, part: &str) {
- let is_empty = self.buf.is_empty();
- self.buf.reserve(part.len() + if !is_empty { 1 } else { 0 });
- self.buf.insert_str(0, part);
- if !is_empty {
- self.buf.insert(part.len(), '/');
- }
- }
-
/// Get the final `String` buffer.
pub(crate) fn finish(self) -> String {
self.buf
diff --git a/src/librustdoc/html/url_parts_builder/tests.rs b/src/librustdoc/html/url_parts_builder/tests.rs
index 636e1ab55279f..aaf75a7c2d5d6 100644
--- a/src/librustdoc/html/url_parts_builder/tests.rs
+++ b/src/librustdoc/html/url_parts_builder/tests.rs
@@ -9,11 +9,6 @@ fn empty() {
t(UrlPartsBuilder::new(), "");
}
-#[test]
-fn singleton() {
- t(UrlPartsBuilder::singleton("index.html"), "index.html");
-}
-
#[test]
fn push_several() {
let mut builder = UrlPartsBuilder::new();
@@ -23,29 +18,12 @@ fn push_several() {
t(builder, "core/str/struct.Bytes.html");
}
-#[test]
-fn push_front_empty() {
- let mut builder = UrlPartsBuilder::new();
- builder.push_front("page.html");
- t(builder, "page.html");
-}
-
-#[test]
-fn push_front_non_empty() {
- let mut builder = UrlPartsBuilder::new();
- builder.push("core");
- builder.push("str");
- builder.push("struct.Bytes.html");
- builder.push_front("nightly");
- t(builder, "nightly/core/str/struct.Bytes.html");
-}
-
#[test]
fn push_fmt() {
let mut builder = UrlPartsBuilder::new();
+ builder.push("nightly");
builder.push_fmt(format_args!("{}", "core"));
builder.push("str");
- builder.push_front("nightly");
builder.push_fmt(format_args!("{}.{}.html", "struct", "Bytes"));
t(builder, "nightly/core/str/struct.Bytes.html");
}
@@ -58,7 +36,8 @@ fn collect() {
#[test]
fn extend() {
- let mut builder = UrlPartsBuilder::singleton("core");
+ let mut builder = UrlPartsBuilder::new();
+ builder.push("core");
builder.extend(["str", "struct.Bytes.html"]);
t(builder, "core/str/struct.Bytes.html");
}
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index d6da6e0993894..81f974fb4eaf8 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -228,14 +228,11 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
let index = (*self.index).clone().into_inner();
debug!("Constructing Output");
- // This needs to be the default HashMap for compatibility with the public interface for
- // rustdoc-json-types
- #[allow(rustc::default_hash_types)]
let output = types::Crate {
root: types::Id(format!("0:0:{}", e.name(self.tcx).as_u32())),
crate_version: self.cache.crate_version.clone(),
includes_private: self.cache.document_private,
- index: index.into_iter().collect(),
+ index,
paths: self
.cache
.paths
diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml
index d63caa7ad7010..fe61741f1da73 100644
--- a/src/rustdoc-json-types/Cargo.toml
+++ b/src/rustdoc-json-types/Cargo.toml
@@ -8,7 +8,9 @@ path = "lib.rs"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
-rustc-hash = "1.1.0"
[dev-dependencies]
serde_json = "1.0"
+
+[features]
+rustc_hash = []
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 3cf8ceed62036..e086f280d6015 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -3,7 +3,14 @@
//! These types are the public API exposed through the `--output-format json` flag. The [`Crate`]
//! struct is the root of the JSON blob and all other items are contained within.
-use rustc_hash::FxHashMap;
+#![cfg_attr(feature = "rustc_hash", feature(rustc_private))]
+#[cfg(feature = "rustc_hash")]
+extern crate rustc_hash;
+#[cfg(feature = "rustc_hash")]
+use rustc_hash::FxHashMap as HashMap;
+#[cfg(not(feature = "rustc_hash"))]
+use std::collections::HashMap;
+
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
@@ -23,11 +30,11 @@ pub struct Crate {
pub includes_private: bool,
/// A collection of all items in the local crate as well as some external traits and their
/// items that are referenced locally.
- pub index: FxHashMap,
+ pub index: HashMap,
/// Maps IDs to fully qualified paths and other info helpful for generating links.
- pub paths: FxHashMap,
+ pub paths: HashMap,
/// Maps `crate_id` of items to a crate name and html_root_url if it exists.
- pub external_crates: FxHashMap,
+ pub external_crates: HashMap,
/// A single version number to be used in the future when making backwards incompatible changes
/// to the JSON output.
pub format_version: u32,
@@ -79,7 +86,7 @@ pub struct Item {
/// Some("") if there is some documentation but it is empty (EG `#[doc = ""]`).
pub docs: Option,
/// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs
- pub links: FxHashMap,
+ pub links: HashMap,
/// Stringified versions of the attributes on this item (e.g. `"#[inline]"`)
pub attrs: Vec,
pub deprecation: Option,
diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml
index 1318a1f447620..8990310a4f474 100644
--- a/src/tools/jsondoclint/Cargo.toml
+++ b/src/tools/jsondoclint/Cargo.toml
@@ -9,7 +9,6 @@ edition = "2021"
anyhow = "1.0.62"
clap = { version = "4.0.15", features = ["derive"] }
fs-err = "2.8.1"
-rustc-hash = "1.1.0"
rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.85"
diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs
index 95a56a9dfac45..29563d7e0d250 100644
--- a/src/tools/jsondoclint/src/validator/tests.rs
+++ b/src/tools/jsondoclint/src/validator/tests.rs
@@ -1,5 +1,5 @@
-use rustc_hash::FxHashMap;
use rustdoc_json_types::{Crate, Item, ItemKind, ItemSummary, Visibility, FORMAT_VERSION};
+use std::collections::HashMap;
use crate::json_find::SelectorPart;
@@ -26,7 +26,7 @@ fn errors_on_missing_links() {
root: id("0"),
crate_version: None,
includes_private: false,
- index: FxHashMap::from_iter([(
+ index: HashMap::from_iter([(
id("0"),
Item {
name: Some("root".to_owned()),
@@ -35,7 +35,7 @@ fn errors_on_missing_links() {
span: None,
visibility: Visibility::Public,
docs: None,
- links: FxHashMap::from_iter([("Not Found".to_owned(), id("1"))]),
+ links: HashMap::from_iter([("Not Found".to_owned(), id("1"))]),
attrs: vec![],
deprecation: None,
inner: ItemEnum::Module(Module {
@@ -45,8 +45,8 @@ fn errors_on_missing_links() {
}),
},
)]),
- paths: FxHashMap::default(),
- external_crates: FxHashMap::default(),
+ paths: HashMap::default(),
+ external_crates: HashMap::default(),
format_version: rustdoc_json_types::FORMAT_VERSION,
};
@@ -72,7 +72,7 @@ fn errors_on_local_in_paths_and_not_index() {
root: id("0:0:1572"),
crate_version: None,
includes_private: false,
- index: FxHashMap::from_iter([
+ index: HashMap::from_iter([
(
id("0:0:1572"),
Item {
@@ -82,7 +82,7 @@ fn errors_on_local_in_paths_and_not_index() {
span: None,
visibility: Visibility::Public,
docs: None,
- links: FxHashMap::from_iter([(("prim@i32".to_owned(), id("0:1:1571")))]),
+ links: HashMap::from_iter([(("prim@i32".to_owned(), id("0:1:1571")))]),
attrs: Vec::new(),
deprecation: None,
inner: ItemEnum::Module(Module {
@@ -101,14 +101,14 @@ fn errors_on_local_in_paths_and_not_index() {
span: None,
visibility: Visibility::Public,
docs: None,
- links: FxHashMap::default(),
+ links: HashMap::default(),
attrs: Vec::new(),
deprecation: None,
inner: ItemEnum::Primitive(Primitive { name: "i32".to_owned(), impls: vec![] }),
},
),
]),
- paths: FxHashMap::from_iter([(
+ paths: HashMap::from_iter([(
id("0:1:1571"),
ItemSummary {
crate_id: 0,
@@ -116,7 +116,7 @@ fn errors_on_local_in_paths_and_not_index() {
kind: ItemKind::Primitive,
},
)]),
- external_crates: FxHashMap::default(),
+ external_crates: HashMap::default(),
format_version: rustdoc_json_types::FORMAT_VERSION,
};
@@ -136,7 +136,7 @@ fn checks_local_crate_id_is_correct() {
root: id("root"),
crate_version: None,
includes_private: false,
- index: FxHashMap::from_iter([(
+ index: HashMap::from_iter([(
id("root"),
Item {
id: id("root"),
@@ -145,7 +145,7 @@ fn checks_local_crate_id_is_correct() {
span: None,
visibility: Visibility::Public,
docs: None,
- links: FxHashMap::default(),
+ links: HashMap::default(),
attrs: Vec::new(),
deprecation: None,
inner: ItemEnum::Module(Module {
@@ -155,8 +155,8 @@ fn checks_local_crate_id_is_correct() {
}),
},
)]),
- paths: FxHashMap::default(),
- external_crates: FxHashMap::default(),
+ paths: HashMap::default(),
+ external_crates: HashMap::default(),
format_version: FORMAT_VERSION,
};
check(&krate, &[]);
diff --git a/src/version b/src/version
index 832e9afb6c139..df484cbb1d9f8 100644
--- a/src/version
+++ b/src/version
@@ -1 +1 @@
-1.70.0
+1.71.0
diff --git a/tests/ui/generics/issue-79605.rs b/tests/ui/generics/issue-79605.rs
new file mode 100644
index 0000000000000..6f4c31e57a374
--- /dev/null
+++ b/tests/ui/generics/issue-79605.rs
@@ -0,0 +1,6 @@
+struct X<'a, T>(&'a T);
+
+impl X<'_, _> {}
+//~^ ERROR the placeholder `_` is not allowed within types on item signatures for implementations
+
+fn main() {}
diff --git a/tests/ui/generics/issue-79605.stderr b/tests/ui/generics/issue-79605.stderr
new file mode 100644
index 0000000000000..c5584962dc9e2
--- /dev/null
+++ b/tests/ui/generics/issue-79605.stderr
@@ -0,0 +1,14 @@
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations
+ --> $DIR/issue-79605.rs:3:12
+ |
+LL | impl X<'_, _> {}
+ | ^ not allowed in type signatures
+ |
+help: use type parameters instead
+ |
+LL | impl X<'_, T> {}
+ | +++ ~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0121`.
diff --git a/tests/ui/implied-bounds/issue-110161.rs b/tests/ui/implied-bounds/issue-110161.rs
new file mode 100644
index 0000000000000..e52c8356b52b3
--- /dev/null
+++ b/tests/ui/implied-bounds/issue-110161.rs
@@ -0,0 +1,26 @@
+// ICE regression relating to unconstrained lifetimes in implied
+// bounds. See #110161.
+
+// compile-flags: --crate-type=lib
+
+trait LtTrait {
+ type Ty;
+}
+
+// erroneous `Ty` impl
+impl LtTrait for () {
+//~^ ERROR not all trait items implemented, missing: `Ty` [E0046]
+}
+
+// `'lt` is not constrained by the erroneous `Ty`
+impl<'lt, T> LtTrait for Box
+where
+ T: LtTrait,
+{
+ type Ty = &'lt ();
+}
+
+// unconstrained lifetime appears in implied bounds
+fn test(_: as LtTrait>::Ty) {}
+
+fn test2<'x>(_: &'x as LtTrait>::Ty) {}
diff --git a/tests/ui/implied-bounds/issue-110161.stderr b/tests/ui/implied-bounds/issue-110161.stderr
new file mode 100644
index 0000000000000..9e0188694ed9c
--- /dev/null
+++ b/tests/ui/implied-bounds/issue-110161.stderr
@@ -0,0 +1,12 @@
+error[E0046]: not all trait items implemented, missing: `Ty`
+ --> $DIR/issue-110161.rs:11:1
+ |
+LL | type Ty;
+ | ------- `Ty` from trait
+...
+LL | impl LtTrait for () {
+ | ^^^^^^^^^^^^^^^^^^^ missing `Ty` in implementation
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0046`.
diff --git a/triagebot.toml b/triagebot.toml
index 58f51959e1f81..917acce901bda 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -11,6 +11,7 @@ allow-unauthenticated = [
"S-*",
"T-*",
"WG-*",
+ "beta-nominated",
"const-hack",
"llvm-main",
"needs-fcp",