From 515395bce0e68f4edc6e2687d84c0c0461359c22 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 25 Jun 2026 03:25:08 +0330 Subject: [PATCH 01/19] Expose debug scope of statement and terminator in rustc_public --- compiler/rustc_public/src/mir/body.rs | 4 ++-- compiler/rustc_public/src/mir/visit.rs | 16 ++++++++-------- .../src/unstable/convert/stable/mir.rs | 4 ++-- .../ui-fulldeps/rustc_public/check_transform.rs | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index f9b5f9af951e5..79d11e54303f3 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -139,7 +139,7 @@ pub struct BasicBlock { #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct Terminator { pub kind: TerminatorKind, - pub span: Span, + pub source_info: SourceInfo, } impl Terminator { @@ -468,7 +468,7 @@ pub enum NonDivergingIntrinsic { #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct Statement { pub kind: StatementKind, - pub span: Span, + pub source_info: SourceInfo, } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index 5a3afc9937351..ae69f14faddd8 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -139,9 +139,9 @@ macro_rules! make_mir_visitor { fn super_basic_block(&mut self, bb: &$($mutability)? BasicBlock) { let BasicBlock { statements, terminator } = bb; for stmt in statements { - self.visit_statement(stmt, Location(stmt.span)); + self.visit_statement(stmt, Location(stmt.source_info.span)); } - self.visit_terminator(terminator, Location(terminator.span)); + self.visit_terminator(terminator, Location(terminator.source_info.span)); } fn super_local_decl(&mut self, local: Local, decl: &$($mutability)? LocalDecl) { @@ -159,8 +159,8 @@ macro_rules! make_mir_visitor { } fn super_statement(&mut self, stmt: &$($mutability)? Statement, location: Location) { - let Statement { kind, span } = stmt; - self.visit_span(span); + let Statement { kind, source_info } = stmt; + self.visit_span(&$($mutability)? source_info.span); match kind { StatementKind::Assign(place, rvalue) => { self.visit_place(place, PlaceContext::MUTATING, location); @@ -199,8 +199,8 @@ macro_rules! make_mir_visitor { } fn super_terminator(&mut self, term: &$($mutability)? Terminator, location: Location) { - let Terminator { kind, span } = term; - self.visit_span(span); + let Terminator { kind, source_info } = term; + self.visit_span(&$($mutability)? source_info.span); match kind { TerminatorKind::Goto { .. } | TerminatorKind::Resume @@ -540,7 +540,7 @@ impl Location { pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) -> Location { let bb = &body.blocks[*bb_idx]; let stmt = &bb.statements[stmt_idx]; - Location(stmt.span) + Location(stmt.source_info.span) } /// Location of the terminator for a given basic block. Assumes that `bb_idx` is valid for a given @@ -548,7 +548,7 @@ pub fn statement_location(body: &Body, bb_idx: &BasicBlockIdx, stmt_idx: usize) pub fn terminator_location(body: &Body, bb_idx: &BasicBlockIdx) -> Location { let bb = &body.blocks[*bb_idx]; let terminator = &bb.terminator; - Location(terminator.span) + Location(terminator.source_info.span) } /// Reference to a place used to represent a partial projection. diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index d126304af8cde..c98a87d67458f 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -74,7 +74,7 @@ impl<'tcx> Stable<'tcx> for mir::Statement<'tcx> { ) -> Self::T { Statement { kind: self.kind.stable(tables, cx), - span: self.source_info.span.stable(tables, cx), + source_info: self.source_info.stable(tables, cx), } } } @@ -695,7 +695,7 @@ impl<'tcx> Stable<'tcx> for mir::Terminator<'tcx> { use crate::mir::Terminator; Terminator { kind: self.kind.stable(tables, cx), - span: self.source_info.span.stable(tables, cx), + source_info: self.source_info.stable(tables, cx), } } } diff --git a/tests/ui-fulldeps/rustc_public/check_transform.rs b/tests/ui-fulldeps/rustc_public/check_transform.rs index 17b9612db3067..a87e45380074e 100644 --- a/tests/ui-fulldeps/rustc_public/check_transform.rs +++ b/tests/ui-fulldeps/rustc_public/check_transform.rs @@ -95,7 +95,7 @@ fn change_panic_msg(mut body: Body, new_msg: &str) -> Body { let new_const = MirConst::from_str(new_msg); args[0] = Operand::Constant(ConstOperand { const_: new_const, - span: bb.terminator.span, + span: bb.terminator.source_info.span, user_ty: None, }); } From e919f70ef3bd37aae3a4b84a7a8bcf99a81f7db4 Mon Sep 17 00:00:00 2001 From: Schneems Date: Sat, 27 Jun 2026 18:21:51 -0500 Subject: [PATCH 02/19] Bump POSIX spec links to the 2024 edition Comments and doc links across std, core, and a process test referenced the older issue 7 (2018 edition) of the POSIX spec, with one link still on issue 6. Point them all at the current issue 8 (2024 edition, IEEE Std 1003.1-2024) under onlinepubs/9799919799, and normalize the `9799919799.2024edition` URL variant to the canonical path. Links that carry a section anchor were verified against the 2024 edition, since section numbering shifts between editions: - Pathname Resolution moved from 4.13 (tag_04_13) to 4.16 (tag_04_16) - Field Splitting moved from tag_18_06_05 to tag_19_06_05 - LC_CTYPE (tag_07_03_01) was unchanged CI's linkchecker only validates intra-doc links, not external URLs, so each page and anchor was checked by hand. --- library/core/src/ascii/ascii_char.rs | 4 ++-- library/core/src/char/methods.rs | 4 ++-- library/core/src/num/mod.rs | 4 ++-- library/std/src/os/fd/owned.rs | 2 +- library/std/src/os/unix/process.rs | 2 +- library/std/src/path.rs | 2 +- library/std/src/sys/fs/unix.rs | 2 +- library/std/src/sys/pal/unix/conf.rs | 2 +- library/std/src/sys/pal/unix/sync/mutex.rs | 2 +- library/std/src/sys/path/unix.rs | 4 ++-- library/std/src/sys/process/unix/unix.rs | 2 +- library/std/src/sys/process/unix/unsupported/wait_status.rs | 2 +- library/std/src/sys/process/unix/vxworks.rs | 2 +- tests/ui/process/process-spawn-failure.rs | 2 +- 14 files changed, 18 insertions(+), 18 deletions(-) diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index abd80aef20bd3..de1adf9c9ec7c 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -1049,8 +1049,8 @@ impl AsciiChar { /// before using this function. /// /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace - /// [pct]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 - /// [bfs]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// [pct]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_06_05 /// /// # Examples /// diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 8517df4da2afc..d0114c30a6b3c 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -2354,8 +2354,8 @@ impl char { /// before using this function. /// /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace - /// [pct]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 - /// [bfs]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// [pct]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_06_05 /// /// # Examples /// diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 002c56083659c..59dfe6bd8d9b7 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1125,8 +1125,8 @@ impl u8 { /// before using this function. /// /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace - /// [pct]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01 - /// [bfs]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 + /// [pct]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap07.html#tag_07_03_01 + /// [bfs]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_06_05 /// /// # Examples /// diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 703113783f198..c226a0dc2403f 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -199,7 +199,7 @@ impl Drop for OwnedFd { unsafe { // Note that errors are ignored when closing a file descriptor. According to POSIX 2024, // we can and indeed should retry `close` on `EINTR` - // (https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/close.html), + // (https://pubs.opengroup.org/onlinepubs/9799919799/functions/close.html), // but it is not clear yet how well widely-used implementations are conforming with this // mandate since older versions of POSIX left the state of the FD after an `EINTR` // unspecified. Ignoring errors is "fine" because some of the major Unices (in diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 3b3a5794ac4f1..e3ded05b5dff0 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -100,7 +100,7 @@ pub impl(self) trait CommandExt { /// locations might not appear where intended. /// /// [POSIX fork() specification]: - /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html + /// https://pubs.opengroup.org/onlinepubs/9799919799/functions/fork.html /// [`std::env`]: mod@crate::env /// [`Error::new`]: ../../../io/struct.Error.html#method.new /// [`Error::other`]: ../../../io/struct.Error.html#method.other diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 2687a12919701..b3e2c2bd6c6a3 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -4137,7 +4137,7 @@ impl Error for NormalizeError {} /// Note that this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior -/// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 +/// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap04.html#tag_04_16 /// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew /// [cygwin-path]: https://cygwin.com/cygwin-api/func-cygwin-conv-path.html #[stable(feature = "absolute_path", since = "1.79.0")] diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 8fee56348c071..7283e8f710c55 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1926,7 +1926,7 @@ impl fmt::Debug for File { // Format in octal, followed by the mode format used in `ls -l`. // // References: -// https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html +// https://pubs.opengroup.org/onlinepubs/9799919799/utilities/ls.html // https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html // https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html // diff --git a/library/std/src/sys/pal/unix/conf.rs b/library/std/src/sys/pal/unix/conf.rs index 4c00379a88439..6d42e890fd45b 100644 --- a/library/std/src/sys/pal/unix/conf.rs +++ b/library/std/src/sys/pal/unix/conf.rs @@ -11,7 +11,7 @@ pub fn page_size() -> usize { /// `_CS_PATH` or `_CS_V[67]_ENV` in the future). /// /// [posix_confstr]: -/// https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html +/// https://pubs.opengroup.org/onlinepubs/9799919799/functions/confstr.html #[cfg(target_vendor = "apple")] pub fn confstr( key: crate::ffi::c_int, diff --git a/library/std/src/sys/pal/unix/sync/mutex.rs b/library/std/src/sys/pal/unix/sync/mutex.rs index 557e70af94ba7..145bfb1e22413 100644 --- a/library/std/src/sys/pal/unix/sync/mutex.rs +++ b/library/std/src/sys/pal/unix/sync/mutex.rs @@ -25,7 +25,7 @@ impl Mutex { // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you // try to re-lock it from the same thread when you already hold a lock - // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). + // (https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_mutex_init.html). // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same diff --git a/library/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs index b49c39a9253fa..5a8c0c77745e0 100644 --- a/library/std/src/sys/path/unix.rs +++ b/library/std/src/sys/path/unix.rs @@ -20,8 +20,8 @@ pub const HAS_PREFIXES: bool = false; pub(crate) fn absolute(path: &Path) -> io::Result { // This is mostly a wrapper around collecting `Path::components`, with // exceptions made where this conflicts with the POSIX specification. - // See 4.13 Pathname Resolution, IEEE Std 1003.1-2017 - // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 + // See 4.16 Pathname Resolution, IEEE Std 1003.1-2024 + // https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap04.html#tag_04_16 // Get the components, skipping the redundant leading "." component if it exists. let mut components = path.strip_prefix(".").unwrap_or(path).components(); diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 55906ed2854f2..dc11f44574a5f 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -1101,7 +1101,7 @@ impl ExitStatus { pub fn exit_ok(&self) -> Result<(), ExitStatusError> { // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is // true on all actual versions of Unix, is widely assumed, and is specified in SuS - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not + // https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html. If it is not // true for a platform pretending to be Unix, the tests (our doctests, and also // unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. match NonZero::try_from(self.0) { diff --git a/library/std/src/sys/process/unix/unsupported/wait_status.rs b/library/std/src/sys/process/unix/unsupported/wait_status.rs index f348d557e4b7e..ac54824875813 100644 --- a/library/std/src/sys/process/unix/unsupported/wait_status.rs +++ b/library/std/src/sys/process/unix/unsupported/wait_status.rs @@ -46,7 +46,7 @@ impl ExitStatus { pub fn exit_ok(&self) -> Result<(), ExitStatusError> { // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is // true on all actual versions of Unix, is widely assumed, and is specified in SuS - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not + // https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html. If it is not // true for a platform pretending to be Unix, the tests (our doctests, and also // unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. match NonZero::try_from(self.wait_status) { diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index c5acff2bdd3c5..e476f2850d901 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -211,7 +211,7 @@ impl ExitStatus { pub fn exit_ok(&self) -> Result<(), ExitStatusError> { // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is // true on all actual versions of Unix, is widely assumed, and is specified in SuS - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not + // https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html. If it is not // true for a platform pretending to be Unix, the tests (our doctests, and also // unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. match NonZero::try_from(self.0) { diff --git a/tests/ui/process/process-spawn-failure.rs b/tests/ui/process/process-spawn-failure.rs index ac2c34bc7836d..b3f0071670bc6 100644 --- a/tests/ui/process/process-spawn-failure.rs +++ b/tests/ui/process/process-spawn-failure.rs @@ -38,7 +38,7 @@ fn find_zombies() { extern crate libc; let my_pid = unsafe { libc::getpid() }; - // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html + // https://pubs.opengroup.org/onlinepubs/9799919799/utilities/ps.html let ps_cmd_output = Command::new("ps").args(&["-A", "-o", "pid,ppid,args"]).output().unwrap(); let ps_output = String::from_utf8_lossy(&ps_cmd_output.stdout); // On AIX, the PPID is not always present, such as when a process is blocked From 81a3c7598ca21e1c89344853f8af6bea4dbb28a8 Mon Sep 17 00:00:00 2001 From: b1yd <2156864690@qq.com> Date: Wed, 3 Jun 2026 16:48:00 +0800 Subject: [PATCH 03/19] fix --- compiler/rustc_parse/src/errors.rs | 30 +++++++++ .../rustc_parse/src/parser/diagnostics.rs | 64 ++++++++++++++++--- compiler/rustc_parse/src/parser/item.rs | 19 +++++- .../ui/parser/bare-type-in-impl-parameter.rs | 1 + .../parser/bare-type-in-impl-parameter.stderr | 18 ++++++ 5 files changed, 120 insertions(+), 12 deletions(-) create mode 100644 tests/ui/parser/bare-type-in-impl-parameter.rs create mode 100644 tests/ui/parser/bare-type-in-impl-parameter.stderr diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 0c850b69ca7f1..c0336cd57603c 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -4660,3 +4660,33 @@ pub(crate) struct UseRegularStructSuggestion { #[suggestion_part(code = "")] pub semicolon: Option, } +#[derive(Diagnostic)] +#[diag("expected type parameter, found path `{$path}`")] +pub(crate) struct FoundPathInGenerics { + #[primary_span] + pub span: Span, + pub path: String, +} +#[derive(Subdiagnostic)] +#[suggestion( + "you might have meant to bind a type parameter to a trait", + applicability = "maybe-incorrect", + code = "T: " +)] + +pub(crate) struct SuggestBindTypeParameter { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "alternatively, you might have meant to introduce type parameter", + applicability = "maybe-incorrect", + code = "{parameters}" +)] +pub(crate) struct SuggestIntroduceTypeParameter { + #[primary_span] + pub span: Span, + pub parameters: String, +} diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 7040fc60e4f1a..28e2841b9f1fe 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -6,7 +6,7 @@ use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, - Block, BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, + Block, BlockCheckMode, Expr, ExprKind, GenericArg, GenericArgs, Generics, Item, ItemKind, MgcaDisambiguation, Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind, }; use rustc_ast_pretty::pprust; @@ -31,14 +31,16 @@ use crate::errors::{ AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi, ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg, DocCommentDoesNotDocumentAnything, DocCommentOnParamType, DoubleColonInBound, - ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, GenericParamsWithoutAngleBrackets, - GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, HelpUseLatestEdition, - InInTypo, IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, - MisspelledKw, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, - SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, - SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, - TernaryOperatorSuggestion, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, - UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType, + ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, FoundPathInGenerics, + GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, + HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait, + IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, MisspelledKw, + PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, + StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, + SuggEscapeIdentifier, SuggRemoveComma, SuggestBindTypeParameter, SuggestIntroduceTypeParameter, + TernaryOperator, TernaryOperatorSuggestion, UnexpectedConstInGenericParam, + UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, + UseEqInstead, WrapType, }; use crate::exp; use crate::parser::FnContext; @@ -3164,4 +3166,48 @@ impl<'a> Parser<'a> { } Ok(()) } + pub(super) fn maybe_type_in_generic_parameter(&mut self, origin_error: Diag<'a>) -> Diag<'a> { + if !self.may_recover() { + return origin_error; + } + self.with_recovery(super::Recovery::Forbidden, |snapshot| { + snapshot.bump(); + let lo = snapshot.token.span.shrink_to_lo(); + + let ty = match snapshot.parse_ty() { + Ok(t) => t, + Err(err) => { + err.cancel(); + return origin_error; + } + }; + let TyKind::Path(_, path) = ty.kind else { + return origin_error; + }; + let Some(GenericArgs::AngleBracketed(AngleBracketedArgs { span: _, ref args })) = + path.segments[0].args + else { + return origin_error; + }; + + let path_span = path.span; + let mut new_error = snapshot.dcx().create_err(FoundPathInGenerics { + span: path_span, + path: snapshot.span_to_snippet(path_span).unwrap(), + }); + new_error.subdiagnostic(SuggestBindTypeParameter { span: lo }); + origin_error.cancel(); + + let params = args + .iter() + .map(|arg| snapshot.span_to_snippet(arg.span()).unwrap()) + .collect::>() + .join(", "); + new_error.subdiagnostic(SuggestIntroduceTypeParameter { + span: path_span, + parameters: params, + }); + new_error + }) + } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 16fed9c6c273b..627711cb2f822 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -662,11 +662,20 @@ impl<'a> Parser<'a> { let constness = self.parse_constness(Case::Sensitive); let safety = self.parse_safety(Case::Sensitive); self.expect_keyword(exp!(Impl))?; - + let mut generics_snapshot = None; // First, parse generic parameters if necessary. let mut generics = if self.choose_generics_over_qpath(0) { self.parse_generics()? } else { + // We might be mistakenly trying to use a generic type as a generic parameter. + // impl> Trait for Y { ... } + if self.look_ahead(0, |t| t == &token::Lt) + && self.look_ahead(1, |t| t.is_ident()) + && self.look_ahead(2, |t| t == &token::Lt) + { + generics_snapshot = Some(self.create_snapshot_for_diagnostic()); + } + let mut generics = Generics::default(); // impl A for B {} // /\ this is where `generics.span` should point when there are no type params. @@ -698,9 +707,13 @@ impl<'a> Parser<'a> { for_span: span.to(self.token.span), })); } else { - self.parse_ty_with_generics_recovery(&generics)? + self.parse_ty_with_generics_recovery(&generics).map_err(|e| { + let Some(mut snapshot) = generics_snapshot else { + return e; + }; + snapshot.maybe_type_in_generic_parameter(e) + })? }; - // If `for` is missing we try to recover. let has_for = self.eat_keyword(exp!(For)); let missing_for_span = self.prev_token.span.between(self.token.span); diff --git a/tests/ui/parser/bare-type-in-impl-parameter.rs b/tests/ui/parser/bare-type-in-impl-parameter.rs new file mode 100644 index 0000000000000..7e71d4d2f283a --- /dev/null +++ b/tests/ui/parser/bare-type-in-impl-parameter.rs @@ -0,0 +1 @@ +impl> Z for X {} //~ ERROR: expected type parameter, found path `Y` diff --git a/tests/ui/parser/bare-type-in-impl-parameter.stderr b/tests/ui/parser/bare-type-in-impl-parameter.stderr new file mode 100644 index 0000000000000..87b65793eec9b --- /dev/null +++ b/tests/ui/parser/bare-type-in-impl-parameter.stderr @@ -0,0 +1,18 @@ +error: expected type parameter, found path `Y` + --> $DIR/bare-type-in-impl-parameter.rs:1:6 + | +LL | impl> Z for X {} + | ^^^^^^^^^^ + | +help: you might have meant to bind a type parameter to a trait + | +LL | impl> Z for X {} + | ++ +help: alternatively, you might have meant to introduce type parameter + | +LL - impl> Z for X {} +LL + impl Z for X {} + | + +error: aborting due to 1 previous error + From a813343031e317673793c07d8fc816c0a1c307bb Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Fri, 12 Jun 2026 15:27:01 +0200 Subject: [PATCH 04/19] consider subtyping when checking if an infer var is sized --- compiler/rustc_hir_typeck/src/closure.rs | 11 ++-- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 12 ++-- .../src/fn_ctxt/inspect_obligations.rs | 59 +++++++++++++++---- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 1 + .../rustc_infer/src/infer/type_variable.rs | 2 +- .../impl-trait/unsized_coercion.next.stderr | 19 ------ tests/ui/impl-trait/unsized_coercion.rs | 4 +- tests/ui/sized/infer_var_subtyping.rs | 19 ++++++ 8 files changed, 82 insertions(+), 45 deletions(-) delete mode 100644 tests/ui/impl-trait/unsized_coercion.next.stderr create mode 100644 tests/ui/sized/infer_var_subtyping.rs diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 95e67b135e411..3268acd786b9b 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -23,6 +23,7 @@ use rustc_trait_selection::traits; use tracing::{debug, instrument, trace}; use super::{CoroutineTypes, Expectation, FnCtxt, check_fn}; +use crate::fn_ctxt::UseSubtyping; /// What signature do we *expect* the closure to have from context? #[derive(Debug, Clone, TypeFoldable, TypeVisitable)] @@ -317,7 +318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates( Ty::new_var(self.tcx, self.root_var(vid)), closure_kind, - self.obligations_for_self_ty(vid) + self.obligations_for_self_ty(vid, UseSubtyping::No) .into_iter() .map(|obl| (obl.predicate, obl.cause.span)), ), @@ -587,7 +588,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare. let mut return_ty = None; - for bound in self.obligations_for_self_ty(return_vid) { + for bound in self.obligations_for_self_ty(return_vid, UseSubtyping::No) { if let Some(ret_projection) = bound.predicate.as_projection_clause() && let Some(ret_projection) = ret_projection.no_bound_vars() && self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput) @@ -987,9 +988,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let output_ty = match *ret_ty.kind() { ty::Infer(ty::TyVar(ret_vid)) => { - self.obligations_for_self_ty(ret_vid).into_iter().find_map(|obligation| { - get_future_output(obligation.predicate, obligation.cause.span) - })? + self.obligations_for_self_ty(ret_vid, UseSubtyping::No).into_iter().find_map( + |obligation| get_future_output(obligation.predicate, obligation.cause.span), + )? } ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. }, .. }) => { return Some(Ty::new_error_with_message( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 9a471d3ea007b..37e198d51a831 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -755,14 +755,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { let sized_did = self.tcx.lang_items().sized_trait(); - self.obligations_for_self_ty(self_ty).into_iter().any(|obligation| { - match obligation.predicate.kind().skip_binder() { + + // NB: `T: Sized` implies that all subtypes and all supertypes of `T` are also sized, + // so it's valid to use subtyping here. (subtyping has to preserve layout and + // `T <: U => &T <: &U`, so subtyping can't change sizedness) + self.obligations_for_self_ty(self_ty, super::UseSubtyping::Yes).into_iter().any( + |obligation| match obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { Some(data.def_id()) == sized_did } _ => false, - } - }) + }, + ) } pub(crate) fn err_args(&self, len: usize, guar: ErrorGuaranteed) -> Vec> { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index 8142b6ad57f3f..c303709c7a7e2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -13,19 +13,38 @@ use tracing::{debug, instrument, trace}; use crate::FnCtxt; +/// Whatever to use subtyping or not when inspecting obligations +#[derive(Debug, Copy, Clone)] +pub(crate) enum UseSubtyping { + /// Do **not** use subtyping. [`FnCtxt::obligations_for_self_ty`] will only return obligations + /// where the self type is known to be equal to the provided vid. + No, + + /// Use subtyping. [`FnCtxt::obligations_for_self_ty`] will return obligations + /// where the self type is a subtype or a supertype of the provided vid. + /// + /// Using this requires extra care, as traits holding for a subtype or a supertype, does not + /// necessarily imply that they hold for the respective supertype or subtype. + Yes, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns a list of all obligations whose self type has been unified /// with the unconstrained type `self_ty`. #[instrument(skip(self), level = "debug")] - pub(crate) fn obligations_for_self_ty(&self, self_ty: ty::TyVid) -> PredicateObligations<'tcx> { + pub(crate) fn obligations_for_self_ty( + &self, + self_ty: ty::TyVid, + subtyping: UseSubtyping, + ) -> PredicateObligations<'tcx> { if self.next_trait_solver() { - self.obligations_for_self_ty_next(self_ty) + self.obligations_for_self_ty_next(self_ty, subtyping) } else { - let ty_var_root = self.root_var(self_ty); let mut obligations = self.fulfillment_cx.borrow().pending_obligations(); trace!("pending_obligations = {:#?}", obligations); - obligations - .retain(|obligation| self.predicate_has_self_ty(obligation.predicate, ty_var_root)); + obligations.retain(|obligation| { + self.predicate_has_self_ty(obligation.predicate, self_ty, subtyping) + }); obligations } } @@ -35,14 +54,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, predicate: ty::Predicate<'tcx>, expected_vid: ty::TyVid, + subtyping: UseSubtyping, ) -> bool { match predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { - self.type_matches_expected_vid(data.self_ty(), expected_vid) + self.type_matches_expected_vid(data.self_ty(), expected_vid, subtyping) } ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { if data.projection_term.kind.is_trait_projection() { - self.type_matches_expected_vid(data.self_ty(), expected_vid) + self.type_matches_expected_vid(data.self_ty(), expected_vid, subtyping) } else { false } @@ -65,14 +85,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip(self), ret)] - fn type_matches_expected_vid(&self, ty: Ty<'tcx>, expected_vid: ty::TyVid) -> bool { + fn type_matches_expected_vid( + &self, + ty: Ty<'tcx>, + expected_vid: ty::TyVid, + subtyping: UseSubtyping, + ) -> bool { let ty = self.shallow_resolve(ty); debug!(?ty); match *ty.kind() { - ty::Infer(ty::TyVar(found_vid)) => { - self.root_var(expected_vid) == self.root_var(found_vid) - } + ty::Infer(ty::TyVar(found_vid)) => match subtyping { + UseSubtyping::No => self.root_var(expected_vid) == self.root_var(found_vid), + UseSubtyping::Yes => { + self.sub_unification_table_root_var(expected_vid) + == self.sub_unification_table_root_var(found_vid) + } + }, _ => false, } } @@ -80,6 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn obligations_for_self_ty_next( &self, self_ty: ty::TyVid, + subtyping: UseSubtyping, ) -> PredicateObligations<'tcx> { // We only look at obligations which may reference the self type. // This lookup uses the `sub_root` instead of the inference variable @@ -94,6 +124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .borrow() .pending_obligations_potentially_referencing_sub_root(&self.infcx, sub_root_var); debug!(?obligations); + let mut obligations_for_self_ty = PredicateObligations::new(); for obligation in obligations { let mut visitor = NestedObligationsForSelfTy { @@ -101,6 +132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, obligations_for_self_ty: &mut obligations_for_self_ty, root_cause: &obligation.cause, + subtyping, }; let goal = obligation.as_goal(); @@ -183,6 +215,7 @@ struct NestedObligationsForSelfTy<'a, 'tcx> { self_ty: ty::TyVid, root_cause: &'a ObligationCause<'tcx>, obligations_for_self_ty: &'a mut PredicateObligations<'tcx>, + subtyping: UseSubtyping, } impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> { @@ -210,7 +243,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> { .orig_values() .iter() .filter_map(|arg| arg.as_type()) - .any(|ty| self.fcx.type_matches_expected_vid(ty, self.self_ty)) + .any(|ty| self.fcx.type_matches_expected_vid(ty, self.self_ty, self.subtyping)) { debug!(goal = ?inspect_goal.goal(), "goal does not mention self type"); return; @@ -218,7 +251,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> { let tcx = self.fcx.tcx; let goal = inspect_goal.goal(); - if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) { + if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty, self.subtyping) { self.obligations_for_self_ty.push(traits::Obligation::new( tcx, self.root_cause.clone(), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index c4a1b56320ff1..6d2e36e081a5e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -8,6 +8,7 @@ mod suggestions; use std::cell::{Cell, RefCell}; use std::ops::Deref; +pub(crate) use inspect_obligations::UseSubtyping; use rustc_errors::DiagCtxtHandle; use rustc_hir::attrs::{DivergingBlockBehavior, DivergingFallbackBehavior}; use rustc_hir::def_id::{DefId, LocalDefId}; diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 4c56bf0923c39..4411e03014123 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -246,7 +246,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { } /// Returns the "root" variable of `vid` in the `sub_unification_table` - /// equivalence table. All type variables that have been are related via + /// equivalence table. All type variables that have been related via /// equality or subtyping will yield the same root variable (per the /// union-find algorithm), so `sub_unification_table_root_var(a) /// == sub_unification_table_root_var(b)` implies that: diff --git a/tests/ui/impl-trait/unsized_coercion.next.stderr b/tests/ui/impl-trait/unsized_coercion.next.stderr deleted file mode 100644 index f5ca850336fbc..0000000000000 --- a/tests/ui/impl-trait/unsized_coercion.next.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/unsized_coercion.rs:12:15 - | -LL | fn hello() -> Box { - | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `dyn Trait` - -error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/unsized_coercion.rs:15:17 - | -LL | let x = hello(); - | ^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `dyn Trait` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/unsized_coercion.rs b/tests/ui/impl-trait/unsized_coercion.rs index 3f5ec3de89f31..f77f2198be0ef 100644 --- a/tests/ui/impl-trait/unsized_coercion.rs +++ b/tests/ui/impl-trait/unsized_coercion.rs @@ -3,17 +3,15 @@ //@ revisions: next old //@[next] compile-flags: -Znext-solver -//@[old] check-pass +//@ check-pass trait Trait {} impl Trait for u32 {} fn hello() -> Box { -//[next]~^ ERROR: the size for values of type `dyn Trait` cannot be known at compilation time if true { let x = hello(); - //[next]~^ ERROR: the size for values of type `dyn Trait` cannot be known at compilation time let y: Box = x; } Box::new(1u32) diff --git a/tests/ui/sized/infer_var_subtyping.rs b/tests/ui/sized/infer_var_subtyping.rs new file mode 100644 index 0000000000000..2535a71aac619 --- /dev/null +++ b/tests/ui/sized/infer_var_subtyping.rs @@ -0,0 +1,19 @@ +// Regression test for failure #4 in . +// +// This checks that when we are checking the sized-ness of an inference variable +// (here coming from the never-to-any coercion) we consider subtyping. That is, +// `?0` is sized if `(?0 <: ?1 || ?0 :> ?1) && ?1: Sized`). +// +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +#![feature(never_type)] + +fn blah(e: !) { + let source = Box::new(e); + let _: Box = source; +} + +fn main() {} From 3bd15a6e65c98d656b33a89714c2a3730e3224f1 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Wed, 17 Jun 2026 17:12:34 +0200 Subject: [PATCH 05/19] fixup docs With comparing `sub_unification_table_root_var` you can have an arbitraty long chain of subtyping relatinships, i.e. a <: b :> c <: d :> e ..., where all a/b/c/d/e/... get the same sub root var. So it's not necessarily true that there exists a type that is directly sub/super of the types compared via `sub_unification_table_root_var`. --- .../rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs | 2 +- compiler/rustc_infer/src/infer/type_variable.rs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index c303709c7a7e2..ce1d650af6b9d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -21,7 +21,7 @@ pub(crate) enum UseSubtyping { No, /// Use subtyping. [`FnCtxt::obligations_for_self_ty`] will return obligations - /// where the self type is a subtype or a supertype of the provided vid. + /// where the self type is related to the provided vid via subtyping. /// /// Using this requires extra care, as traits holding for a subtype or a supertype, does not /// necessarily imply that they hold for the respective supertype or subtype. diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 4411e03014123..21ba07428d9ec 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -249,10 +249,8 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// equivalence table. All type variables that have been related via /// equality or subtyping will yield the same root variable (per the /// union-find algorithm), so `sub_unification_table_root_var(a) - /// == sub_unification_table_root_var(b)` implies that: - /// ```text - /// exists X. (a <: X || X <: a) && (b <: X || X <: b) - /// ``` + /// == sub_unification_table_root_var(b)` implies that `a` and `b` are + /// transitively related via subtyping. pub(crate) fn sub_unification_table_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { self.sub_unification_table().find(vid).vid } From 6dc26200d114b7ca988cc849fb36b0b8a68ced0b Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 4 Jul 2026 17:51:27 +0900 Subject: [PATCH 06/19] rustc_target: Add ARMv8-M related target features --- compiler/rustc_target/src/target_features.rs | 12 +++++++++--- tests/ui/check-cfg/target_feature.stderr | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 4977e6c1217d8..611bf5950d27d 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -153,6 +153,7 @@ type ImpliedFeatures = &'static [&'static str]; static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("aclass", Unstable(sym::arm_target_feature), &[]), + ("acquire-release", Unstable(sym::arm_target_feature), &[]), ("aes", Unstable(sym::arm_target_feature), &["neon"]), ( "atomics-32", @@ -171,6 +172,8 @@ static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("fpregs", Unstable(sym::arm_target_feature), &[]), ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), ("mclass", Unstable(sym::arm_target_feature), &[]), + ("mve", Unstable(sym::arm_target_feature), &["v8.1m.main", "dsp", "fpregs"]), + ("mve.fp", Unstable(sym::arm_target_feature), &["mve"]), ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), ("rclass", Unstable(sym::arm_target_feature), &[]), ("sha2", Unstable(sym::arm_target_feature), &["neon"]), @@ -188,9 +191,13 @@ static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("v5te", Unstable(sym::arm_target_feature), &[]), ("v6", Unstable(sym::arm_target_feature), &["v5te"]), ("v6k", Unstable(sym::arm_target_feature), &["v6"]), - ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "thumb2"]), + ("v6m", Unstable(sym::arm_target_feature), &["v6"]), + ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "v8m", "thumb2"]), ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), ("v8", Unstable(sym::arm_target_feature), &["v7"]), + ("v8.1m.main", Unstable(sym::arm_target_feature), &["v8m.main"]), + ("v8m", Unstable(sym::arm_target_feature), &["v6m"]), + ("v8m.main", Unstable(sym::arm_target_feature), &["v7"]), ("vfp2", Unstable(sym::arm_target_feature), &[]), ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), @@ -1073,9 +1080,8 @@ const X86_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static const AARCH64_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "neon")]; -// We might want to add "helium" too. const ARM_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = - &[(128, "neon")]; + &[(128, "neon"), (128, "mve")]; const AMDGPU_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[(1024, "")]; diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index bcc3abf4ff267..98ba1de498bbe 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -14,6 +14,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `7e10` `a` `aclass` +`acquire-release` `addsubiw` `adx` `aes` @@ -223,6 +224,8 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `mul32high` `multivalue` `mutable-globals` +`mve` +`mve.fp` `neon` `nnp-assist` `nontrapping-fptoint` @@ -366,6 +369,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `v68` `v69` `v6k` +`v6m` `v6t2` `v7` `v71` @@ -374,6 +378,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `v79` `v8` `v8.1a` +`v8.1m.main` `v8.2a` `v8.3a` `v8.4a` @@ -382,6 +387,8 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `v8.7a` `v8.8a` `v8.9a` +`v8m` +`v8m.main` `v8plus` `v9` `v9.1a` From b570ebd37eb81fd8dff1b2e4651609d766043597 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Sat, 4 Jul 2026 17:54:11 +0700 Subject: [PATCH 07/19] Weaken guarantee for `From for RangeInclusive` As per https://github.com/rust-lang/rust/pull/155114#issuecomment-4845403701, this `From` impl no longer guarantees panicking for exhausted iterators. Instead, it only guarantees that the conversion will either panic or produce an empty range. This is done so that we can optimize the implementation of `legacy::RangeInclusive` in a way such that we cannot check if it has been exhausted in a generic context without a `Step` and/or `PartialOrd` bound. --- library/core/src/range.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/library/core/src/range.rs b/library/core/src/range.rs index fb7a51a779f6d..74d1cfde034eb 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -402,7 +402,8 @@ const impl From> for RangeInclusive { /// /// # Panics /// - /// Panics if the legacy range iterator has been exhausted. + /// If the legacy range iterator has been exhausted, + /// this function will either panic or return an empty range. /// /// # Examples /// @@ -419,19 +420,16 @@ const impl From> for RangeInclusive { /// assert_eq!((empty.start, empty.last), (0, 0)); /// ``` /// - /// ```should_panic + /// ``` /// use core::range::legacy; /// use core::range::RangeInclusive; + /// use std::panic::catch_unwind; /// /// let mut exhausted: legacy::RangeInclusive = 0..=0; /// exhausted.next(); - /// # if exhausted.is_empty() { - /// # // assert!s don't work correctly in `should_panic` doctests since you - /// # // can't assert the panic message. Skip the rest of the test instead, - /// # // so that the expected panic doesn't happen and the test fails. - /// assert!(exhausted.is_empty()); - /// let _ = RangeInclusive::from(exhausted); // this panics - /// # } + /// let result = catch_unwind(|| RangeInclusive::from(exhausted)); + /// // The `from` call either panicked or returned an empty range. + /// assert!(result.is_err() || result.is_ok_and(|range| range.is_empty())); /// ``` #[inline] fn from(value: legacy::RangeInclusive) -> Self { From c9ad0e5c95a28440bf841981d07c64184b0d27a5 Mon Sep 17 00:00:00 2001 From: Muhtasim-Rasheed Date: Sun, 5 Jul 2026 19:53:22 +0600 Subject: [PATCH 08/19] Emit a suggestion to cast the never type into a concrete type when it fails to satisfy an `impl Trait` bound --- .../src/error_reporting/traits/suggestions.rs | 17 ++++++++++++++--- tests/ui/impl-trait/return-never-type.rs | 14 ++++++++++++++ tests/ui/impl-trait/return-never-type.stderr | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 tests/ui/impl-trait/return-never-type.rs create mode 100644 tests/ui/impl-trait/return-never-type.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 139aa6bd77a87..9f5cfb39b9718 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4479,7 +4479,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ObligationCauseCode::OpaqueReturnType(expr_info) => { let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info { - let expr_ty = tcx.short_string(expr_ty, err.long_ty_path()); let expr = tcx.hir_expect_expr(hir_id); (expr_ty, expr) } else if let Some(body_id) = tcx.hir_node_by_def_id(body_id).body_id() @@ -4494,15 +4493,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder() && self.can_eq(param_env, pred.self_ty(), expr_ty) { - let expr_ty = tcx.short_string(expr_ty, err.long_ty_path()); (expr_ty, expr) } else { return; }; + let expr_ty_string = tcx.short_string(expr_ty, err.long_ty_path()); + if expr_ty.is_never() + && let span = expr.span.source_callsite() + && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) + && span != expr.span + { + err.span_suggestion( + span, + "`!` can be coerced to any type; consider casting it to a concrete type that implements the trait", + format!("{snippet} as /* Type */"), + Applicability::HasPlaceholders, + ); + } err.span_label( expr.span, with_forced_trimmed_paths!(format!( - "return type was inferred to be `{expr_ty}` here", + "return type was inferred to be `{expr_ty_string}` here", )), ); suggest_remove_deref(err, &expr); diff --git a/tests/ui/impl-trait/return-never-type.rs b/tests/ui/impl-trait/return-never-type.rs new file mode 100644 index 0000000000000..97ad2756dce71 --- /dev/null +++ b/tests/ui/impl-trait/return-never-type.rs @@ -0,0 +1,14 @@ +//@ edition:2024 + +#![feature(never_type)] + +use std::ops::Add; + +fn foo() -> impl Add { + //~^ ERROR cannot add `u32` to `!` + //~| HELP the trait `Add` is not implemented for `!` + unimplemented!() + //~^ HELP `!` can be coerced to any type; consider casting it to a concrete type that implements the trait +} + +fn main() {} diff --git a/tests/ui/impl-trait/return-never-type.stderr b/tests/ui/impl-trait/return-never-type.stderr new file mode 100644 index 0000000000000..dff06d7f41c98 --- /dev/null +++ b/tests/ui/impl-trait/return-never-type.stderr @@ -0,0 +1,18 @@ +error[E0277]: cannot add `u32` to `!` + --> $DIR/return-never-type.rs:7:13 + | +LL | fn foo() -> impl Add { + | ^^^^^^^^^^^^^ no implementation for `! + u32` +... +LL | unimplemented!() + | ---------------- return type was inferred to be `!` here + | + = help: the trait `Add` is not implemented for `!` +help: `!` can be coerced to any type; consider casting it to a concrete type that implements the trait + | +LL | unimplemented!() as /* Type */ + | +++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 1920371ad120c8c23e0254e04086d448ef46a2ee Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Sun, 5 Jul 2026 22:55:53 +0700 Subject: [PATCH 09/19] Disable `RangeInclusive`'s `From` test when unwinding isn't available. --- library/core/src/range.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 74d1cfde034eb..07f423edcfea1 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -421,6 +421,10 @@ const impl From> for RangeInclusive { /// ``` /// /// ``` + /// # // This test requires unwinding to work. + /// # // Disable it when unwinding isn't available. + /// # #[cfg(panic = "unwind")] + /// # fn main() { /// use core::range::legacy; /// use core::range::RangeInclusive; /// use std::panic::catch_unwind; @@ -430,6 +434,9 @@ const impl From> for RangeInclusive { /// let result = catch_unwind(|| RangeInclusive::from(exhausted)); /// // The `from` call either panicked or returned an empty range. /// assert!(result.is_err() || result.is_ok_and(|range| range.is_empty())); + /// # } + /// # #[cfg(not(panic = "unwind"))] + /// # fn main() {} /// ``` #[inline] fn from(value: legacy::RangeInclusive) -> Self { From 711ec67c1c6d9326a04127506d71005a3ca9bff7 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 26 May 2026 17:19:00 +0200 Subject: [PATCH 10/19] enable eager param_env norm in new solver --- .../rustc_trait_selection/src/traits/mod.rs | 6 -- .../next-solver-region-resolution.rs | 2 +- .../next-solver-region-resolution.stderr | 21 ++-- .../unsatisfied-const-trait-bound.stderr | 4 +- .../traits/next-solver/alias-bound-unsound.rs | 1 + .../next-solver/alias-bound-unsound.stderr | 12 ++- .../cycles/normalizes-to-is-not-productive.rs | 1 + .../normalizes-to-is-not-productive.stderr | 33 +++++- .../find-param-recursion-issue-152716.rs | 2 +- .../find-param-recursion-issue-152716.stderr | 11 +- .../normalize/normalize-param-env-2.stderr | 12 ++- .../normalize-param-env-4.next.stderr | 10 +- .../param_env-region-constraints-ambiguity.rs | 101 ++++++++++++++++++ .../recursive-self-normalization-2.rs | 3 +- .../recursive-self-normalization-2.stderr | 29 +---- 15 files changed, 192 insertions(+), 56 deletions(-) create mode 100644 tests/ui/traits/next-solver/normalize/param_env-region-constraints-ambiguity.rs diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index a98d06351f258..e6e919c45939c 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -257,12 +257,6 @@ fn do_normalize_predicates<'tcx>( elaborated_env: ty::ParamEnv<'tcx>, predicates: Vec>, ) -> Result>, ErrorGuaranteed> { - // Even if we move back to eager normalization elsewhere, - // param env normalization remains lazy in the next solver. - if tcx.next_trait_solver_globally() { - return Ok(predicates); - } - // FIXME. We should really... do something with these region // obligations. But this call just continues the older // behavior (i.e., doesn't cause any new bugs), and it would diff --git a/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs b/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs index 2bd8529df3991..16c0f80b1c8dd 100644 --- a/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs +++ b/tests/ui/specialization/min_specialization/next-solver-region-resolution.rs @@ -16,7 +16,7 @@ where } impl<'a, T> Foo for &T -//~^ ERROR: conflicting implementations of trait `Foo` for type `&_` +//~^ ERROR: cycle detected when computing normalized predicates of `` where Self::Item: Baz, { diff --git a/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr b/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr index cae27b4b31097..4c2dc5e8e71fa 100644 --- a/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr +++ b/tests/ui/specialization/min_specialization/next-solver-region-resolution.stderr @@ -1,17 +1,22 @@ -error[E0119]: conflicting implementations of trait `Foo` for type `&_` +error[E0391]: cycle detected when computing normalized predicates of `` --> $DIR/next-solver-region-resolution.rs:18:1 | -LL | / impl<'a, T> Foo for &'a T -LL | | where -LL | | Self::Item: 'a, - | |___________________- first implementation here -... LL | / impl<'a, T> Foo for &T LL | | LL | | where LL | | Self::Item: Baz, - | |____________________^ conflicting implementation for `&_` + | |____________________^ + | + = note: ...which immediately requires computing normalized predicates of `` again +note: cycle used when computing whether impls specialize one another + --> $DIR/next-solver-region-resolution.rs:12:1 + | +LL | / impl<'a, T> Foo for &'a T +LL | | where +LL | | Self::Item: 'a, + | |___________________^ + = note: for more information, see and error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0119`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr index 1e450571ed9da..147b248c33da8 100644 --- a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr +++ b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr @@ -18,8 +18,9 @@ LL | fn accept0(_: Container<{ T::make() }>) {} = note: ...which requires building an abstract representation for `accept0::{constant#0}`... = note: ...which requires building THIR for `accept0::{constant#0}`... = note: ...which requires type-checking `accept0::{constant#0}`... + = note: ...which requires computing normalized predicates of `accept0::{constant#0}`... = note: ...which again requires evaluating type-level constant, completing the cycle - = note: cycle used when checking that `accept0` is well-formed + = note: cycle used when computing normalized predicates of `accept0` = note: for more information, see and error[E0391]: cycle detected when checking if `accept1::{constant#0}` is a trivial const @@ -32,6 +33,7 @@ LL | const fn accept1(_: Container<{ T::make() }>) {} = note: ...which requires building an abstract representation for `accept1::{constant#0}`... = note: ...which requires building THIR for `accept1::{constant#0}`... = note: ...which requires type-checking `accept1::{constant#0}`... + = note: ...which requires computing normalized predicates of `accept1::{constant#0}`... = note: ...which requires evaluating type-level constant... = note: ...which requires const-evaluating + checking `accept1::{constant#0}`... = note: ...which again requires checking if `accept1::{constant#0}` is a trivial const, completing the cycle diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.rs b/tests/ui/traits/next-solver/alias-bound-unsound.rs index 432b13d161afb..7c3985f30cae9 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.rs +++ b/tests/ui/traits/next-solver/alias-bound-unsound.rs @@ -21,6 +21,7 @@ trait Foo { impl Foo for () { type Item = String where String: Copy; //~^ ERROR overflow evaluating the requirement `String: Copy` + //~| ERROR: overflow evaluating the requirement `<() as Foo>::Item == _` [E0275] } fn main() { diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.stderr b/tests/ui/traits/next-solver/alias-bound-unsound.stderr index b20534f5dc584..d82c5016eeaca 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.stderr +++ b/tests/ui/traits/next-solver/alias-bound-unsound.stderr @@ -1,3 +1,9 @@ +error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` + --> $DIR/alias-bound-unsound.rs:22:5 + | +LL | type Item = String where String: Copy; + | ^^^^^^^^^ + error[E0275]: overflow evaluating the requirement `String: Copy` --> $DIR/alias-bound-unsound.rs:22:38 | @@ -13,17 +19,17 @@ LL | type Item: Copy | ^^^^ this trait's associated type doesn't have the requirement `String: Copy` error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == String` - --> $DIR/alias-bound-unsound.rs:28:22 + --> $DIR/alias-bound-unsound.rs:29:22 | LL | let _ = identity(<() as Foo>::copy_me(&x)); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:28:43 + --> $DIR/alias-bound-unsound.rs:29:43 | LL | let _ = identity(<() as Foo>::copy_me(&x)); | ^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs index ffbbecaf89570..83e300b077428 100644 --- a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs @@ -38,6 +38,7 @@ fn impls_bound() { // - normalize `>::Assoc` // - via blanket impl, requires where-clause `Foo: Bound` -> cycle fn generic() +//~^ ERROR the trait bound `Foo: Bound` is not satisfied where >::Assoc: Bound, //~^ ERROR the trait bound `Foo: Bound` is not satisfied diff --git a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr index f679b94a92377..e18265190278a 100644 --- a/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr +++ b/tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.stderr @@ -1,5 +1,32 @@ error[E0277]: the trait bound `Foo: Bound` is not satisfied - --> $DIR/normalizes-to-is-not-productive.rs:42:31 + --> $DIR/normalizes-to-is-not-productive.rs:40:1 + | +LL | / fn generic() +LL | | +LL | | where +LL | | >::Assoc: Bound, + | |____________________________________^ unsatisfied trait bound + | +help: the trait `Bound` is not implemented for `Foo` + --> $DIR/normalizes-to-is-not-productive.rs:18:1 + | +LL | struct Foo; + | ^^^^^^^^^^ +help: the trait `Bound` is implemented for `u32` + --> $DIR/normalizes-to-is-not-productive.rs:11:1 + | +LL | impl Bound for u32 { + | ^^^^^^^^^^^^^^^^^^ +note: required for `Foo` to implement `Trait` + --> $DIR/normalizes-to-is-not-productive.rs:23:19 + | +LL | impl Trait for T { + | ----- ^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error[E0277]: the trait bound `Foo: Bound` is not satisfied + --> $DIR/normalizes-to-is-not-productive.rs:43:31 | LL | >::Assoc: Bound, | ^^^^^ unsatisfied trait bound @@ -30,7 +57,7 @@ LL | | } | |_^ required by this bound in `Bound` error[E0277]: the trait bound `Foo: Bound` is not satisfied - --> $DIR/normalizes-to-is-not-productive.rs:47:19 + --> $DIR/normalizes-to-is-not-productive.rs:48:19 | LL | impls_bound::(); | ^^^ unsatisfied trait bound @@ -51,6 +78,6 @@ note: required by a bound in `impls_bound` LL | fn impls_bound() { | ^^^^^ required by this bound in `impls_bound` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs index 914773c82196a..4d088073c3ba6 100644 --- a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs +++ b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.rs @@ -14,7 +14,7 @@ fn foo() where T: for<'a> Proj<'a, Assoc = for<'b> fn(>::Assoc)>, (): Trait<>::Assoc> - //~^ ERROR: overflow evaluating the requirement `(): Trait<>::Assoc>` + //~^ ERROR: the trait bound `(): Trait fn(>::Assoc))>` is not satisfied { } diff --git a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr index e2ee83cfadbef..1408890184a90 100644 --- a/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr +++ b/tests/ui/traits/next-solver/find-param-recursion-issue-152716.stderr @@ -1,11 +1,14 @@ -error[E0275]: overflow evaluating the requirement `(): Trait<>::Assoc>` +error[E0277]: the trait bound `(): Trait fn(>::Assoc))>` is not satisfied --> $DIR/find-param-recursion-issue-152716.rs:16:9 | LL | (): Trait<>::Assoc> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait fn(>::Assoc))>` is not implemented for `()` | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`find_param_recursion_issue_152716`) +help: consider extending the `where` clause, but there might be an alternative better way to express this requirement + | +LL | (): Trait<>::Assoc>, (): Trait fn(>::Assoc))> + | +++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr index d3effe2eb0389..553a2157074f3 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr +++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-2.stderr @@ -35,6 +35,16 @@ LL | trait A { LL | fn f() | ^ this trait's associated function doesn't have the requirement `_: A` +error[E0275]: overflow evaluating the requirement `<() as A>::Assoc == _` + --> $DIR/normalize-param-env-2.rs:22:5 + | +LL | / fn f() +LL | | where +LL | | Self::Assoc: A, + | |__________________________^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` --> $DIR/normalize-param-env-2.rs:24:22 | @@ -82,7 +92,7 @@ LL | where LL | Self::Assoc: A, | ^^^^ required by this bound in `A::f` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0275, E0283. For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr index 47d38365e970e..aea394c6ec58a 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr +++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr @@ -1,3 +1,11 @@ +error[E0275]: overflow evaluating the requirement `::Assoc == _` + --> $DIR/normalize-param-env-4.rs:17:1 + | +LL | / fn foo() +LL | | where +LL | | ::Assoc: Trait, + | |_______________________________^ + error[E0275]: overflow evaluating the requirement `::Assoc: Trait` --> $DIR/normalize-param-env-4.rs:19:26 | @@ -22,6 +30,6 @@ note: required by a bound in `impls_trait` LL | fn impls_trait() {} | ^^^^^ required by this bound in `impls_trait` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/normalize/param_env-region-constraints-ambiguity.rs b/tests/ui/traits/next-solver/normalize/param_env-region-constraints-ambiguity.rs new file mode 100644 index 0000000000000..08a7148ca5e88 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/param_env-region-constraints-ambiguity.rs @@ -0,0 +1,101 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#265. Different where-clauses +// normalizing to the same bound caused ambiguity errors if we lazily normalized +// where-clauses when using them to prove a goal. +// +// We avoid these errors by eagerly normalizing the `param_env`. + +mod one { + trait Trait { + type Assoc<'a> + where + Self: 'a; + } + + trait Bound<'a> {} + fn impls_bound<'a, T: Bound<'a>>() {} + fn foo<'a, T: 'a>() + where + T: Trait = T> + Bound<'a>, + T::Assoc<'a>: Bound<'a>, + { + impls_bound::<'_, T>(); + } +} + +mod two { + trait Trait { + type Assoc<'a> + where + Self: 'a; + } + + trait Bound {} + fn impls_bound() {} + fn foo<'a, T: 'a>() + where + T: Trait = T> + Bound, + T::Assoc<'a>: Bound, + { + impls_bound::(); + } +} + +// Minimization of tokio-par-util. +mod three { + trait Trait1 { + type Assoc1; + } + + trait Trait2 { + type Assoc2; + } + + struct Indir(T); + impl Trait2 for Indir + where + T: Trait1, + T::Assoc1: Trait2 + 'static, + { + type Assoc2 = ::Assoc2; + } + + struct WrapperTwo(T, U); + + impl Trait1 for WrapperTwo + where + T: Trait1, + T::Assoc1: Trait2 + 'static, + // additional region constraint in this candidate so they + // can't be merged. + U: Trait1 as Trait2>::Assoc2>, + U: Trait1::Assoc2>, + { + type Assoc1 = i32; + } +} + +// Minimization of `qazer` +mod four { + trait Value { + type SelfType<'a> + where + Self: 'a; + } + + trait Repository {} + struct RedbRepo(From, Into); + + impl Repository for RedbRepo + where + for<'a> From: Value = From> + Clone + 'static, + for<'a> ::SelfType<'a>: Clone, + { + } +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs index ed52d05b39c99..8dc27c0da605a 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs @@ -13,8 +13,7 @@ fn needs_bar() {} fn test::Assoc2> + Foo2::Assoc1>>() { needs_bar::(); - //~^ ERROR: the trait bound `::Assoc2: Bar` is not satisfied - //~| ERROR: the size for values of type `::Assoc2` cannot be known at compilation time + //~^ ERROR: the trait bound `::Assoc1: Bar` is not satisfied } fn main() {} diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr index 40291ce0cfb86..6f5111a6193ca 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `::Assoc2: Bar` is not satisfied +error[E0277]: the trait bound `::Assoc1: Bar` is not satisfied --> $DIR/recursive-self-normalization-2.rs:15:17 | LL | needs_bar::(); - | ^^^^^^^^^ the trait `Bar` is not implemented for `::Assoc2` + | ^^^^^^^^^ the trait `Bar` is not implemented for `::Assoc1` | note: required by a bound in `needs_bar` --> $DIR/recursive-self-normalization-2.rs:12:17 @@ -11,30 +11,9 @@ LL | fn needs_bar() {} | ^^^ required by this bound in `needs_bar` help: consider further restricting the associated type | -LL | fn test::Assoc2> + Foo2::Assoc1>>() where ::Assoc2: Bar { +LL | fn test::Assoc2> + Foo2::Assoc1>>() where ::Assoc1: Bar { | ++++++++++++++++++++++++++++++ -error[E0277]: the size for values of type `::Assoc2` cannot be known at compilation time - --> $DIR/recursive-self-normalization-2.rs:15:17 - | -LL | needs_bar::(); - | ^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `::Assoc2` -note: required by an implicit `Sized` bound in `needs_bar` - --> $DIR/recursive-self-normalization-2.rs:12:14 - | -LL | fn needs_bar() {} - | ^ required by the implicit `Sized` requirement on this type parameter in `needs_bar` -help: consider further restricting the associated type - | -LL | fn test::Assoc2> + Foo2::Assoc1>>() where ::Assoc2: Sized { - | ++++++++++++++++++++++++++++++++ -help: consider relaxing the implicit `Sized` restriction - | -LL | fn needs_bar() {} - | ++++++++ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. From 4463c0fea0d404826d4e7b699374da1559aae135 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 26 May 2026 17:22:55 +0200 Subject: [PATCH 11/19] remove unnecessary code --- .../src/check/compare_impl_item.rs | 19 ++---- .../rustc_trait_selection/src/traits/mod.rs | 64 ------------------- 2 files changed, 7 insertions(+), 76 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index ce4c21ff8d86e..af779152ed675 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -239,26 +239,21 @@ fn compare_method_predicate_entailment<'tcx>( let hybrid_preds = hybrid_preds.into_iter().map(Unnormalized::skip_norm_wip); let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id); let param_env = ty::ParamEnv::new(tcx.mk_clauses_from_iter(hybrid_preds)); - // FIXME(-Zhigher-ranked-assumptions): The `hybrid_preds` + // NOTE(-Zhigher-ranked-assumptions): The `hybrid_preds` // should be well-formed. However, using them may result in // region errors as we currently don't track placeholder // assumptions. // - // To avoid being backwards incompatible with the old solver, - // we also eagerly normalize the where-bounds in the new solver - // here while ignoring region constraints. This means we can then - // use where-bounds whose normalization results in placeholder - // errors further down without getting any errors. + // We eagerly normalize the where-clauses here while ignoring + // region constraints. This means we can then use where-bounds + // whose normalization results in placeholder errors further + // down without getting any errors. // - // It should be sound to do so as the only region errors here + // This should be sound to do so as the only region errors here // should be due to missing implied bounds. // // cc trait-system-refactor-initiative/issues/166. - let param_env = if tcx.next_trait_solver_globally() { - traits::deeply_normalize_param_env_ignoring_regions(tcx, param_env, normalize_cause) - } else { - traits::normalize_param_env_or_error(tcx, param_env, normalize_cause) - }; + let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); debug!(caller_bounds=?param_env.caller_bounds()); let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index e6e919c45939c..1e3b8ad577b87 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -30,7 +30,6 @@ use rustc_errors::ErrorGuaranteed; pub use rustc_infer::traits::*; use rustc_macros::TypeVisitable; use rustc_middle::query::Providers; -use rustc_middle::span_bug; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{ self, Clause, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeFolder, @@ -475,69 +474,6 @@ pub fn normalize_param_env_or_error<'tcx>( ty::ParamEnv::new(tcx.mk_clauses(&predicates)) } -/// Deeply normalize the param env using the next solver ignoring -/// region errors. -/// -/// FIXME(-Zhigher-ranked-assumptions): this is a hack to work around -/// the fact that we don't support placeholder assumptions right now -/// and is necessary for `compare_method_predicate_entailment`, see the -/// use of this function for more info. We should remove this once we -/// have proper support for implied bounds on binders. -#[instrument(level = "debug", skip(tcx))] -pub fn deeply_normalize_param_env_ignoring_regions<'tcx>( - tcx: TyCtxt<'tcx>, - unnormalized_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, -) -> ty::ParamEnv<'tcx> { - let predicates: Vec<_> = - util::elaborate(tcx, unnormalized_env.caller_bounds().into_iter()).collect(); - - debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); - - let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates)); - if !elaborated_env.has_aliases() { - return elaborated_env; - } - - let span = cause.span; - let infcx = tcx - .infer_ctxt() - .with_next_trait_solver(true) - .ignoring_regions() - .build(TypingMode::non_body_analysis()); - let predicates = match crate::solve::deeply_normalize::<_, FulfillmentError<'tcx>>( - infcx.at(&cause, elaborated_env), - Unnormalized::new_wip(predicates), - ) { - Ok(predicates) => predicates, - Err(errors) => { - infcx.err_ctxt().report_fulfillment_errors(errors); - // An unnormalized env is better than nothing. - debug!("normalize_param_env_or_error: errored resolving predicates"); - return elaborated_env; - } - }; - - debug!("do_normalize_predicates: normalized predicates = {:?}", predicates); - // FIXME(-Zhigher-ranked-assumptions): We're ignoring region errors for now. - // There're placeholder constraints `leaking` out. - // See the fixme in the enclosing function's docs for more. - let _errors = infcx.resolve_regions(cause.body_id, elaborated_env, []); - - let predicates = match infcx.fully_resolve(predicates) { - Ok(predicates) => predicates, - Err(fixup_err) => { - span_bug!( - span, - "inference variables in normalized parameter environment: {}", - fixup_err - ) - } - }; - debug!("normalize_param_env_or_error: final predicates={:?}", predicates); - ty::ParamEnv::new(tcx.mk_clauses(&predicates)) -} - #[derive(Debug)] pub enum EvaluateConstErr { /// The constant being evaluated was either a generic parameter or inference variable, *or*, From 81ffc417903af1777123e353967613334259577b Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 27 May 2026 10:47:21 +0200 Subject: [PATCH 12/19] entirely ignore region errors in normalize_predicates the old solver doesn't register `TypeOutlives` and `RegionOutlives` goals if `ignoring_regions` is set. The new solver always registers these requirements, so we instead ignore errors caused by these constraints. --- .../rustc_trait_selection/src/traits/mod.rs | 19 +++++---- tests/crashes/136661.rs | 25 ----------- ...alize-param-env-missing-implied-bound-1.rs | 41 +++++++++++++++++++ ...alize-param-env-missing-implied-bound-2.rs | 39 ++++++++++++++++++ 4 files changed, 91 insertions(+), 33 deletions(-) delete mode 100644 tests/crashes/136661.rs create mode 100644 tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-1.rs create mode 100644 tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-2.rs diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 1e3b8ad577b87..3abc79bd21304 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -287,17 +287,20 @@ fn do_normalize_predicates<'tcx>( // We can use the `elaborated_env` here; the region code only // cares about declarations like `'a: 'b`. + // // FIXME: It's very weird that we ignore region obligations but apparently // still need to use `resolve_regions` as we need the resolved regions in // the normalized predicates. - let errors = infcx.resolve_regions(cause.body_id, elaborated_env, []); - if !errors.is_empty() { - tcx.dcx().span_delayed_bug( - span, - format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"), - ); - } - + // + // FIXME(-Zhigher-ranked-assumptions): We're ignoring region errors for now. + // There're placeholder constraints `leaking` out. This is a hack to work around + // the fact that we don't support placeholder assumptions right now and is necessary + // for `compare_method_predicate_entailment`. We should remove this once we + // have proper support for implied bounds on binders. + // + // This is required by trait-system-refactor-initiative#166. The new solver encounters + // this more frequently as we entirely ignore outlives predicates with the old solver. + let _errors = infcx.resolve_regions(cause.body_id, elaborated_env, []); match infcx.fully_resolve(predicates) { Ok(predicates) => Ok(predicates), Err(fixup_err) => { diff --git a/tests/crashes/136661.rs b/tests/crashes/136661.rs deleted file mode 100644 index 76161a566f4c7..0000000000000 --- a/tests/crashes/136661.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ known-bug: #136661 - -#![allow(unused)] - -trait Supertrait {} - -trait Other { - fn method(&self) {} -} - -impl WithAssoc for &'static () { - type As = (); -} - -trait WithAssoc { - type As; -} - -trait Trait: Supertrait { - fn method(&self) {} -} - -fn hrtb Trait<&'a ()>>() {} - -pub fn main() {} diff --git a/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-1.rs b/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-1.rs new file mode 100644 index 0000000000000..7fcb164d3a95d --- /dev/null +++ b/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-1.rs @@ -0,0 +1,41 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// In `fn hrtb` normalizing the elaborated where-clause +// `T: for<'a> Supertrait<<&'a () as WithAssoc>::As>` results in +// a region error as `&'a ()` is not equal to `&'static ()`. +// +// This happens even though the where-clauses themselves are well-formed +// as we never have to prove `&'a (): WithAssoc` as `'a` is a bound variable. +// +// I don't think this can cause unsoundness and has already been accepted in the +// old solver as we didn't register TypeOutlives constraints if `ignoring_regions` +// is set. The new solver always registers outlives constraints, so this then +// caused an error there. We need to ignore these region errors with the new solver +// due to trait-system-refactor-initiative#166. + +#![allow(unused)] + +trait Supertrait {} + +trait Other { + fn method(&self) {} +} + +impl WithAssoc for T { + type As = T; +} + +trait WithAssoc { + type As; +} + +trait Trait: Supertrait { + fn method(&self) {} +} + +fn hrtb Trait<&'a ()>>() {} + +pub fn main() {} diff --git a/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-2.rs b/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-2.rs new file mode 100644 index 0000000000000..9339518aae1a0 --- /dev/null +++ b/tests/ui/traits/normalize/normalize-param-env-missing-implied-bound-2.rs @@ -0,0 +1,39 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// In `fn hrtb` normalizing the elaborated where-clause +// `T: for<'a> Supertrait<<&'a () as WithAssoc>::As>` results in +// a region error as `&'a ()` is not `'static`. +// +// This happens even though the where-clauses themselves are well-formed +// as we never have to prove `&'a (): WithAssoc` as `'a` is a bound variable. +// +// Unlike `normalize-param-env-missing-implied-bound-1.rs` this snippet caused +// an ICE with the old trait solver, as we did register region constraints from +// relating types. This ICE was tracked in #136661. + +#![allow(unused)] + +trait Supertrait {} + +trait Other { + fn method(&self) {} +} + +impl WithAssoc for &'static () { + type As = (); +} + +trait WithAssoc { + type As; +} + +trait Trait: Supertrait { + fn method(&self) {} +} + +fn hrtb Trait<&'a ()>>() {} + +pub fn main() {} From 22d9d00232022675a42aeeffaf91af2908f856d8 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 13:39:35 +1000 Subject: [PATCH 13/19] Move `std::io::SeekFrom` to `core::io` --- library/alloc/src/io/mod.rs | 4 ++-- library/core/src/io/mod.rs | 2 ++ library/core/src/io/seek.rs | 29 +++++++++++++++++++++++++++++ library/std/src/io/mod.rs | 30 +----------------------------- 4 files changed, 34 insertions(+), 31 deletions(-) create mode 100644 library/core/src/io/seek.rs diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 86dc8c685a985..486f5bf330a70 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -13,8 +13,8 @@ pub use core::io::const_error; pub use core::io::{BorrowedBuf, BorrowedCursor}; #[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ - Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Sink, Take, empty, - repeat, sink, + Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, SeekFrom, Sink, + Take, empty, repeat, sink, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 95fcc2d6dc19b..e031222e6f00a 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -5,6 +5,7 @@ mod cursor; mod error; mod impls; mod io_slice; +mod seek; mod size_hint; mod util; @@ -21,6 +22,7 @@ pub use self::{ cursor::Cursor, error::{Error, ErrorKind, Result}, io_slice::{IoSlice, IoSliceMut}, + seek::SeekFrom, util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, }; #[doc(hidden)] diff --git a/library/core/src/io/seek.rs b/library/core/src/io/seek.rs new file mode 100644 index 0000000000000..bd0f6ca7a46cd --- /dev/null +++ b/library/core/src/io/seek.rs @@ -0,0 +1,29 @@ +/// Enumeration of possible methods to seek within an I/O object. +/// +/// It is used by the [`Seek`] trait. +/// +/// [`Seek`]: ../../std/io/trait.Seek.html +#[derive(Copy, PartialEq, Eq, Clone, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")] +pub enum SeekFrom { + /// Sets the offset to the provided number of bytes. + #[stable(feature = "rust1", since = "1.0.0")] + Start(#[stable(feature = "rust1", since = "1.0.0")] u64), + + /// Sets the offset to the size of this object plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + #[stable(feature = "rust1", since = "1.0.0")] + End(#[stable(feature = "rust1", since = "1.0.0")] i64), + + /// Sets the offset to the current position plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + #[stable(feature = "rust1", since = "1.0.0")] + Current(#[stable(feature = "rust1", since = "1.0.0")] i64), +} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index b06ec9af7239d..1923c8c5dcb93 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -311,7 +311,7 @@ pub use alloc_crate::io::const_error; pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Chain, Empty, Error, ErrorKind, Repeat, Result, Sink, Take, empty, repeat, sink, + Chain, Empty, Error, ErrorKind, Repeat, Result, SeekFrom, Sink, Take, empty, repeat, sink, }; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; @@ -1953,34 +1953,6 @@ pub(crate) fn stream_len_default(self_: &mut T) -> Result Ok(len) } -/// Enumeration of possible methods to seek within an I/O object. -/// -/// It is used by the [`Seek`] trait. -#[derive(Copy, PartialEq, Eq, Clone, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")] -pub enum SeekFrom { - /// Sets the offset to the provided number of bytes. - #[stable(feature = "rust1", since = "1.0.0")] - Start(#[stable(feature = "rust1", since = "1.0.0")] u64), - - /// Sets the offset to the size of this object plus the specified number of - /// bytes. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - #[stable(feature = "rust1", since = "1.0.0")] - End(#[stable(feature = "rust1", since = "1.0.0")] i64), - - /// Sets the offset to the current position plus the specified number of - /// bytes. - /// - /// It is possible to seek beyond the end of an object, but it's an error to - /// seek before byte 0. - #[stable(feature = "rust1", since = "1.0.0")] - Current(#[stable(feature = "rust1", since = "1.0.0")] i64), -} - fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { let mut read = 0; loop { From 83de83478d34efb1a00728ee512be10d60e423f4 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Thu, 21 May 2026 13:52:15 +1000 Subject: [PATCH 14/19] Move `std::io::Seek` to `core::io` --- library/alloc/src/io/impls.rs | 65 +++++++++- library/alloc/src/io/mod.rs | 6 +- library/alloc/src/lib.rs | 1 + library/core/src/io/cursor.rs | 37 ++++++ library/core/src/io/impls.rs | 30 ++++- library/core/src/io/mod.rs | 3 +- library/core/src/io/seek.rs | 193 ++++++++++++++++++++++++++- library/core/src/io/util.rs | 63 ++++++++- library/std/src/io/cursor.rs | 37 +----- library/std/src/io/impls.rs | 87 +------------ library/std/src/io/mod.rs | 238 +--------------------------------- library/std/src/io/util.rs | 21 +-- library/std/src/lib.rs | 1 + 13 files changed, 395 insertions(+), 387 deletions(-) diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index 6f392bba93ef4..732842bc65df7 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,5 +1,7 @@ use crate::boxed::Box; -use crate::io::SizeHint; +use crate::io::{self, Seek, SeekFrom, SizeHint}; +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +use crate::sync::Arc; // ============================================================================= // Forwarding implementations @@ -18,5 +20,66 @@ impl SizeHint for Box { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for Box { + #[inline] + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (**self).seek(pos) + } + + #[inline] + fn rewind(&mut self) -> io::Result<()> { + (**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> io::Result { + (**self).stream_len() + } + + #[inline] + fn stream_position(&mut self) -> io::Result { + (**self).stream_position() + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + (**self).seek_relative(offset) + } +} + // ============================================================================= // In-memory buffer implementations + +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Seek for Arc +where + for<'a> &'a S: Seek, + S: crate::io::IoHandle, +{ + #[inline] + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (&**self).seek(pos) + } + + #[inline] + fn rewind(&mut self) -> io::Result<()> { + (&**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> io::Result { + (&**self).stream_len() + } + + #[inline] + fn stream_position(&mut self) -> io::Result { + (&**self).stream_position() + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + (&**self).seek_relative(offset) + } +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 486f5bf330a70..44a1d1b71d164 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -13,9 +13,9 @@ pub use core::io::const_error; pub use core::io::{BorrowedBuf, BorrowedCursor}; #[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ - Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, SeekFrom, Sink, - Take, empty, repeat, sink, + Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Seek, SeekFrom, + Sink, Take, empty, repeat, sink, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, take}; +pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, stream_len_default, take}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 33fbcd8e32a6e..01b4e6f938616 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -155,6 +155,7 @@ #![feature(ptr_metadata)] #![feature(raw_os_error_ty)] #![feature(rev_into_inner)] +#![feature(seek_stream_len)] #![feature(set_ptr_value)] #![feature(share_trait)] #![feature(sized_type_properties)] diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index 24d3bfc67b988..f155f199d7be9 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -1,3 +1,5 @@ +use crate::io::{self, ErrorKind, SeekFrom}; + /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. /// @@ -291,3 +293,38 @@ where self.pos = other.pos; } } + +#[stable(feature = "rust1", since = "1.0.0")] +impl io::Seek for Cursor +where + T: AsRef<[u8]>, +{ + fn seek(&mut self, style: SeekFrom) -> io::Result { + let (base_pos, offset) = match style { + SeekFrom::Start(n) => { + self.set_position(n); + return Ok(n); + } + SeekFrom::End(n) => (self.get_ref().as_ref().len() as u64, n), + SeekFrom::Current(n) => (self.position(), n), + }; + match base_pos.checked_add_signed(offset) { + Some(n) => { + self.set_position(n); + Ok(n) + } + None => Err(io::const_error!( + ErrorKind::InvalidInput, + "invalid seek to a negative or overflowing position", + )), + } + } + + fn stream_len(&mut self) -> io::Result { + Ok(self.get_ref().as_ref().len() as u64) + } + + fn stream_position(&mut self) -> io::Result { + Ok(self.position()) + } +} diff --git a/library/core/src/io/impls.rs b/library/core/src/io/impls.rs index cc222c56121e2..e8c716f060119 100644 --- a/library/core/src/io/impls.rs +++ b/library/core/src/io/impls.rs @@ -1,4 +1,4 @@ -use crate::io::SizeHint; +use crate::io::{self, Seek, SeekFrom, SizeHint}; // ============================================================================= // Forwarding implementations @@ -17,6 +17,34 @@ impl SizeHint for &mut T { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for &mut S { + #[inline] + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (**self).seek(pos) + } + + #[inline] + fn rewind(&mut self) -> io::Result<()> { + (**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> io::Result { + (**self).stream_len() + } + + #[inline] + fn stream_position(&mut self) -> io::Result { + (**self).stream_position() + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + (**self).seek_relative(offset) + } +} + // ============================================================================= // In-memory buffer implementations diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index e031222e6f00a..d85ee311e18a9 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -22,13 +22,14 @@ pub use self::{ cursor::Cursor, error::{Error, ErrorKind, Result}, io_slice::{IoSlice, IoSliceMut}, - seek::SeekFrom, + seek::{Seek, SeekFrom}, util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ error::{Custom, CustomOwner, OsFunctions}, + seek::stream_len_default, size_hint::SizeHint, util::{chain, take}, }; diff --git a/library/core/src/io/seek.rs b/library/core/src/io/seek.rs index bd0f6ca7a46cd..94865a1d11ecc 100644 --- a/library/core/src/io/seek.rs +++ b/library/core/src/io/seek.rs @@ -1,8 +1,197 @@ +use crate::io::Result; + +/// The `Seek` trait provides a cursor which can be moved within a stream of +/// bytes. +/// +/// The stream typically has a fixed size, allowing seeking relative to either +/// end or the current offset. +/// +/// # Examples +/// +/// `File`s implement `Seek`: +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// use std::fs::File; +/// use std::io::SeekFrom; +/// +/// fn main() -> io::Result<()> { +/// let mut f = File::open("foo.txt")?; +/// +/// // move the cursor 42 bytes from the start of the file +/// f.seek(SeekFrom::Start(42))?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoSeek")] +pub trait Seek { + /// Seek to an offset, in bytes, in a stream. + /// + /// A seek beyond the end of a stream is allowed, but behavior is defined + /// by the implementation. + /// + /// If the seek operation completed successfully, + /// this method returns the new position from the start of the stream. + /// That position can be used later with [`SeekFrom::Start`]. + /// + /// # Errors + /// + /// Seeking can fail, for example because it might involve flushing a buffer. + /// + /// Seeking to a negative offset is considered an error. + #[stable(feature = "rust1", since = "1.0.0")] + fn seek(&mut self, pos: SeekFrom) -> Result; + + /// Rewind to the beginning of a stream. + /// + /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`. + /// + /// # Errors + /// + /// Rewinding can fail, for example because it might involve flushing a buffer. + /// + /// # Example + /// + /// ```no_run + /// use std::io::{Read, Seek, Write}; + /// use std::fs::OpenOptions; + /// + /// let mut f = OpenOptions::new() + /// .write(true) + /// .read(true) + /// .create(true) + /// .open("foo.txt")?; + /// + /// let hello = "Hello!\n"; + /// write!(f, "{hello}")?; + /// f.rewind()?; + /// + /// let mut buf = String::new(); + /// f.read_to_string(&mut buf)?; + /// assert_eq!(&buf, hello); + /// # std::io::Result::Ok(()) + /// ``` + #[stable(feature = "seek_rewind", since = "1.55.0")] + fn rewind(&mut self) -> Result<()> { + self.seek(SeekFrom::Start(0))?; + Ok(()) + } + + /// Returns the length of this stream (in bytes). + /// + /// The default implementation uses up to three seek operations. If this + /// method returns successfully, the seek position is unchanged (i.e. the + /// position before calling this method is the same as afterwards). + /// However, if this method returns an error, the seek position is + /// unspecified. + /// + /// If you need to obtain the length of *many* streams and you don't care + /// about the seek position afterwards, you can reduce the number of seek + /// operations by simply calling `seek(SeekFrom::End(0))` and using its + /// return value (it is also the stream length). + /// + /// Note that length of a stream can change over time (for example, when + /// data is appended to a file). So calling this method multiple times does + /// not necessarily return the same length each time. + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_stream_len)] + /// use std::{ + /// io::{self, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// + /// let len = f.stream_len()?; + /// println!("The file is currently {len} bytes long"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_stream_len", issue = "59359")] + fn stream_len(&mut self) -> Result { + stream_len_default(self) + } + + /// Returns the current seek position from the start of the stream. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(0))`. + /// + /// # Example + /// + /// ```no_run + /// use std::{ + /// io::{self, BufRead, BufReader, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = BufReader::new(File::open("foo.txt")?); + /// + /// let before = f.stream_position()?; + /// f.read_line(&mut String::new())?; + /// let after = f.stream_position()?; + /// + /// println!("The first line was {} bytes long", after - before); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "seek_convenience", since = "1.51.0")] + fn stream_position(&mut self) -> Result { + self.seek(SeekFrom::Current(0)) + } + + /// Seeks relative to the current position. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but + /// doesn't return the new position which can allow some implementations + /// such as `BufReader` to perform more efficient seeks. + /// + /// # Example + /// + /// ```no_run + /// use std::{ + /// io::{self, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// f.seek_relative(10)?; + /// assert_eq!(f.stream_position()?, 10); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "seek_seek_relative", since = "1.80.0")] + fn seek_relative(&mut self, offset: i64) -> Result<()> { + self.seek(SeekFrom::Current(offset))?; + Ok(()) + } +} + +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn stream_len_default(self_: &mut T) -> Result { + let old_pos = self_.stream_position()?; + let len = self_.seek(SeekFrom::End(0))?; + + // Avoid seeking a third time when we were already at the end of the + // stream. The branch is usually way cheaper than a seek operation. + if old_pos != len { + self_.seek(SeekFrom::Start(old_pos))?; + } + + Ok(len) +} + /// Enumeration of possible methods to seek within an I/O object. /// /// It is used by the [`Seek`] trait. -/// -/// [`Seek`]: ../../std/io/trait.Seek.html #[derive(Copy, PartialEq, Eq, Clone, Debug)] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")] diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs index a40318bb85e08..de37690436c80 100644 --- a/library/core/src/io/util.rs +++ b/library/core/src/io/util.rs @@ -1,4 +1,4 @@ -use crate::io::SizeHint; +use crate::io::{ErrorKind, Result, Seek, SeekFrom, SizeHint}; use crate::{cmp, fmt}; /// `Empty` ignores any data written via [`Write`], and will always be empty @@ -23,6 +23,24 @@ impl SizeHint for Empty { } } +#[stable(feature = "empty_seek", since = "1.51.0")] +impl Seek for Empty { + #[inline] + fn seek(&mut self, _pos: SeekFrom) -> Result { + Ok(0) + } + + #[inline] + fn stream_len(&mut self) -> Result { + Ok(0) + } + + #[inline] + fn stream_position(&mut self) -> Result { + Ok(0) + } +} + /// Creates a value that is always at EOF for reads, and ignores all data written. /// /// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] @@ -464,6 +482,49 @@ impl Take { } } +#[stable(feature = "seek_io_take", since = "1.89.0")] +impl Seek for Take { + fn seek(&mut self, pos: SeekFrom) -> Result { + let new_position = match pos { + SeekFrom::Start(v) => Some(v), + SeekFrom::Current(v) => self.position().checked_add_signed(v), + SeekFrom::End(v) => self.len.checked_add_signed(v), + }; + let new_position = match new_position { + Some(v) if v <= self.len => v, + _ => return Err(ErrorKind::InvalidInput.into()), + }; + while new_position != self.position() { + if let Some(offset) = new_position.checked_signed_diff(self.position()) { + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + break; + } + let offset = if new_position > self.position() { i64::MAX } else { i64::MIN }; + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + } + Ok(new_position) + } + + fn stream_len(&mut self) -> Result { + Ok(self.len) + } + + fn stream_position(&mut self) -> Result { + Ok(self.position()) + } + + fn seek_relative(&mut self, offset: i64) -> Result<()> { + if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) { + return Err(ErrorKind::InvalidInput.into()); + } + self.inner.seek_relative(offset)?; + self.limit = self.limit.wrapping_sub(offset as u64); + Ok(()) + } +} + #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] #[must_use] diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 53b60c1ae6703..4b3b67daadbd3 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -7,42 +7,7 @@ pub use core::io::Cursor; use crate::alloc::Allocator; use crate::cmp; use crate::io::prelude::*; -use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; - -#[stable(feature = "rust1", since = "1.0.0")] -impl io::Seek for Cursor -where - T: AsRef<[u8]>, -{ - fn seek(&mut self, style: SeekFrom) -> io::Result { - let (base_pos, offset) = match style { - SeekFrom::Start(n) => { - self.set_position(n); - return Ok(n); - } - SeekFrom::End(n) => (self.get_ref().as_ref().len() as u64, n), - SeekFrom::Current(n) => (self.position(), n), - }; - match base_pos.checked_add_signed(offset) { - Some(n) => { - self.set_position(n); - Ok(n) - } - None => Err(io::const_error!( - ErrorKind::InvalidInput, - "invalid seek to a negative or overflowing position", - )), - } - } - - fn stream_len(&mut self) -> io::Result { - Ok(self.get_ref().as_ref().len() as u64) - } - - fn stream_position(&mut self) -> io::Result { - Ok(self.position()) - } -} +use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; #[stable(feature = "rust1", since = "1.0.0")] impl Read for Cursor diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 1dd1859e9a3d8..08bb34376b075 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -3,7 +3,7 @@ mod tests; use crate::alloc::Allocator; use crate::collections::VecDeque; -use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; +use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Write}; use crate::sync::Arc; use crate::{cmp, fmt, mem, str}; @@ -90,33 +90,6 @@ impl Write for &mut W { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Seek for &mut S { - #[inline] - fn seek(&mut self, pos: SeekFrom) -> io::Result { - (**self).seek(pos) - } - - #[inline] - fn rewind(&mut self) -> io::Result<()> { - (**self).rewind() - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - (**self).stream_len() - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (**self).stream_position() - } - - #[inline] - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - (**self).seek_relative(offset) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for &mut B { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -229,33 +202,6 @@ impl Write for Box { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Seek for Box { - #[inline] - fn seek(&mut self, pos: SeekFrom) -> io::Result { - (**self).seek(pos) - } - - #[inline] - fn rewind(&mut self) -> io::Result<()> { - (**self).rewind() - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - (**self).stream_len() - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (**self).stream_position() - } - - #[inline] - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - (**self).seek_relative(offset) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Box { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -804,34 +750,3 @@ where (&**self).write_fmt(fmt) } } -#[stable(feature = "io_traits_arc", since = "1.73.0")] -impl Seek for Arc -where - for<'a> &'a S: Seek, - S: crate::io::IoHandle, -{ - #[inline] - fn seek(&mut self, pos: SeekFrom) -> io::Result { - (&**self).seek(pos) - } - - #[inline] - fn rewind(&mut self) -> io::Result<()> { - (&**self).rewind() - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - (&**self).stream_len() - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (&**self).stream_position() - } - - #[inline] - fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - (&**self).seek_relative(offset) - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 1923c8c5dcb93..0f2d9238c37ad 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -299,7 +299,6 @@ mod tests; use core::slice::memchr; -pub(crate) use alloc_crate::io::IoHandle; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use alloc_crate::io::RawOsError; #[doc(hidden)] @@ -311,8 +310,9 @@ pub use alloc_crate::io::const_error; pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Chain, Empty, Error, ErrorKind, Repeat, Result, SeekFrom, Sink, Take, empty, repeat, sink, + Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, empty, repeat, sink, }; +pub(crate) use alloc_crate::io::{IoHandle, stream_len_default}; #[stable(feature = "iovec", since = "1.36.0")] pub use alloc_crate::io::{IoSlice, IoSliceMut}; use alloc_crate::io::{OsFunctions, SizeHint}; @@ -1762,197 +1762,6 @@ pub trait Write { } } -/// The `Seek` trait provides a cursor which can be moved within a stream of -/// bytes. -/// -/// The stream typically has a fixed size, allowing seeking relative to either -/// end or the current offset. -/// -/// # Examples -/// -/// [`File`]s implement `Seek`: -/// -/// [`File`]: crate::fs::File -/// -/// ```no_run -/// use std::io; -/// use std::io::prelude::*; -/// use std::fs::File; -/// use std::io::SeekFrom; -/// -/// fn main() -> io::Result<()> { -/// let mut f = File::open("foo.txt")?; -/// -/// // move the cursor 42 bytes from the start of the file -/// f.seek(SeekFrom::Start(42))?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoSeek")] -pub trait Seek { - /// Seek to an offset, in bytes, in a stream. - /// - /// A seek beyond the end of a stream is allowed, but behavior is defined - /// by the implementation. - /// - /// If the seek operation completed successfully, - /// this method returns the new position from the start of the stream. - /// That position can be used later with [`SeekFrom::Start`]. - /// - /// # Errors - /// - /// Seeking can fail, for example because it might involve flushing a buffer. - /// - /// Seeking to a negative offset is considered an error. - #[stable(feature = "rust1", since = "1.0.0")] - fn seek(&mut self, pos: SeekFrom) -> Result; - - /// Rewind to the beginning of a stream. - /// - /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`. - /// - /// # Errors - /// - /// Rewinding can fail, for example because it might involve flushing a buffer. - /// - /// # Example - /// - /// ```no_run - /// use std::io::{Read, Seek, Write}; - /// use std::fs::OpenOptions; - /// - /// let mut f = OpenOptions::new() - /// .write(true) - /// .read(true) - /// .create(true) - /// .open("foo.txt")?; - /// - /// let hello = "Hello!\n"; - /// write!(f, "{hello}")?; - /// f.rewind()?; - /// - /// let mut buf = String::new(); - /// f.read_to_string(&mut buf)?; - /// assert_eq!(&buf, hello); - /// # std::io::Result::Ok(()) - /// ``` - #[stable(feature = "seek_rewind", since = "1.55.0")] - fn rewind(&mut self) -> Result<()> { - self.seek(SeekFrom::Start(0))?; - Ok(()) - } - - /// Returns the length of this stream (in bytes). - /// - /// The default implementation uses up to three seek operations. If this - /// method returns successfully, the seek position is unchanged (i.e. the - /// position before calling this method is the same as afterwards). - /// However, if this method returns an error, the seek position is - /// unspecified. - /// - /// If you need to obtain the length of *many* streams and you don't care - /// about the seek position afterwards, you can reduce the number of seek - /// operations by simply calling `seek(SeekFrom::End(0))` and using its - /// return value (it is also the stream length). - /// - /// Note that length of a stream can change over time (for example, when - /// data is appended to a file). So calling this method multiple times does - /// not necessarily return the same length each time. - /// - /// # Example - /// - /// ```no_run - /// #![feature(seek_stream_len)] - /// use std::{ - /// io::{self, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// - /// let len = f.stream_len()?; - /// println!("The file is currently {len} bytes long"); - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "seek_stream_len", issue = "59359")] - fn stream_len(&mut self) -> Result { - stream_len_default(self) - } - - /// Returns the current seek position from the start of the stream. - /// - /// This is equivalent to `self.seek(SeekFrom::Current(0))`. - /// - /// # Example - /// - /// ```no_run - /// use std::{ - /// io::{self, BufRead, BufReader, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = BufReader::new(File::open("foo.txt")?); - /// - /// let before = f.stream_position()?; - /// f.read_line(&mut String::new())?; - /// let after = f.stream_position()?; - /// - /// println!("The first line was {} bytes long", after - before); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "seek_convenience", since = "1.51.0")] - fn stream_position(&mut self) -> Result { - self.seek(SeekFrom::Current(0)) - } - - /// Seeks relative to the current position. - /// - /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but - /// doesn't return the new position which can allow some implementations - /// such as [`BufReader`] to perform more efficient seeks. - /// - /// # Example - /// - /// ```no_run - /// use std::{ - /// io::{self, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = File::open("foo.txt")?; - /// f.seek_relative(10)?; - /// assert_eq!(f.stream_position()?, 10); - /// Ok(()) - /// } - /// ``` - /// - /// [`BufReader`]: crate::io::BufReader - #[stable(feature = "seek_seek_relative", since = "1.80.0")] - fn seek_relative(&mut self, offset: i64) -> Result<()> { - self.seek(SeekFrom::Current(offset))?; - Ok(()) - } -} - -pub(crate) fn stream_len_default(self_: &mut T) -> Result { - let old_pos = self_.stream_position()?; - let len = self_.seek(SeekFrom::End(0))?; - - // Avoid seeking a third time when we were already at the end of the - // stream. The branch is usually way cheaper than a seek operation. - if old_pos != len { - self_.seek(SeekFrom::Start(old_pos))?; - } - - Ok(len) -} - fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { let mut read = 0; loop { @@ -2604,49 +2413,6 @@ impl BufRead for Take { } } -#[stable(feature = "seek_io_take", since = "1.89.0")] -impl Seek for Take { - fn seek(&mut self, pos: SeekFrom) -> Result { - let new_position = match pos { - SeekFrom::Start(v) => Some(v), - SeekFrom::Current(v) => self.position().checked_add_signed(v), - SeekFrom::End(v) => self.len.checked_add_signed(v), - }; - let new_position = match new_position { - Some(v) if v <= self.len => v, - _ => return Err(ErrorKind::InvalidInput.into()), - }; - while new_position != self.position() { - if let Some(offset) = new_position.checked_signed_diff(self.position()) { - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - break; - } - let offset = if new_position > self.position() { i64::MAX } else { i64::MIN }; - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - } - Ok(new_position) - } - - fn stream_len(&mut self) -> Result { - Ok(self.len) - } - - fn stream_position(&mut self) -> Result { - Ok(self.position()) - } - - fn seek_relative(&mut self, offset: i64) -> Result<()> { - if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) { - return Err(ErrorKind::InvalidInput.into()); - } - self.inner.seek_relative(offset)?; - self.limit = self.limit.wrapping_sub(offset as u64); - Ok(()) - } -} - /// An iterator over `u8` values of a reader. /// /// This struct is generally created by calling [`bytes`] on a reader. diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index fbb69a11c6b92..ce2137f9567c7 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -5,8 +5,7 @@ mod tests; use crate::fmt; use crate::io::{ - self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Seek, SeekFrom, Sink, - Write, + self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Sink, Write, }; #[stable(feature = "rust1", since = "1.0.0")] @@ -84,24 +83,6 @@ impl BufRead for Empty { } } -#[stable(feature = "empty_seek", since = "1.51.0")] -impl Seek for Empty { - #[inline] - fn seek(&mut self, _pos: SeekFrom) -> io::Result { - Ok(0) - } - - #[inline] - fn stream_len(&mut self) -> io::Result { - Ok(0) - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - Ok(0) - } -} - #[stable(feature = "empty_write", since = "1.73.0")] impl Write for Empty { #[inline] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index afa30c8c00f27..bc1ba00636407 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -374,6 +374,7 @@ #![feature(random)] #![feature(raw_os_error_ty)] #![feature(seek_io_take_position)] +#![feature(seek_stream_len)] #![feature(share_trait)] #![feature(slice_internals)] #![feature(slice_ptr_get)] From a3f9abd094d89930ee7154ca544f1a89608b3227 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 22 May 2026 10:28:58 +1000 Subject: [PATCH 15/19] Fix documentation links to `Seek` --- library/core/src/io/cursor.rs | 2 +- library/core/src/io/error.rs | 2 +- library/core/src/io/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index f155f199d7be9..c104b426f48df 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -23,7 +23,7 @@ use crate::io::{self, ErrorKind, SeekFrom}; /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html /// [`Write`]: ../../std/io/trait.Write.html -/// [`Seek`]: ../../std/io/trait.Seek.html +/// [`Seek`]: crate::io::Seek /// [Vec]: ../../alloc/vec/struct.Vec.html /// /// ```no_run diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 2d15307daf153..4214a5f87e3ef 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -80,7 +80,7 @@ pub type Result = result::Result; // FIXME(#74481): Hard-links required to link from `core` to `std` /// [Read]: ../../std/io/trait.Read.html /// [Write]: ../../std/io/trait.Write.html -/// [Seek]: ../../std/io/trait.Seek.html +/// [Seek]: crate::io::Seek #[stable(feature = "rust1", since = "1.0.0")] #[rustc_has_incoherent_inherent_impls] pub struct Error { diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index d85ee311e18a9..1c27e42229bb7 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -49,7 +49,7 @@ pub use self::{ /// [file]: ../../std/fs/struct.File.html /// [arc]: ../../alloc/sync/struct.Arc.html /// [`Write`]: ../../std/io/trait.Write.html -/// [`Seek`]: ../../std/io/trait.Seek.html +/// [`Seek`]: crate::io::Seek #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub trait IoHandle {} From 6a69276f3fcdfc4cfeffe34aaf0549e1ac292233 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Fri, 3 Jul 2026 10:50:33 +1000 Subject: [PATCH 16/19] Add documentation to `stream_len_default` Co-Authored-By: Clar Fon <15850505+clarfonthey@users.noreply.github.com> --- library/core/src/io/seek.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/core/src/io/seek.rs b/library/core/src/io/seek.rs index 94865a1d11ecc..4c242c761dfe6 100644 --- a/library/core/src/io/seek.rs +++ b/library/core/src/io/seek.rs @@ -174,6 +174,9 @@ pub trait Seek { } } +/// The default implementation of [`Seek::stream_len`]. +/// This may be desirable in `libstd` where the default implementation is desirable, +/// but additional work needs to be done before or after. #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub fn stream_len_default(self_: &mut T) -> Result { From cae0245705e2e54f07c85b261ffe9fbb2ce2d292 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 12 May 2026 22:36:50 +1000 Subject: [PATCH 17/19] Add `std::io::cursor::WriteThroughCursor` Used to work around `Cursor` not being a fundamental type when implementing `Write` for `Cursor>` and other `alloc` types once `Cursor` and `Write` are both located in `core`. --- library/std/src/io/cursor.rs | 114 +++++++++++++++++++++++++---------- 1 file changed, 81 insertions(+), 33 deletions(-) diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 53b60c1ae6703..443e84db3ef38 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -137,6 +137,54 @@ where } } +/// Trait used to allow indirect implementation of `Write` for `Cursor`. +/// Since [`Cursor`] is not a foundational type, it is not possible to implement +/// `Write` for `Cursor` if `Write` is defined in `libcore` and `T` is in a +/// downstream crate (e.g., `liballoc` or `libstd`). +/// +/// Methods are identical in purpose and meaning to their `Write` namesakes. +trait WriteThroughCursor: Sized { + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result; + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result; + fn is_write_vectored(this: &Cursor) -> bool; + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()>; + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()>; + fn flush(this: &mut Cursor) -> io::Result<()>; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Cursor { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + WriteThroughCursor::write(self, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + WriteThroughCursor::write_vectored(self, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + WriteThroughCursor::is_write_vectored(self) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + WriteThroughCursor::write_all(self, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + WriteThroughCursor::write_all_vectored(self, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + WriteThroughCursor::flush(self) + } +} + // Non-resizing write implementation #[inline] fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { @@ -348,117 +396,117 @@ impl Write for Cursor<&mut [u8]> { } #[stable(feature = "cursor_mut_vec", since = "1.25.0")] -impl Write for Cursor<&mut Vec> +impl WriteThroughCursor for &mut Vec where A: Allocator, { - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf) } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs) } #[inline] - fn is_write_vectored(&self) -> bool { + fn is_write_vectored(_this: &Cursor) -> bool { true } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf)?; Ok(()) } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs)?; Ok(()) } #[inline] - fn flush(&mut self) -> io::Result<()> { + fn flush(_this: &mut Cursor) -> io::Result<()> { Ok(()) } } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor> +impl WriteThroughCursor for Vec where A: Allocator, { - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf) } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs) } #[inline] - fn is_write_vectored(&self) -> bool { + fn is_write_vectored(_this: &Cursor) -> bool { true } - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all(pos, inner, buf)?; Ok(()) } - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); vec_write_all_vectored(pos, inner, bufs)?; Ok(()) } #[inline] - fn flush(&mut self) -> io::Result<()> { + fn flush(_this: &mut Cursor) -> io::Result<()> { Ok(()) } } #[stable(feature = "cursor_box_slice", since = "1.5.0")] -impl Write for Cursor> +impl WriteThroughCursor for Box<[u8], A> where A: Allocator, { #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); slice_write(pos, inner, buf) } #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); slice_write_vectored(pos, inner, bufs) } #[inline] - fn is_write_vectored(&self) -> bool { + fn is_write_vectored(_this: &Cursor) -> bool { true } #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); slice_write_all(pos, inner, buf) } #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); slice_write_all_vectored(pos, inner, bufs) } #[inline] - fn flush(&mut self) -> io::Result<()> { + fn flush(_this: &mut Cursor) -> io::Result<()> { Ok(()) } } From e7408fbec68b145b985a1953ca4fa3321874fb39 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Mon, 6 Jul 2026 10:10:28 +0000 Subject: [PATCH 18/19] Fix rustdoc ICE on deprecated note in inlined re-export chain * add regression test for deprecated note on inlined reexport chain * resolve deprecated-note links from the owning reexport * add a comment to regression test --- .../passes/collect_intra_doc_links.rs | 19 ++++++++++++++----- ...ecated-note-from-inlined-reexport-chain.rs | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 tests/rustdoc-ui/intra-doc/deprecated-note-from-inlined-reexport-chain.rs diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index a12e95d210967..abf017174b547 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -31,7 +31,7 @@ use smallvec::{SmallVec, smallvec}; use tracing::{debug, info, instrument, trace}; use crate::clean::utils::find_nearest_parent_module; -use crate::clean::{self, Crate, Item, ItemId, ItemLink, PrimitiveType}; +use crate::clean::{self, Crate, Item, ItemId, ItemLink, PrimitiveType, reexport_chain}; use crate::core::DocContext; use crate::html::markdown::{MarkdownLink, MarkdownLinkRange, markdown_links}; use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS}; @@ -1148,10 +1148,19 @@ impl LinkCollector<'_, '_> { // `use` statement, we need to use the `def_id` of the `use` statement, not the // inlined item. // - let item_id = if let Some(inline_stmt_id) = item.inline_stmt_id - && find_attr!(tcx, inline_stmt_id, Deprecated { span, ..} if span == depr_span) - { - inline_stmt_id.to_def_id() + let item_id = if let Some(inline_stmt_id) = item.inline_stmt_id { + let target_def_id = item.item_id.expect_def_id(); + reexport_chain(tcx, inline_stmt_id, target_def_id) + .iter() + .flat_map(|reexport| reexport.id()) + .find(|&reexport_def_id| { + find_attr!( + tcx, + reexport_def_id, + Deprecated { span, .. } if span == depr_span + ) + }) + .unwrap_or(target_def_id) } else { item.item_id.expect_def_id() }; diff --git a/tests/rustdoc-ui/intra-doc/deprecated-note-from-inlined-reexport-chain.rs b/tests/rustdoc-ui/intra-doc/deprecated-note-from-inlined-reexport-chain.rs new file mode 100644 index 0000000000000..d2e527fcb29f3 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/deprecated-note-from-inlined-reexport-chain.rs @@ -0,0 +1,18 @@ +//@ check-pass + +// Regression test for https://github.com/rust-lang/rust/issues/158745. +// This checks that rustdoc resolves intra-doc links in deprecation notes using +// the re-export that carries the attribute, even when that re-export is later +// inlined through another re-export. + +#![deny(rustdoc::broken_intra_doc_links)] + +mod bar { + pub struct A; +} + +#[deprecated(note = "[std::io::ErrorKind::NotFound]")] +pub use bar::A; + +#[doc(inline)] +pub use A as X; From 9c8554034a67c0193bcfc029972a5ca48ac84ebb Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Mon, 6 Jul 2026 10:34:32 +0000 Subject: [PATCH 19/19] Fix typo for link on nto-qnx.md --- src/doc/rustc/src/platform-support/nto-qnx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md index b3ab1c7c4485b..c9258f9558de8 100644 --- a/src/doc/rustc/src/platform-support/nto-qnx.md +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -2,7 +2,7 @@ **Tier: 3** -Support for the [QNX®][qnx.com] [QNX Software Development Platform (SDP)], version 7.0, 7.1 and 8.0. +Support for the [QNX®](https://qnx.com) [QNX Software Development Platform (SDP)], version 7.0, 7.1 and 8.0. [QNX Software Development Platform (SDP)]: https://qnx.software/en/software/products-and-solutions/qnx-software-development-platform